chunksize应根据单行平均字节和可用内存合理设置,建议先估算均行长,再以50000起步实测;需透传dtype等参数,避免类型推断错误;全局统计须累积计算,写库需事务控制;务必显式关闭TextFileReader。

read_csv 的 chunksize 参数到底怎么设才不崩内存
直接用 read_csv 读千万行 CSV 很容易 OOM,关键不是“能不能分块”,而是 chunksize 设多大才合理。它不是越大越好,也不是越小越稳——得看单行平均字节和可用内存。比如一行平均 200 字节,100 万行就是约 200MB;若机器只有 4GB 可用内存,chunksize 超过 15 万就可能触发频繁 GC 或直接报 MemoryError。
实操建议:
- 先用
head -n 1000 file.csv | wc -c估算单行均长,再乘以目标 chunk 行数反推内存占用 - 首次尝试从
chunksize=50000起步,在 notebook 里跑一两块观察top或psutil.virtual_memory() - 避免设成
chunksize=1:IO 开销暴涨,Pandas 内部解析初始化成本被反复放大 - 如果列含长文本或嵌套 JSON,chunksize 要进一步砍半——字符串对象在 Python 中的内存开销远高于原始字节
分块后 merge / groupby / to_sql 容易漏掉全局逻辑
逐块 pd.concat 再计算,看似稳妥,但像 groupby().agg('mean') 或 value_counts(normalize=True) 这类操作,直接在每块上算再平均会出错——因为分母不是全局总数。同样,to_sql 默认不处理主键冲突或重复写入。
常见错误现象:ValueError: Tables not defined(SQL 表未预建)、IntegrityError: UNIQUE constraint failed(重复 ID 写入)、结果统计值偏差超 10%。
实操建议:
- 全局统计类操作:先用第一块
.columns和.dtypes定义空容器(如dict存各组 sum/count),每块迭代更新,最后统一除总 count - 写数据库:用
if_exists='append'+ 显式事务控制,或先写临时表再INSERT OR REPLACE合并 - 需要去重或排序的场景(如取 top-k):每块先局部 top-k,最后合并再全局 top-k,别省这一步
dtype 和 parse_dates 不传进 chunk 循环里等于白设
很多人把 dtype、parse_dates、usecols 放在第一次 read_csv 调用里,但忘了——每个 chunk 都是独立调用 read_csv,参数必须显式透传进去,否则 Pandas 会为每块重新推断类型,既慢又不准。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
典型后果:object 列本该是 int64,因某块含空值被判成字符串;日期列没 parse_dates 就变成字符串,后续 .dt.month 报 AttributeError。
实操建议:
- 把所有关键参数打包成 dict,例如
csv_kwargs = {'usecols': [...], 'dtype': {...}, 'parse_dates': [...], 'chunksize': 50000},然后for chunk in pd.read_csv('x.csv', **csv_kwargs): - 对含混合类型的数值列(如 '1'、'2.5'、''),强制指定
dtype={'col': 'string'}再手动pd.to_numeric(..., errors='coerce'),比让 Pandas 自推更可控 - 时间列若含时区或非标准格式(如 '2023-01-01T12:00:00+08:00'),别依赖
parse_dates,改用date_parser参数传自定义函数
用 iterator=True + get_chunk() 比 for 循环更灵活但更易忘关
pd.read_csv(..., chunksize=N) 返回的是 TextFileReader 对象,本质是带状态的迭代器。如果中途 break 或异常退出,没显式 .close(),底层文件句柄可能卡住,尤其在 Windows 上会导致后续无法删除或重命名该 CSV 文件。
性能影响:不用 iterator=True 也能循环,但 get_chunk() 允许动态调整每次读多少行(比如前几块快,后面遇到长文本行就自动降 chunksize),而普通 for 循环 chunksize 是死的。
实操建议:
- 务必用
with pd.read_csv(..., chunksize=N) as reader:(虽然 Pandas 当前版本还没实现__enter__,所以实际得手写 try/finally) - 更稳妥写法:
reader = pd.read_csv('data.csv', chunksize=50000) try: for chunk in reader: process(chunk) finally: reader.close() - 调试时想看某一块,用
reader.get_chunk(1000)提前取样,比等完整循环快得多
分块读取本身不难,难的是每一块都得当“独立数据集”来验逻辑——类型、缺失、边界值、聚合维度,全得在 chunk 粒度上重新校准。没人会在意你用了 chunksize=100000 还是 87321,但所有人会立刻发现统计结果差了 3%。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










