如何使用 Pandas 處理大型 CSV 檔案?
在這篇文章中,我們將介紹使用 Pandas 處理大型 CSV 檔案的幾種方法。CSV 檔案是常見的資料容器,如果您有一個大型 CSV 檔案需要使用 Pandas 高效地處理,您有幾種選擇。
Pandas 是一個記憶體工具
您需要能夠將資料放入記憶體中才能使用 Pandas。如果您一次可以處理一部分資料,則可以將其讀入塊並處理每個塊。或者,如果您知道有足夠的記憶體載入檔案,則有一些提示可以幫助減少檔案大小。
請注意,一般來說,您應該擁有三到十倍於要操作的 DataFrame 大小的記憶體。額外的記憶體應該為您提供足夠的空間來執行許多常見操作。
1.使用 Python 檢查系統的記憶體
讓我們首先檢查一下系統的記憶體。psutil 可以在 Windows、MAC 和 Linux 上執行。psutil 可以使用 pip install 從 Python 的包管理器下載。
如果在安裝過程中遇到 psutil 的編譯錯誤,請嘗試以下步驟:sudo yum install python3-devel sudo pip install psutil
現在使用以下命令檢查版本:
pip freeze | grep psutil
輸入
import psutil memory = psutil.virtual_memory() print(f" {'*' * 3} Memory used percentage - {memory.percent}
{'*' * 4} Free Memory available - { round(memory.free / (1024.0 ** 3))} GB")
*** 記憶體使用率 - 64.4%
**** 可用記憶體 - 6 GB
2.確定 CSV 檔案的記憶體使用情況
現在我們將估計整個檔案將佔用多少記憶體。我使用了來自 kaggle.com 的 tmdb_5000_movies 資料集。
輸入
import pandas as pd data = pd.read_csv("tmdb_5000_movies.csv") # Lets check the memory usage of the file print(f" ** Memory usage of the file - {sum(data.memory_usage()) * 0.000001} MB for {len(data.index)} Rows")
** 檔案的記憶體使用情況 - 52833 行佔用 8.453408 MB
data.memory_usage() 方法顯示了 DataFrame 的記憶體使用情況,而 len(data.index) 顯示了 DataFrame 的總行數。
我們可以看到 52833 行使用了大約 8+ MB 的記憶體。如果我們有一百萬行,這將佔用大約 151+ GB 的記憶體。現在,將所有內容都放入記憶體並使其掛起是一個糟糕的主意,不要這樣做。
現在,讓我們看看這些資料型別的限制。
示例
import numpy as np # Limits of Integer Data Type print(f" ** Output limits of Numpy Integer Data Types ") print(f" ** limits of Numpy Integer - {np.iinfo(np.int8)}") print(f" ** limits of Numpy Integer - {np.iinfo(np.int16)}") print(f" ** limits of Numpy Integer - {np.iinfo(np.int64)}") # Limits of Float Data Type print(f" ** Output limits of Numpy Float Data Types ") print(f" ** limits of Numpy Float - {np.finfo(np.float16)}") print(f" ** limits of Numpy Float - {np.finfo(np.float64)}")
輸出
** Output limits of Numpy Integer Data Types ** limits of Numpy Integer - Machine parameters for int8 --------------------------------------------------------------- min = -128 max = 127 --------------------------------------------------------------- ** limits of Numpy Integer - Machine parameters for int16 --------------------------------------------------------------- min = -32768 max = 32767 --------------------------------------------------------------- ** limits of Numpy Integer - Machine parameters for int64 --------------------------------------------------------------- min = -9223372036854775808 max = 9223372036854775807 --------------------------------------------------------------- ** Output limits of Numpy Float Data Types ** limits of Numpy Float - Machine parameters for float16 --------------------------------------------------------------- precision = 3 resolution = 1.00040e-03 machep = -10 eps = 9.76562e-04 negep = -11 epsneg = 4.88281e-04 minexp = -14 tiny = 6.10352e-05 maxexp = 16 max = 6.55040e+04 nexp = 5 min = -max --------------------------------------------------------------- ** limits of Numpy Float - Machine parameters for float64 --------------------------------------------------------------- precision = 15 resolution = 1.0000000000000001e-15 machep = -52 eps = 2.2204460492503131e-16 negep = -53 epsneg = 1.1102230246251565e-16 minexp = -1022 tiny = 2.2250738585072014e-308 maxexp = 1024 max = 1.7976931348623157e+308 nexp = 11 min = -max ---------------------------------------------------------------
輸入
**3.Converting Numeric Data Types** Let’s run the .info() method to validate our data types in depth. File "<ipython−input−17−aad3ab034212>", line 1 **3.Converting Numeric Data Types** ^ SyntaxError: invalid syntax
輸入
# Lets print the DataFrame information print(f" {data.info()}")
現在,讓我們總結一下資料型別和列的計數,看看 Pandas 如何對資料進行分類。
輸入
# lets summarize the data types and count of columns print(f" ** Summarize the data types and count of columns
{data.dtypes.value_counts()}")
在本節中,我們將重點關注 int64 和 float64 資料型別,研究資料/精度並進行轉換。我將使用 dtype 引數告訴 Pandas 使用更小的數字型別而不是預設的 64 位,現在您理解為什麼上面理解資料型別的第一步很重要了。
輸入
# Define a dictionary converting the numeric data types data_types_conversion_numeric = { "popularity": np.float16, "runtime": np.float16, "vote_average": np.float16, "id": np.int16, "revenue": np.int16, "vote_count": np.int16 } data_02 = pd.read_csv("tmdb_5000_movies.csv", dtype=data_types_conversion_numeric) print(f" ** Memory usage of the file - {sum(data_02.memory_usage()) * 0.000001} MB for {len(data_02.index)} Rows")
如您所見,將資料型別更改為使用更小的數字型別為我們節省了 23% 的記憶體,如果您儲存的是少量資料,還可以使用 int8,這可能會導致進一步的節省。
4.轉換物件資料型別
物件資料型別將值視為字串。Pandas 中的字串值佔用大量記憶體,因為每個值都儲存為 Python 字串,如果列結果是非數字的,Pandas 會將其轉換為物件列。
將物件資料型別轉換為分類資料將使用更少的記憶體,因為 Pandas 只儲存一次字串,而不是為每一行建立新的字串。
首先,檢查物件列的 .value_counts 方法。如果它們是低基數,則可以將其轉換為分類列以節省更多記憶體。
輸入
print(data_02.original_language.value_counts())
基數不是很高,我將開始將物件資料型別轉換為分類資料。
輸入
data_types_conversion_numeric = { "popularity": np.float16, "runtime": np.float16, "vote_average": np.float16, "id": np.int16, "revenue": np.int16, "vote_count": np.int16, "genres": "category", "homepage": "category", "keywords": "category", "original_language": "category", "original_title": "category", "overview": "category", "production_companies": "category", "production_countries": "category", "release_date": "category", "spoken_languages": "category", "status": "category", "tagline": "category", "title": "category" } data_02 = pd.read_csv("tmdb_5000_movies.csv", dtype=data_types_conversion_numeric) print(f" ** Memory usage of the file - {sum(data_02.memory_usage()) * 0.000001} MB for {len(data_02.index)} Rows")
我們現在處於原始大小的 46%。大約節省了 54% 的記憶體。
5.識別和刪除 Pandas 中的重複項。
您正在處理的原始檔中可能存在重複項,如果不需要,刪除它們將節省更多記憶體。在我的例子中,為了使檔案大小變大,我必須重複記錄
讓我們在刪除重複項之前驗證原始檔中重複項的數量。
輸入
print(f" ** File has {len(data_02) - len(data_02.drop_duplicates())} duplicate rows off the total {len(data_02)} ")
len(your dataframe) 輸出 DataFrame 中的總行數,而 len(dataframe.drop_duplicates()) 將輸出 DataFrame 中的唯一值。所以我的檔案如上所述有相當數量的重複項,刪除它們將節省大量記憶體。
輸入
data_03 = data_02.drop_duplicates() print(f" ** Memory usage of the file after dropping duplicates - {sum(data_03.memory_usage()) * 0.000001} MB for {len(data_03.index)} Rows")
好吧,刪除重複項後節省了一些記憶體。如果您有重複項並希望刪除它們,請使用此步驟。
6.如何在 Pandas 中刪除不需要的列
If there are columns that you know can be ignored, then specify usecols parameter to include the columns you want to load. Here, we will ignore the columns “homepage”, “keywords”, “original_title” and “tagline”.
輸入
# prepare a list of columns that you want to load unwanted_columns = ["homepage", "keywords","original_title", "tagline"] data_columns = [columns for columns in list(pd.read_csv("tmdb_5000_movies.csv").columns) if columns not in unwanted_columns] # Define a dictionary converting the numeric data types data_types_conversion = { "popularity": np.float16, "runtime": np.float16, "vote_average": np.float16, "id": np.int16, "revenue": np.int16, "vote_count": np.int16, "genres": "category", "original_language": "category", "overview": "category", "production_companies": "category", "production_countries": "category", "release_date": "category", "spoken_languages": "category", "status": "category", "title": "category" } data_02 = pd.read_csv("tmdb_5000_movies.csv", dtype=data_types_conversion, usecols=data_columns) print(f" ** Memory usage of the file after dropping cols - {sum(data_02.memory_usage()) * 0.000001} MB for {len(data_02.index)} Rows")
我們現在處於原始大小的 32%。大約節省了 68% 的記憶體。
7.如何使用 Pandas 處理資料塊。
如果您一次可以處理資料塊並且不需要所有資料都位於記憶體中,則可以使用 chunk size 引數。我個人建議將其作為最後的選擇。
# read the csv file data = pd.read_csv("tmdb_5000_movies.csv") # prepare a list of columns that you want to load unwanted_columns = ["homepage", "keywords","original_title", "tagline"] data_columns = [columns for columns in list(pd.read_csv("tmdb_5000_movies.csv").columns) if columns not in unwanted_columns] # Define a dictionary converting the numeric data types data_types_conversion = { "popularity": np.float16, "runtime": np.float16, "vote_average": np.float16, "id": np.int16, "revenue": np.int16, "vote_count": np.int16, "genres": "category", "original_language": "category", "overview": "category", "production_companies": "category", "production_countries": "category", "release_date": "category", "spoken_languages": "category", "status": "category", "title": "category" } data_02 = pd.read_csv("tmdb_5000_movies.csv", dtype=data_types_conversion, usecols=data_columns,chunksize=10000) # Process the data frame in chunks for chunk in data_02: print(f" ** Memory usage of the file after dropping cols − {sum(chunk.memory_usage()) * 0.000001} MB for {len(chunk.index)} Rows") print(f"Do some more processing here... ")
我們現在處於原始大小的 14%。大約節省了 86% 的記憶體。
注意 - 使用 .describe() 方法持續比較每個步驟的結果。