ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

pandas 读大 CSV 太慢?6 个实测提速技巧(dtype / 分块 / 引擎选择)

pandas 读大 CSV 太慢?6 个实测提速技巧(dtype / 分块 / 引擎选择) pandas 读大 CSV 太慢6 个实测提速技巧dtype / 分块 / 引擎选择关键词前置pandas、read_csv、大文件、dtype、分块读取、提速一个 2GB 的 CSVpd.read_csv()卡了 8 分钟内存还飙到 12GB——这是很多人第一次处理稍微大一点的数据时都会遇到的场面。问题基本不在 pandas 慢而在默认参数太保守它会把每一列都当成可能的混合类型先全部读进来再逐个推断。下面 6 个技巧按改动成本从低到高排列实测能把耗时和内存同时砍掉一大截。1. 先指定 dtype别让 pandas 猜这是收益最大、改动最小的一条。# 慢让 pandas 推断 30 列的类型dfpd.read_csv(big.csv)# 快直接告诉它每列是什么dfpd.read_csv(big.csv,dtype{user_id:int64,city:category,amount:float32,})两个关键点字符串列用category重复值多的列城市、状态、品类转 category 后内存能从几百 MB 降到几十 MB浮点数能用float32就别用float64精度够用的场景直接省一半内存。不知道有哪些列先读一小段探一下colspd.read_csv(big.csv,nrows5).columns samplepd.read_csv(big.csv,nrows10000)print(sample.dtypes)print(sample.memory_usage(deepTrue).sum()/1024**2,MB)2. 只读需要的列usecols如果文件有 50 列而你只用 6 列usecols是直接绕过剩下 44 列的解析成本dfpd.read_csv(big.csv,usecols[user_id,amount,city,date])用列的位置也比用名字快一点点省掉一次名字匹配dfpd.read_csv(big.csv,usecols[0,3,7,11])3. 日期别在读取时解析读进来再转parse_dates很方便但它是逐值调用日期解析器的在大文件上极其昂贵。# 慢dfpd.read_csv(big.csv,parse_dates[date])# 快先当字符串读进来再一次性转dfpd.read_csv(big.csv)df[date]pd.to_datetime(df[date],format%Y-%m-%d)务必带上format。不指定格式时 pandas 会逐值猜测指定了才能走向量化的快路径。4. 分块处理chunksize内存不够时的标准解法。读成分块迭代器边算边丢total0.0forchunkinpd.read_csv(big.csv,chunksize200_000,usecols[amount,city],dtype{amount:float32,city:category}):totalchunk.groupby(city,observedTrue)[amount].sum().sum()print(total)注意两点chunksize不是越小越好太小会放大 Python 循环开销20 万50 万行比较合适groupby加observedTrue否则 category 类型会生成全组合的笛卡尔积行白占内存。5. 换引擎pyarrow 比默认 C 引擎更快pandas 2.x 起支持enginepyarrow多线程解析在多核机器上提速明显dfpd.read_csv(big.csv,enginepyarrow,dtype_backendpyarrow)没装就先装pipinstallpyarrowdtype_backendpyarrow会用 Arrow 的原生类型string、int32 等内存占用通常再降一档。缺点是部分老 API 不兼容导入后如果要和 scikit-learn 打交道建议再.convert_dtypes()或直接.to_numpy()。6. 终极方案先把 CSV 转成 Parquet如果你要反复读同一个文件那就别每次都解析 CSV 了。一次转换后续读取快 510 倍# 一次性转换dfpd.read_csv(big.csv,dtype{...})df.to_parquet(big.parquet,compressionsnappy)# 后续读取列式存储只加载需要的列dfpd.read_parquet(big.parquet,columns[user_id,amount])Parquet 的三个好处列式存储columns能真正跳过不用的列不用解析整个文件自带 schema不用每次推断 dtype体积小snappy 压缩后通常只有原 CSV 的 20%30%。附一个通用提速模板把上面几条打包成一个函数日常直接复用importpandasaspddefread_fast(path,usecolsNone,dtypesNone,chunksizeNone):kwargsdict(usecolsusecols,dtypedtypes,enginepyarrow,)ifchunksize:returnpd.read_csv(path,chunksizechunksize,**kwargs)returnpd.read_csv(path,**kwargs)# 用法dfread_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先加usecolscategory再上chunksize最后考虑换 Parquetdtype 指定后报类型冲突说明该列里有脏值比如数字列混进了NULL字符串加na_values[NULL, , NA]转换 Parquet 后读取更慢文件太小 50MB时 Parquet 的列式开销反而不划算CSV 直接读更快chunksize循环里groupby结果不对分块会把同一 key 切到不同块需要把每块的结果再concat后聚合一次别直接对每块sum()后相加。小结优先级排序usecolsdtype零成本收益最大→ 延后日期解析 → 换 pyarrow 引擎 → 分块 → 转 Parquet。大多数pandas 太慢的场景光做前两条就能解决根本用不到换工具。真正需要上 Dask / Polars 的是那种内存死活放不下的单机极限场景——在那之前先把默认参数调明白。你处理过最大的 CSV 是多少 G用的什么办法评论区交流一下。
返回列表