前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >加载大型CSV文件到Pandas DataFrame的技巧和诀窍

加载大型CSV文件到Pandas DataFrame的技巧和诀窍

作者头像
磐创AI
发布2024-04-03 19:13:38
970
发布2024-04-03 19:13:38
举报

现实世界中的大多数数据集通常都非常庞大,以千兆字节为单位,并包含数百万行。在本文中,我将讨论处理大型CSV数据集时可以采用的一些技巧。

处理大型CSV文件时,有两个主要关注点:

  1. 加载大型CSV文件时所使用的内存量。
  2. 加载大型CSV文件所花费的时间。

理想情况下,你希望最小化DataFrame的内存占用,同时减少加载所需的时间。在本文中,我将通过使用一个示例数据集来向你演示。

我们的数据集

在本文中,将使用从https://www.kaggle.com/datasets/4e614ec846ab778f6a2ff166232d5a65f5e6786b4f5781690588bd2cccd71cb6?resource=download 获取的日本贸易统计数据。

该数据集包含了从1988年到2020年的贸易数据。它包含超过1亿行,CSV文件占用了4.5 GB的空间。因此,这个数据集是用来说明本文概念的理想数据集。

将CSV文件加载到Pandas DataFrame中

首先,让我们从加载包含超过1亿行的整个CSV文件开始。我想看看加载DataFrame需要多长时间,以及它的内存占用情况:

代码语言:javascript
复制
import time
import pandas as pd

start = time.time()

df = pd.read_csv("custom_1988_2020.csv")

print(time.time() - start, ' seconds')
display(df)
display(df.info())

输出如下:

加载整个CSV文件需要大约30秒,其总内存占用令人震惊,达到了6.8 GB!(我使用的是内存为32GB的Mac Studio作为参考。)

检查列

让我们检查数据框中的列:

代码语言:javascript
复制
df.columns

现在,你应该意识到这个CSV文件没有标题,因此Pandas将假定CSV文件的第一行包含标题:

代码语言:javascript
复制
Index(['198801', '1', '103', '100', '000000190', '0', '35843', '34353'], dtype='object')
加载时使用标题

由于CSV文件没有标题,你至少可以使用header参数告诉Pandas该CSV文件没有标题:

代码语言:javascript
复制
# loading with no headers specified
df = pd.read_csv("custom_1988_2020.csv", header=None)
display(df)

Pandas现在将自动以0、1等开头的列名命名列。

从https://www.kaggle.com/datasets/4e614ec846ab778f6a2ff166232d5a65f5e6786b4f5781690588bd2cccd71cb6?resource=download上的数据集描述中,我们看看各列:

  • ym(年份+月份)
  • exp_imp(出口:1,进口:2)
  • hs9(HS编码)
  • 海关
  • 国家
  • Q1
  • Q2(数量)
  • Value(以千日元为单位)

让我们使用names参数为这些列命名:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'])
display(df)

现在DataFrame的列名如下:“YearMonth”,“ExportImport”,“HSCode”,“Customs”,“Country”,“Q1”,“Q2_Quantity”,“Value”。

加载特定列

由于CSV文件非常庞大,你可能会问自己的下一个问题是,你真的需要所有列吗?要加载特定列,你可以使用usecols参数指定要加载的列:

代码语言:javascript
复制
start = time.time()

df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 usecols = ["YearMonth", "Value"])

print(time.time() - start, ' seconds')
display(df)
display(df.info())

如上面的输出所示,内存占用减少到了1.7GB,加载时间也减少到了17秒。

usecols参数还支持列位置索引。上面的代码也可以用列号0和7来重写:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 usecols = [0, 7])

print(time.time() - start, ' seconds')
display(df)

请注意,你不能使用-1来表示最后一列,像这样:

代码语言:javascript
复制
usecols = [0, -1])   # -1 is not supported

usecols参数还支持lambda函数。例如,如果你想检索除“Country”列之外的所有列,你可以使用以下lambda表达式:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 usecols = lambda column: column not in ['Country'])
display(df)

现在,“Country”列将被排除在结果之外。

在usecols参数中使用lambda函数可以让你做一些有趣的事情,比如加载列名包含“Q”的列,例如:

代码语言:javascript
复制
usecols = lambda column: "Q" in column

