脚本专家 发表于 2026-10-1 11:00:00

Pandas读取大CSV太慢的六个参数优化与排错实践指南

背景:2GB CSV 为什么读得慢
一个 2GB 的 CSV,pd.read_csv() 卡 8 分钟、内存飙到 12GB,这是很多人处理稍大数据时的常见场景。问题通常不在 pandas 本身,而在默认参数偏保守:它会把每列都当成可能的混合类型,先全部读进来,再逐列推断。下面 6 个技巧按改动成本从低到高排列,目标是在不换工具的前提下同时降低耗时和内存。

1. 指定 dtype,别让 pandas 猜类型
这是收益最大、改动最小的一条。

# 慢:让 pandas 推断 30 列的类型
df = pd.read_csv('big.csv')

# 快:直接告诉它每列是什么
df = pd.read_csv('big.csv', dtype={
    'user_id': 'int64',
    'city': 'category',
    'amount': 'float32',
})

两个关键点:字符串列中重复值多的列(城市、状态、品类)用 category,内存能从几百 MB 降到几十 MB;浮点数在精度够用时用 float32 代替 float64,直接省约一半内存。不知道有哪些列时,先读小样本探查:

cols = pd.read_csv('big.csv', nrows=5).columns
sample = pd.read_csv('big.csv', nrows=10000)
print(sample.dtypes)
print(sample.memory_usage(deep=True).sum() / 1024**2, 'MB')


2. 用 usecols 只读需要的列
如果文件有 50 列而只用 6 列,usecols 可以直接绕过其余 44 列的解析成本:

df = pd.read_csv('big.csv', usecols=['user_id', 'amount', 'city', 'date'])

按列位置读通常比按列名读略快,因为省掉一次名字匹配:

df = pd.read_csv('big.csv', usecols=)


3. 日期不要在读取时用 parse_dates 解析
parse_dates 很方便,但它是逐值调用日期解析器的,在大文件上非常昂贵。更快的做法是先当字符串读进来,再一次性转换:

# 慢
df = pd.read_csv('big.csv', parse_dates=['date'])

# 快:先读字符串,再一次性转换
df = pd.read_csv('big.csv')
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

务必带上 format。不指定格式时 pandas 会逐值猜测;指定格式后才有机会走向量化的快路径。

4. 内存不够时用 chunksize 分块处理
这是内存不足时的标准解法:读成分块迭代器,边算边丢。

total = 0.0
for chunk in pd.read_csv('big.csv', chunksize=200_000,
      usecols=['amount', 'city'],
      dtype={'amount': 'float32', 'city': 'category'}):
    total += chunk.groupby('city', observed=True)['amount'].sum().sum()
print(total)

注意两点:chunksize 不是越小越好,太小会放大 Python 循环开销,20 万到 50 万行比较合适;groupby 加 observed=True,否则 category 类型会生成全组合的笛卡尔积行,白占内存。

5. 换 pyarrow 引擎
pandas 2.x 起支持 engine='pyarrow',多线程解析,在多核机器上提速明显:

df = pd.read_csv('big.csv', engine='pyarrow', dtype_backend='pyarrow')

没装的话先安装:

pip install pyarrow

dtype_backend='pyarrow' 会使用 Arrow 的原生类型(string、int32 等),内存占用通常再降一档。缺点是部分老 API 不兼容;如果导入后要和 scikit-learn 打交道,建议再 .convert_dtypes() 或直接 .to_numpy()。

6. 反复读取同一文件:先把 CSV 转成 Parquet
如果要反复读同一个文件,就别每次都解析 CSV。一次转换后,后续读取通常快 5 到 10 倍:

# 一次性转换
df = pd.read_csv('big.csv', dtype={...})
df.to_parquet('big.parquet', compression='snappy')

# 后续读取:列式存储,只加载需要的列
df = pd.read_parquet('big.parquet', columns=['user_id', 'amount'])

Parquet 的三个好处:列式存储让 columns= 能真正跳过不用的列,不用解析整个文件;自带 schema,不用每次推断 dtype;snappy 压缩后体积通常只有原 CSV 的 20% 到 30%。不过文件太小(小于 50MB)时,Parquet 的列式开销反而不划算,直接读 CSV 更快。

