用 chunksize 分块读取可避免 oom,需配合 for 循环触发实际读取、合并局部统计量、显式指定 dtype、调优 chunksize 并关闭冗余解析;超大文件优先选 polars 或 dask。

pd.read_csv 读大文件直接 OOM 怎么办
别硬扛,pd.read_csv 默认把整个文件塞进内存,几 GB 的 CSV 很容易触发 MemoryError。核心解法是分块读取 + 流式处理,不是“调大内存”或换机器。
实操建议:
- 用
chunksize参数(单位:行数),返回TextFileReader迭代器,每次只加载一块 - 避免写
df = pd.read_csv(..., chunksize=10000)就停——那只是创建了迭代器,没真正读数据 - 必须用
for chunk in df:或next(df)触发实际读取 - 如果后续要聚合(比如求总和、均值),别在每块里算完整结果再拼——先算局部统计量,最后合并(如:各块的
sum和count,再算全局均值)
分块后 merge / groupby 报错 KeyError 或结果不对
常见现象:单块能跑通,循环里做 groupby 或 merge 却提示列不存在,或最终结果比预期少——本质是没处理好跨块逻辑。
原因和对策:
-
merge不能跨块做:除非右表很小,否则必须提前载入内存;若右表也大,改用pd.concat([chunk.merge(small_df) for chunk in reader]),但注意内存仍可能涨——更稳的是用dask或polars -
groupby默认只在当前 chunk 内聚合:想全量分组,得先收集所有分组键的唯一值(比如用set.union(*[set(chunk['key']) for chunk in reader])),再按 key 分批过滤 + 合并,或改用pd.concat(chunks).groupby(...)(仅限总内存够) - 注意
dtype不一致:不同 chunk 可能推断出不同列类型(尤其含空值的字符串列),导致concat失败。务必在read_csv中显式传dtype={'col': 'string'}或converters
用 chunksize 后速度反而更慢?
不是 bug,是 I/O 和 Python 开销叠加的结果。小 chunksize(如 100 行)会让磁盘反复寻道,且每块都触发 Pandas 解析开销;太大又起不到降内存作用。
调优要点:
- 从
chunksize=50000起试,观察内存峰值(用psutil.Process().memory_info().rss)和单块耗时 - 关掉不必要的解析:加
usecols只读需要的列,parse_dates=False避免自动转时间,low_memory=False省去类型推测 - CSV 编码/分隔符不标准?提前用
encoding='utf-8-sig'或sep=';'显式指定,否则每块都报错重试 - SSD 上差异不大,但机械硬盘上,
chunksize太小会让吞吐暴跌——这时候不如换pyarrow后端:pd.read_csv(..., engine='pyarrow'),它本身支持更高效的流式读
想彻底绕过 pandas 内存瓶颈,有什么轻量替代
当文件稳定超 10GB、字段多、计算逻辑复杂时,硬调 chunksize 会越来越难维护。这时候该考虑不把整行加载成 Series 的方案。
可行路径:
- 用
csv模块 + 生成器手动解析:适合只取几列、做简单过滤或计数,内存恒定在 KB 级,但没了向量化能力 -
polars的scan_csv:不立刻读内存,返回惰性 DataFrame,.collect()才执行,且自带分块优化,语法接近 pandas,迁移成本低 - 真要长期处理 TB 级?别卡在单机:
dask.dataframe能模拟 pandas API,但底层自动分块+调度,不过要注意compute()仍可能爆内存——得配合persist()和磁盘 spill
最常被忽略的一点:分块不是万能的,它解决的是「读」的内存压力,但如果你在每块里建了大型中间结构(比如 chunk.groupby('x').apply(lambda g: big_func(g))),内存峰值可能出现在处理阶段而非读取阶段——得盯紧每一步的对象生命周期。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