或者加载列名长度超过七个字符的列:

代码语言:javascript
复制
usecols = lambda column: len(column) > 7
加载前n行

在许多情况下,你不需要整个CSV文件中的所有行。也许前100行就足够了。为此,你可以使用nrows参数指定要加载的前n行:

代码语言:javascript
复制
start = time.time()

df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 nrows=100)

print(time.time() - start, ' seconds')
display(df[:15])
display(df.info())

从上面的结果中,你可以看到现在只需要0.1秒来加载前100行,生成的DataFrame只占用6.4 KB的空间。

跳过行

有时你可能想要跳过CSV文件中的某些行。要实现这一点,可以使用skiprows参数:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=2,
                 nrows=100
)
display(df[:15])

上面的结果显示跳过了CSV文件的前两行:

你可以跳过制定行

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=[0,2,4],
                 nrows=100
)
display(df[:15])

上面的结果显示跳过了第0、2和4行:

你也可以使用一个range对象来指定要跳过的行范围:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=range(5,10),
                 nrows=100
)
display(df[:15])

上面的结果显示跳过了第5到9行。skiprows参数的值也可以用lambda函数编写,像这样:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=lambda x: 5 <= x < 10,
                 nrows=100
)

使用lambda函数,你可以跳过所有偶数行:

代码语言:javascript
复制
df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=lambda x: x % 2 == 0,
                 nrows=100
)

print(time.time() - start, ' seconds')
display(df[:15])

上面的结果显示跳过了所有偶数行:

加载特定行

到目前为止,你已经学会了如何加载前n行,以及如何跳过CSV文件中的特定行。那么如何加载CSV文件中的特定行呢?虽然没有允许你这样做的参数,但你可以利用skiprows参数来实现你想要的效果。

使用skiprows参数中的lambda函数,你可以指定不跳过的行(实际上就是你想要加载的行):

代码语言:javascript
复制
start = time.time()

df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=lambda x: x not in [1,3],
                 nrows=100
)

print(time.time() - start, ' seconds')
display(df[:15])
display(df.info())

上面的结果显示保留了序号为1和3的行:

这种方法的缺点是必须扫描整个CSV文件,因此加载仅两行需要20秒。

加载最后的n行数据

要讨论的最后一个挑战是如何从CSV文件中加载最后的n行数据。加载前n行数据很容易,但加载最后的n行并不那么直接。但是你可以利用到目前为止学到的知识来解决这个问题。

首先,统计CSV文件中有多少行数据:

代码语言:javascript
复制
# read the last n rows
start = time.time()

row_count = sum(1 for l in open('custom_1988_2020.csv')) 

print(time.time() - start, ' seconds')
row_count

由于CSV文件中有超过1亿行数据,统计行数大约需要10秒。还要记住,对于这个CSV文件,没有标题行。所以113607322是实际的记录行数。

然后,要加载最后的20行数据,可以使用skiprows参数,并传递一个lambda函数来跳过除了最后的20行之外的所有行:

代码语言:javascript
复制
# read the last n rows
start = time.time()

df = pd.read_csv("custom_1988_2020.csv", 
                 header=None, 
                 names=['YearMonth', 'ExportImport', 'HSCode', 'Customs', 
                        'Country', 'Q1', 'Q2_Quantity', 'Value'],                 
                 skiprows=lambda x: 0 <= x < row_count - 20,
                 nrows=100)

print(time.time() - start, ' seconds')
display(df)
display(df.info())

结果显示了最后的20行数据加载到了Pandas DataFrame中。

与前面的部分一样,缺点是在加载过程中必须扫描整个CSV文件(因此加载DataFrame需要22秒)。

总结

在本文中,介绍了许多从CSV文件加载Pandas DataFrame的技巧。通常情况下,没有必要将整个CSV文件加载到DataFrame中。通过仅加载所需的数据,你不仅可以节省加载所需数据的时间,还可以节省内存,因为DataFrame需要的内存更少。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 我们的数据集
  • 将CSV文件加载到Pandas DataFrame中
  • 检查列
  • 加载时使用标题
  • 加载特定列
  • 加载前n行
  • 跳过行
  • 加载特定行
  • 加载最后的n行数据
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档