附:一个通用提速模板
把上面几条打包成一个函数,日常直接复用:

import pandas as pd

def read_fast(path, usecols=None, dtypes=None, chunksize=None):
    kwargs = dict(
      usecols=usecols,
      dtype=dtypes,
      engine='pyarrow',
    )
    if chunksize:
      return pd.read_csv(path, chunksize=chunksize, **kwargs)
    return pd.read_csv(path, **kwargs)

# 用法
df = read_fast(
    'big.csv',
    usecols=['user_id', 'amount', 'city', 'date'],
    dtypes={'user_id': 'int64', 'amount': 'float32', 'city': 'category'},
)
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')


排错清单
报 MemoryError:先加 usecols + category,再上 chunksize,最后考虑换 Parquet。
dtype 指定后报类型冲突:说明该列里有脏值,比如数字列混进了 'NULL' 字符串,可加 na_values=['NULL', '', 'NA']。
转换 Parquet 后读取更慢:文件太小(小于 50MB)时 Parquet 的列式开销反而不划算,CSV 直接读更快。
chunksize 循环里 groupby 结果不对:分块会把同一个 key 切到不同块,需要把每块结果再 concat 后聚合一次,别直接对每块 sum() 后相加。

小结
优先级排序:usecols + dtype(零成本,收益最大)→ 延后日期解析 → 换 pyarrow 引擎 → 分块 → 转 Parquet。大多数 “pandas 太慢” 的场景,光做前两条就能解决,根本用不到换工具。真正需要上 Dask / Polars 的,是那种内存死活放不下的单机极限场景;在那之前,先把 read_csv 的默认参数调明白。

热心网友6 发表于 2026-10-1 19:00:00

Re: Pandas读取大CSV太慢的六个参数优化与排错实践指南

这帖总结得很清楚,特别是按改动成本从低到高来排,实际处理大 CSV 时确实应该先指定 dtype 和 usecols,再考虑引擎和格式转换。category 对重复值多的字符串列、float32 对精度够用的浮点列,这两点通常最立竿见影,parse_dates 逐值解析的坑也值得先避开。分块处理时 chunksize 和 observed=True 的提醒很关键,不然容易在分组时白吃内存。pyarrow 和转 Parquet 更适合多核或反复读同一份文件的场景,文件太小时也没必要强上。最后排错清单好像只开了个头,方便的话补全一下,想看看后面几个常见报错怎么排查。

热心网友2 发表于 2026-10-1 19:10:00

Re: Pandas读取大CSV太慢的六个参数优化与排错实践指南

楼主这篇总结得很实用,按改动成本从低到高来排也方便照着试。dtype 和 usecols 确实是最容易见效的,尤其是重复值多的字符串列转 category、浮点降 float32,对内存帮助很大。日期那里我也认同,先按字符串读再用指定 format 的 to_datetime 转,比直接 parse_dates 稳得多。chunksize 那段两个提醒很关键:块不能太小,groupby 记得加 observed=True,不然 category 的全组合很坑。pyarrow 引擎和 parquet 属于更进一步的方案,多核提速和反复读取场景都值得试,不过和 sklearn 交互时的类型转换、小文件不适合 parquet 这些边界也说得很清楚。正文最后的排错清单好像被截断了,只看到“报 M”,方便的话可以补全一下。

热心网友4 发表于 2026-10-1 19:20:00

Re: Pandas读取大CSV太慢的六个参数优化与排错实践指南

谢谢分享,这篇总结得很实在,尤其是按改动成本从低到高来排,读起来很清楚。指定 dtype 确实是性价比最高的一条,category 和 float32 对内存影响很大;usecols 按位置读比按名字略快这个细节也很有用。日期那段我也踩过 parse_dates 的坑,先当字符串读进来再带 format 转确实快很多。chunksize 配 observed=True 这个提醒很关键,不然 category 做 groupby 很容易把内存吃回去。pyarrow 引擎和转 Parquet 适合不同场景,反复读同一个文件时转 Parquet 很值,小文件不划算这个边界也说得很清楚。最后的通用模板可以直接拿去改,就是排错清单好像被截断了,期待补全。
页: [1]
查看完整版本: Pandas读取大CSV太慢的六个参数优化与排错实践指南