如何在Python中高效读取大型CSV文件并避免内存不足问题
如何在Python中高效读取大型CSV文件并避免内存不足问题
嗨,我完全懂处理千万级行CSV时内存不够用的崩溃感——之前做用户行为分析时,我也跟你一样踩过这个坑!下面就针对你提到的几种方法详细拆解,再分享些实战中的最佳实践,帮你搞定大文件处理:
一、用Pandas的chunksize分块读取(平衡易用性和内存)
这个方法是我处理大CSV时最常用的,既保留了Pandas的便捷性,又不会一次性把文件塞进内存。核心思路是把文件拆成小“块”,逐块处理后释放内存,再处理下一块。
举个实际例子,比如你要过滤某列大于100的行,同时统计该列总和:
import pandas as pd # 根据你的内存大小设置块大小,比如10万行(可以按需调整) chunk_size = 100000 # 创建块迭代器,指定数据类型节省内存 chunk_iter = pd.read_csv('large_file.csv', chunksize=chunk_size, dtype={'value': 'float32', 'id': 'int32'}) # 初始化结果变量,或者准备输出文件 total_value = 0 output_path = 'filtered_data.csv' # 先处理第一个块,写入表头 first_chunk = next(chunk_iter) filtered_first = first_chunk[first_chunk['value'] > 100] filtered_first.to_csv(output_path, index=False) total_value += first_chunk['value'].sum() # 循环处理剩余块 for chunk in chunk_iter: # 这里写你的业务逻辑:过滤、计算、聚合都可以 filtered_chunk = chunk[chunk['value'] > 100] # 追加到输出文件,不要重复写表头 filtered_chunk.to_csv(output_path, mode='a', header=False, index=False) # 累加统计值 total_value += chunk['value'].sum() print(f"列value的总和是:{total_value}")
注意事项:
- 不要把所有块都存在列表里(比如
chunks = list(chunk_iter)),这样又会占满内存; - 尽量在块级别完成计算(比如累加、分组统计),避免把所有数据合并成一个大DataFrame;
- 一定要指定
dtype,默认的int64/float64会占用双倍内存,用int32/float32足够的话能省一半空间。
二、Dask DataFrame(大文件的Pandas平替)
Dask绝对是处理超大型数据集的好选择——它的API几乎和Pandas一模一样,不用改太多代码就能实现“大于内存”的数据处理。它会自动把数据拆成块,后台并行处理,最后按需把结果拉回内存。
示例代码:
import dask.dataframe as dd # 读取CSV,Dask会自动分块(默认块大小是64MB左右) ddf = dd.read_csv('large_file.csv', usecols=['id', 'category', 'value'], # 只读需要的列 dtype={'value': 'float32'}) # 做和Pandas一样的操作:过滤、分组统计 filtered_ddf = ddf[ddf['value'] > 100] # 分组求均值,注意这里只是定义计算逻辑,还没执行 group_mean = filtered_ddf.groupby('category')['value'].mean() # 执行计算并获取结果(如果结果不大的话) result = group_mean.compute() print(result) # 把过滤后的数据写入文件,支持生成单个文件或多个分块文件 filtered_ddf.to_csv('dask_filtered.csv', single_file=True, index=False)
适用场景:
- 当你的代码本来就是用Pandas写的,不想重构,只是数据太大装不下内存;
- 需要并行处理来加快速度(Dask会利用多核CPU);
- 如果最终计算结果还是很大,也可以继续用Dask的API处理,不用拉回内存。
三、原生csv模块(极致内存节省)
如果你的处理逻辑比较简单,而且对内存占用要求极致,原生的csv.reader或csv.DictReader是最佳选择——它逐行读取文件,内存占用几乎可以忽略,缺点是需要自己处理数据类型、缺失值这些细节,速度也不如Pandas/Dask。
示例:
import csv total_value = 0 # 用DictReader可以按列名访问行数据,比reader更直观 with open('large_file.csv', 'r', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: # 手动转换数据类型(避免字符串占用冗余内存) value = float(row['value']) if value > 100: # 这里可以写入新文件,或者做简单计算 total_value += value print(f"列value的总和是:{total_value}")
适用场景:
- 内存极其有限(比如低配服务器);
- 处理逻辑简单,不需要复杂的Pandas操作(比如只是过滤、计数、简单累加)。
额外优化技巧
- 只读取需要的列:不管用哪种方法,都用
usecols参数指定要加载的列,避免加载无关数据; - 处理缺失值:提前用
na_values指定缺失值标识(比如pd.read_csv(..., na_values=['NA', ''])),避免Pandas把缺失值识别成其他类型; - 转成更高效的文件格式:如果可以的话,把CSV转成Parquet或Feather——这些是列存储格式,压缩率高,读取速度快,内存占用只有CSV的几分之一,Pandas和Dask都支持读写;
- 并行加速:如果用原生
csv模块,可以结合multiprocessing做并行处理(注意要把文件分成多个部分,避免多进程同时读同一个文件的冲突); - 监控内存使用:用
memory_profiler库监控代码的内存占用,找到内存瓶颈所在。
备注:内容来源于stack exchange,提问作者R. Marolahy




