如何使用 Pandas 处理大型 CSV 文件?

pandasserver side programmingprogramming更新于 2025/4/11 20:37:17

在本文中,我们将介绍使用 Pandas 处理大型 CSV 文件的选项。CSV 文件是常见的数据容器,如果您有一个大型 CSV 文件,想要使用 Pandas 有效地处理,您有几个选项。

Pandas 是一个内存工具

您需要能够将数据放入内存中才能使用 Pandas。如果您可以一次处理部分数据,则可以将其读入块并处理每个块。或者,如果您知道应该有足够的内存来加载文件,那么有一些提示可以帮助减少文件大小。

请注意,一般来说,您应该拥有您想要操作的 DataFrame 大小的三到十倍的内存量。额外的内存应该会给你足够的额外空间来执行许多常见操作。

1.使用 Python 检查系统内存

让我们从检查系统内存开始。psutil 可以在 Windows、MAC 和 Linux 上运行。可以使用 pip install 从 Python 的包管理器下载 psutil。

如果在安装时遇到 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")

# 检查文件的内存使用情况
print(f" ** 文件的内存使用情况 - {sum(data.memory_usage()) * 0.000001} MB for {len(data.index)} Rows")

** 文件的内存使用情况 - 52833 行,8.453408 MB

data.memory_usage() 方法显示数据框的内存使用情况,而 len(data.index) 显示数据框的总行数。

我们可以看到,52833 行使用了大约 8+ MB 的内存。如果有十亿行,则大约需要 151+ GB 的内存。现在,把所有东西都塞进内存并导致程序挂起可不是个好主意,千万不要这么做。

现在,让我们来看看这些数据类型的限制。

示例

import numpy as np

# 整数数据类型的限制
print(f" ** Numpy 整数数据类型的输出限制 ")
print(f" ** Numpy 整数的限制 - {np.iinfo(np.int8)}")
print(f" ** Numpy 整数的限制 - {np.iinfo(np.int16)}")
print(f" ** Numpy 整数的限制 - {np.iinfo(np.int64)}")

# 浮点数据类型的限制
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)}")

输出

** Numpy 整数数据类型的输出限制
** Numpy 整数的限制 - int8 的机器参数
---------------------------------------------------------------
min = -128
max = 127
---------------------------------------------------------------
** Numpy 整数的限制 - int16 的机器参数
---------------------------------------------------------------
min = -32768
max = 32767
------------------------------------------------------------------------
** Numpy 整数的限制 - int64 的机器参数
------------------------------------------------------------------------
min = -9223372036854775808
max = 9223372036854775807
------------------------------------------------------------------------
** Numpy 浮点数据类型的输出限制
** Numpy 浮点的限制 - int16 的机器参数float16
---------------------------------------------------------------
precision = 3resolution = 1.00040e-03
machep = -10eps = 9.76562e-04
negep = -11epsneg = 4.88281e-04
minexp = -14tiny = 6.10352e-05
maxexp = 16max = 6.55040e+04
nexp = 5min = -max
--------------------------------------------------------------------------------
**NumpyFloat 的限制 - 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

输入

# 让我们打印 DataFrame 信息
print(f" {data.info()}")

现在,让我们总结一下数据类型和列数,看看 pandas 如何对数据进行分类。

输入

# 让我们总结一下数据类型和列数
print(f" ** Summarize the data types and count of columns 
{data.dtypes.value_counts()}")

在本节中,我们将重点介绍 int64 和 float64 数据类型,研究其数据/精度,并进行转换。我将使用 dtype 参数告诉 Pandas 使用较小的数值类型,而不是默认的 64 位,现在您明白了为什么首先了解数据类型这一步很重要。

输入

# 定义一个用于转换数值数据类型的字典
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" ** 文件的内存使用量 - {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" ** 文件包含 {len(data_02) - len(data_02.drop_duplicates())} 个重复行,总数为 {len(data_02)} ")

len(your dataframe) 输出数据框中的总行数,而 len(dataframe.drop_duplicates()) 输出数据框中的唯一值。因此,正如上面提到的,我的文件包含大量重复项,删除它们可以节省大量内存。

输入

data_03 = data_02.drop_duplicates()
print(f" ** 删除重复项后文件的内存使用量 - {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”.

输入

# 准备要加载的列列表
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]

# 定义一个字典,用于将数字数据类型转换为其他数据类型
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 处理数据块。

如果您可以一次处理数据块,并且不需要将所有数据都存储在内存中,则可以使用块大小参数。我个人建议这是你的最后选择。

# 读取 csv 文件
data = pd.read_csv("tmdb_5000_movies.csv")

# 准备要加载的列的列表
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]

# 定义一个字典,用于将数值数据类型转换为其他数据类型
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)

# 分块处理数据帧
   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() 方法继续比较每个步骤的结果。


相关文章