如何使用 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() 方法持续比较每个步骤的结果。