直接读大csv会爆内存,因默认全载入且类型推断开销大;应分块读取(chunksize)、预筛列(usecols)和指定类型(dtype),超5gb可用dask延迟计算。

直接用 pd.read_csv() 读大文件大概率会卡死或爆内存,必须分块处理或预筛选。
为什么不能直接 pd.read_csv('big.csv')
不是函数不行,是默认行为会把整张表一次性加载进内存。一个 2GB 的 CSV,Pandas 可能吃掉 4–6GB 内存(类型推断 + 字符串对象开销),Jupyter kernel 很容易被系统 kill 或无响应。你看到的“卡住”“没反应”“kernel dead”,八成是这个原因。
- 即使机器有 32GB 内存,Pandas 对超宽(列数 > 500)、超长(行数 > 10M)CSV 的解析效率也会断崖式下降
-
header='infer'默认要扫描前 100 行猜列类型,大文件下这步就卡住 - 中文路径、BOM 头、混合编码(比如某几行是 GBK)会让
read_csv在中途抛UnicodeDecodeError,且不提示具体哪一行
用 chunksize 分批读取并处理
这是最常用也最稳妥的方式:不求一次全载入,只按需拉数据块做计算或清洗。
chunk_list = []
for chunk in pd.read_csv('data.csv', chunksize=50000):
# 每次处理 5 万行
processed_chunk = chunk.dropna().query('price > 0')
chunk_list.append(processed_chunk)
<p>df = pd.concat(chunk_list, ignore_index=True)
</p>
-
chunksize值不是越大越好:50000 是经验起点,可依你内存调到 10000 或 100000 - 别在循环里反复
append到 DataFrame —— 改用 list 收集再pd.concat,否则性能暴跌 - 如果只是想统计、抽样或查错,根本不用
concat,处理完当前 chunk 就break
用 usecols 和 dtype 提前瘦身
很多大 CSV 实际只用其中 3–5 列,其余全是日志字段或冗余 ID。跳过它们能省下 60%+ 内存和时间。
# 只读第 0、2、4 列,且明确指定类型(避免 object)
df = pd.read_csv(
'big.csv',
usecols=[0, 2, 4],
dtype={'user_id': 'category', 'amount': 'float32'}
)
-
usecols接列表(列索引)或字符串列表(列名),比传函数更快 -
category类型对重复值多的 ID/状态列压缩率极高;float32比默认float64节省一半内存 - 千万别信
low_memory=False—— 它只是关掉类型警告,不解决内存问题,还可能让列类型错乱
用 dask.dataframe 替代 pandas(适合 >5GB 场景)
当文件超过 5GB,或你需要跑 groupby/merge 等重操作时,dask 是更合适的选择:它延迟计算、自动分片、支持磁盘溢出。
import dask.dataframe as dd
df = dd.read_csv('huge.csv', blocksize='64MB') # 每块读 64MB
result = df.groupby('region')['sales'].sum().compute() # .compute() 才真正执行
-
blocksize建议设为内存的 1/4~1/2(比如 16GB 内存设'4GB') - 语法和 pandas 高度兼容,但报错信息更晦涩;调试时先用小样本验证逻辑
- 不要用
dask做简单head()或单行过滤 —— 启动调度器开销反而更大
真正麻烦的从来不是“怎么读进来”,而是“读进来之后发现内存又满了”。提前用 usecols 锁列、用 dtype 定类型、用 chunksize 控节奏,比事后杀 kernel 重来强十倍。











