如何优化Python脚本以高效处理大型CSV文件?
如何优化Python脚本以高效处理大型CSV文件?
处理几个GB级的CSV文件确实是个头疼的问题,我之前也碰到过类似的场景——8GB内存的机器跑脚本经常崩溃,还得等几个小时。结合我的实践经验,给你分享几个既提速度又省内存的优化思路:
一、优化标准库csv模块的现有用法(零额外依赖)
如果不想引入新库,调整现有代码的细节就能带来明显提升:
- 调大文件缓冲区,减少磁盘IO次数
默认的文件缓冲区很小,对于大文件来说会频繁读写磁盘,这是速度慢的核心原因之一。打开文件时指定buffering参数(比如10MB缓冲区),能大幅降低IO开销:
import csv buffer = [] buffer_size = 10000 # 批量写入的行数 # 10MB缓冲区:1024*1024*10 with open('large_file.csv', 'r', buffering=1024*1024*10) as infile, \ open('output_file.csv', 'w', newline='', buffering=1024*1024*10) as outfile: reader = csv.reader(infile) writer = csv.writer(outfile) # 先写入表头(如果原文件有表头的话) header = next(reader) writer.writerow(header) for row in reader: try: # 你的过滤/转换逻辑 if int(row[2]) > 1000: buffer.append(row) # 攒够一批就批量写入 if len(buffer) >= buffer_size: writer.writerows(buffer) buffer = [] except (ValueError, IndexError): # 跳过格式错误的行 continue # 写入剩余的行 if buffer: writer.writerows(buffer)
关键改进:
- 用
writerows批量写入替代逐行writerow,减少系统调用次数 - 调大缓冲区,降低磁盘IO的频率
- 避免不必要的类型转换
如果你的转换逻辑里有大量类型转换(比如int(row[2])),可以提前确认列的类型,或者用更高效的转换方式,比如用fastnumbers库的fast_int函数(比内置int快),不过如果不想加依赖,尽量把转换逻辑简化。
二、用专用高性能库(推荐!)
对于大CSV处理,Python生态有几个专门优化的库,速度和内存效率远超标准库和pandas:
1. Polars(速度+内存双优,首选)
Polars是用Rust开发的DataFrame库,专门针对大型数据集优化,速度比pandas快5-10倍,内存占用只有pandas的1/3左右,而且支持懒加载(不会把整个文件读进内存)。
示例代码:
import polars as pl # 懒加载文件,不占用内存 df = pl.scan_csv('large_file.csv') # 应用过滤和转换(这里用列名,比索引更清晰) # 假设第3列的列名是"value" filtered_df = df.filter(pl.col("value") > 1000) # 把结果写入CSV,延迟执行,全程高效 filtered_df.sink_csv('output_file.csv')
我之前处理5GB的CSV,用原csv脚本要3小时,用Polars只花了20分钟,内存占用一直稳定在几百MB。
2. Dask(适合超大型/分布式数据集)
如果你的文件大到单进程处理不过来(比如几十GB),Dask是个好选择——它可以自动分块处理,利用多核CPU,甚至可以扩展到集群。API和pandas几乎一致,学习成本低:
import dask.dataframe as dd # 自动分块读取文件 df = dd.read_csv('large_file.csv') # 过滤逻辑,和pandas语法完全一样 filtered_df = df[df['value'] > 1000] # 写入结果,支持合并成单个文件 filtered_df.to_csv('output_file.csv', single_file=True, index=False)
Dask的优势是并行处理,适合转换逻辑复杂、CPU密集的场景。
3. 优化Pandas的用法(如果坚持用Pandas)
如果已经习惯Pandas,调整参数也能提升性能:
- 指定列类型:避免Pandas自动推断类型(比如把int推断成object,既占内存又慢)
- 调大chunksize:减少循环次数,比如从10000调到100000
- 关闭低内存检查:
low_memory=False
示例:
import pandas as pd # 预先指定列的类型,比如第3列是int dtypes = {'value': int} # 调大chunksize到10万行 chunks = pd.read_csv( 'large_file.csv', chunksize=100000, dtype=dtypes, low_memory=False ) # 先写入表头 first_chunk = next(chunks) first_chunk[first_chunk['value'] > 1000].to_csv('output_file.csv', index=False) # 处理剩余chunk,追加写入 for chunk in chunks: filtered = chunk[chunk['value'] > 1000] filtered.to_csv('output_file.csv', mode='a', header=False, index=False)
三、其他通用优化技巧
- 用SSD代替机械硬盘:磁盘IO是大文件处理的常见瓶颈,SSD的读写速度是机械硬盘的10-100倍,换SSD后速度会有质的提升。
- 并行处理(CPU密集场景):如果你的转换逻辑是CPU密集型(比如复杂的数据清洗),可以用
multiprocessing或concurrent.futures来并行处理chunk,但要注意避免多个进程同时写文件——最好每个进程处理完chunk写入临时文件,最后合并临时文件。 - 避免不必要的列:如果只需要处理部分列,在读取时指定
usecols参数,比如csv.reader可以配合itertools.islice,Polars/Pandas可以指定usecols=["col1", "col2"],减少内存占用。
总结
- 追求简单快速见效:用Polars,代码少、速度快、内存省
- 超大型/分布式数据:用Dask
- 不想加新依赖:优化标准库csv的缓冲区和批量写入
- 习惯Pandas:调大chunksize+指定列类型
备注:内容来源于stack exchange,提问作者Lior Dahan
相关产品推荐
相关产品推荐

