应使用 chunksize 分块读取大 csv 文件,避免内存溢出;配合 usecols 选列、dtype 指定类型、合理设置 chunksize(5000–50000)、处理编码与分隔符异常,并优先存为 parquet 提升后续读取效率。

用 chunksize 分块读取,别一次性 read_csv()
直接调用 pd.read_csv("huge.csv") 很可能内存爆掉——Pandas 会把整个文件加载进内存构造成 DataFrame。真正可行的做法是设 chunksize 参数,让它返回一个可迭代的 TextFileReader 对象。
常见错误是设了 chunksize=10000 却忘了循环处理:
reader = pd.read_csv("data.csv", chunksize=5000)
# ❌ 这行什么也没做,reader 还没被消费
for chunk in reader:
# ✅ 在这里处理每个 chunk,比如过滤、聚合、写入数据库
process(chunk)
-
chunksize不是越小越好:太小(如 100)会导致 I/O 开销剧增;太大(如 50 万)仍可能 OOM;建议从 5000–50000 试起,观察内存占用 - 如果只需统计或抽样,用
nrows+skiprows比分块更轻量 -
dtype强制指定列类型(如{"user_id": "uint32", "score": "float32"})能省 30%–60% 内存,必须加
跳过无用列用 usecols,别让 Pandas 白读
很多超大 CSV 有几十列,但你只关心其中 3–4 列。默认读全量列不仅慢,还浪费内存。用 usecols 可以只加载目标列,性能提升常达 2–5 倍。
注意两种写法效果不同:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
usecols=["id", "name", "amount"]—— 按列名指定,安全但需确认 header 存在且无歧义 -
usecols=[0, 2, 5]—— 按位置指定,更快(跳过列名解析),适合结构固定、无 header 或 header 行混乱的文件 - 如果列名含空格或特殊字符,用位置方式更稳;若用列名,建议先用
pd.read_csv(..., nrows=0)快速获取列名列表
处理编码和分隔符异常:别卡在第一行
超大文件出错往往不是数据量问题,而是第 1 行就因编码或分隔符不一致报错,比如 UnicodeDecodeError 或 ParserError: Error tokenizing data。
- 先用命令行快速探查:
head -n 3 huge.csv | cat -A看是否含^M(Windows 换行)、\t或隐藏控制字符 -
encoding别硬写"utf-8",试试"utf-8-sig"(去 BOM)或"latin1"(总能读,不报错) - 分隔符不确定时,用
sep=r"\s+"处理多空格,或sep=";"应对分号 CSV;必要时加engine="python"支持正则分隔,但速度略降 - 遇到脏数据(如某行多一个逗号),加
on_bad_lines="skip"(pandas ≥ 1.3)或旧版用error_bad_lines=False
写入中间结果时优先用 to_parquet,别再碰 CSV
如果你需要反复读取同一份清洗后的子集,每次重新 read_csv 分块处理是低效的。清洗完立刻存成 Parquet:
# 清洗后保存为列式格式
chunk.to_parquet(f"clean_chunk_{i}.parquet", index=False)
# 下次直接 pd.read_parquet(),快 5–10 倍,内存占用低 60%+
- Parquet 支持按列读取(
columns=["a", "b"]),比 CSV 的usecols更彻底 - 如果最终要进数据库,用
chunk.to_sql()批量插入,别转成 list 再循环execute() - 避免用
pd.concat(chunks)拼回完整 DataFrame——这等于又回到内存爆炸的老路
真正难的不是“怎么读”,而是判断哪部分逻辑必须在内存里做、哪部分可以流式丢弃。比如计数可以边读边累加,但排序就必须全量载入或改用外部排序工具。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










