代码 | 如何处理远超电脑内存的csv文件
一、问题
最近分享的数据集都是体量巨大,
下图是 数据集 | 3571万条专利申请数据集(1985-2022年) 截图,其中广东省.csv 已经超过很多学员电脑内存(现在常见的笔记本内存是8G和16G),我们应该如何应对这类 巨大csv文件 呢?
二、思路
一般应对 广东省.csv 这种巨大csv文件,可以从以下两大类思路:
思路1. 使用更高配置的电脑
思路2. 花点功夫学大文件处理技巧
2.1 使用更高配置的电脑(服务器)
思路1, 方法简单,思路简单, 写代码的方式一如既往, 认知成本低, 美中不足要花钱。
买电脑;如果你不差钱,直接换更好的电脑, 8G-->16G-->32G-->64-->96G-->128G... 预算决定数据处理能力的上限。 租用服务器;如果差钱,资金不足脑力凑。租用服务器的难点是像你我刚接触电脑一样,要熟悉服务器操作,前期存在较大的认知难度和学习难度。
2.2 花点功夫学大文件处理技巧
网上关于处理大文件的技巧虽然很多,比如针对每个字段的数据类型,整形、浮点型、64位、32位, 反正大邓是不太懂。咱们学python的原则是,用最少的时间学到最常用最有用的,解决80%的问题,剩下的20%太难的问题还是交给专业人士。假设你我电脑内存是8G,要在此环境下进行数据处理, 以下是常见的处理方法
读取前n条记录 读取某个(些)字段 小批次读取 转csv为xlsx
在接下来的章节中,我们重点分享以上5类技巧代码。
三、代码技巧
以csv、xlsx这类数据, 每行代表一条记录,每列代表一个字段,而文件体积是由行数和列数决定。而 pd.read_csv有三个最常用的参数nrows、usecols、chunksize,分别决定读前nrows行、选择usecols列读取、按照chunksize分批次读取。
以 广东省.csv 有10.42G, 而电脑内存只有8G, 对这个csv文件,除了知道文件名,其他信息一无所知。这时候最简单的技巧就是尝试着读取前n条记录,先了解字段有哪些。
3.1 读取前n条记录
3.1.1 参数nrows
读取前n条记录, 了解csv字段有哪些
import pandas as pd
#只读取csv中前100条记录
df = pd.read_csv('广东省.csv', nrows=5)
df.head()
字段太多, 很多字段隐藏起来。详细了解csv的字段信息,还需要
df.columns 获得所有的字段名 df.info(memory_usage='deep') 所有字段及字段数据类型 df.memory_usage(deep=True) 每个字段占用的系统内存
3.1.2 df.columns
获得所有的字段名
df.columns
Run
Index(['专利公开号', '专利名称', '专利类型', '专利摘要', '申请人', '专利申请号', '申请日', '申请公布日',
'授权公布号', '授权公布日', '申请地址', '主权项', '发明人', '分类号', '主分类号', '代理机构', '分案原申请号',
'优先权', '国际申请', '国际公布', '代理人', '省份或国家代码', '法律状态', '专利领域', '专利学科',
'多次公布'],
dtype='object')
3.1.3 df.info(memory_usage='deep')
单单用df.columns只知其名,但无法内部什么数据结构。使用df.info()
df.info(memory_usage='deep')
Run
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 26 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 专利公开号 5 non-null object
1 专利名称 5 non-null object
2 专利类型 5 non-null object
3 专利摘要 5 non-null object
4 申请人 5 non-null object
5 专利申请号 5 non-null object
6 申请日 5 non-null object
7 申请公布日 0 non-null float64
8 授权公布号 5 non-null object
9 授权公布日 5 non-null object
10 申请地址 5 non-null object
11 主权项 3 non-null object
12 发明人 5 non-null object
13 分类号 5 non-null object
14 主分类号 5 non-null object
15 代理机构 4 non-null object
16 分案原申请号 0 non-null float64
17 优先权 0 non-null float64
18 国际申请 0 non-null float64
19 国际公布 0 non-null float64
20 代理人 4 non-null object
21 省份或国家代码 5 non-null int64
22 法律状态 5 non-null object
23 专利领域 5 non-null object
24 专利学科 5 non-null object
25 多次公布 0 non-null float64
dtypes: float64(6), int64(1), object(19)
memory usage: 14.7 KB
Dtype中,object是字符串(文本)型数据;float64是数字型数据。5条记录占用系统内存14.7kb
3.1.4 df.memory_usage(deep=True)
但最好能了解每个字段占用的系统内存, 后续可以决定只读取必要的字段,减少内存占用。
df.memory_usage(deep=True)
Run
Index 132
专利公开号 342
专利名称 512
专利类型 450
专利摘要 2434
申请人 528
专利申请号 361
申请日 335
申请公布日 40
授权公布号 342
授权公布日 335
申请地址 668
主权项 2322
发明人 450
分类号 366
主分类号 320
代理机构 496
分案原申请号 40
优先权 40
国际申请 40
国际公布 40
代理人 390
省份或国家代码 40
法律状态 3084
专利领域 470
专利学科 474
多次公布 40
dtype: int64
对字段进行排序, 按照占用内存从大到小排列
df.memory_usage(deep=True).sort_values(ascending=False)
Run
法律状态 3084
专利摘要 2434
主权项 2322
申请地址 668
申请人 528
专利名称 512
代理机构 496
专利学科 474
专利领域 470
发明人 450
专利类型 450
代理人 390
分类号 366
专利申请号 361
专利公开号 342
授权公布号 342
授权公布日 335
申请日 335
主分类号 320
Index 132
国际公布 40
国际申请 40
省份或国家代码 40
优先权 40
分案原申请号 40
申请公布日 40
多次公布 40
dtype: int64
5条记录总内存(单位:字节)
df.memory_usage(deep=True).sum()
Run
15091
3.2 读取某字段
使用usecols参数,设置只读取某个(些)字段
#读取前5行,且只读入 '专利公开号', '专利名称', '法律状态' 这3个字段
df2 = pd.read_csv('广东省.csv', nrows=5, usecols=['专利公开号', '专利名称', '法律状态'])
df2
同样是5条记录, 读入全字段和读入选定字段,占用系统内存分别是
print('全字段: ', df.memory_usage(deep=True).sum())
print('选定字段: ', df2.memory_usage(deep=True).sum())
de_x = df.memory_usage(deep=True).sum()/df2.memory_usage(deep=True).sum()
print(f'压缩倍数: {round(de_x, 2)}')
Run
全字段: 15091
选定字段: 4070
压缩倍数: 3.71
所以电脑内存仅为8G, 是能够处理几倍于内存的巨大csv文件, 具体取决度数据中字段占用内存的情况。
3.3 小批次读取
当探索完前n行,选中某些列,我们已经了解了哪些字段是我们必须要用的, 占用系统内存的大小。接下来,我们就可以尝试着按照批次读取数据。后面一节,我们尝试转csv为xlsx,其实就是按批次读取, 将数据转为体量更小的xlsx。
为了让实验简单高效,我们假设只读取前50行, 每批次是5行。对比下占用系统内存的量
import pandas as pd
#一次性读取
df100 = pd.read_csv('广东省.csv', nrows=50)
print(df100.memory_usage(deep=True).sum(), end='\n\n')
#分批次读取
#每5条记录是一个批次,得到chunk_dfs
chunk_dfs = pd.read_csv('广东省.csv', chunksize=5, nrows=50)
#每个chunk_df就是我们熟悉的dataframe类型数据
for chunk_df in chunk_dfs:
print(chunk_df.memory_usage(deep=True).sum())
Run
一次性读取
147200
分批次读取
15091
15709
15475
15383
13999
14173
14151
14343
13313
16751
在实践中,nrows 和 chunksize不会同时出现, 而且chunksize一般都会设置的很大,例如1000条或者10000条。
chunk_dfs = pd.read_csv('csv文件', chunksize=1000, nrows=50)
看到chunk_dfs也不要害怕,其实每个chunk_df就是我们熟悉的df,即dataframe数据类型。
3.4 转csv为xlsx
有了chunksize参数的学习经历, 就可以尝试着把csv转为xlsx。同样的数据量,存储到 xlsx比csv占用的存储空间要小很多,如下图
本节不是什么新知识, 可以结合之前知识和本文前面的技巧, 实现csv转xlsx。需要注意的是每个xlsx最多只能存储104万行(记录),所以像 广东省.csv 这类巨大csv文件,如果真要转文件格式, 会转换成很多个xlsx的。
import pandas as pd
# 原csv路径和新csv文件前缀
csvf = 'large_file.csv'
xlf_prefix = 'small_file'
# chunksize不能超过104w
# 因为xlsx最多仅可存储104w行
chunksize = 1000000
# 定义文件编号
file_counter = 1
# 读取csv文件并拆分成多个小块的DataFrame
chunk_dfs = pd.read_csv(csvf, chunksize=chunksize):
for chunk_df in chunk_dfs:
# 将每块DataFrame保存为一个单独的csv文件
chunk_df.to_excel(f'{xlf_prefix}_{file_counter}.xlsx', index=False)
file_counter += 1
上述代码会将大型CSV文件拆分成多个小块的DataFrame,每个DataFrame包含1000000行数据。
然后,它将每个小DataFrame保存为单独的csv文件,文件名以“small_file_”作为前缀,后面跟着一个编号。
四、总结
记住这行代码
pd.read_csv(csvf, nrows, usecols, chunksize)
8G内存的电脑, 通过以上技巧,基本可以把我们应对大数据的潜力放大N倍。N可以是几倍、十几倍、几十倍、上百倍...,放大潜力的过程起主要作用的是usecols和chunksize这两个参数。
精选内容
数据集 | 3571万条专利申请数据集(1985-2022年)
预训练模型 | 使用1000w专利摘要训练word2vec模型,可用于开发词典
使用中文MD&A数据集训练word2vec预训练模型, 可扩展或新建会计金融等领域的情感词典
数据集 | 2001-2022年A股上市公司年报&管理层讨论与分析
管理世界 | 用正则表达式、文本向量化、线性回归算法从md&a数据中计算 「企业融资约束指标」
中国管理科学 | 使用业绩说明会文本数据测量上市公司前瞻性信息
数据集 | 2001-2022年A股上市公司年报&管理层讨论与分析