直接用pandas.read_csv读多个csv再写parquet会内存爆炸、速度慢、类型推断错误,因pandas全量加载且跨文件dtype不一致;正确做法是用pyarrow.csv.read_csv逐个流式读取,显式指定schema、use_threads、block_size及strings_to_nulls,并统一cast baseline_schema后写入parquet。

为什么直接用 pandas.read_csv() 读多个CSV再写Parquet会出问题
内存爆掉、速度慢、类型推断错——这是最常踩的坑。pandas 默认把所有 CSV 全读进内存再统一转,哪怕单个文件只有 10MB,100 个就是 1GB+;更麻烦的是,不同 CSV 的同名列可能被 pandas 推断成不同 dtype(比如有的列全空被当 float64,有的含字符串被当 object),导致后续合并或写 Parquet 失败,报错类似 ArrowInvalid: Cannot merge types string and double。
正确做法是逐个处理、显式指定 schema、流式写入:
- 用
pyarrow直接读 CSV(跳过 pandas 中间层),控制 chunk 大小和列类型 - 每个文件单独转成 Parquet,不拼接、不合并
- 写入时强制统一 schema(尤其 null 值多的列,显式设为
string或nullable int)
用 pyarrow.csv.read_csv() 替代 pandas.read_csv()
它更快、内存可控、支持类型预声明。关键不是“能不能读”,而是“怎么读才不会崩”:
-
read_options里设use_threads=True和block_size=1024*1024(1MB chunk)能显著提速 -
convert_options必须配strings_to_nulls=True,否则空字符串变""而非NULL,Parquet 里类型对不上 - 列类型不能靠猜:用
column_types={"user_id": pa.int64(), "name": pa.string()}显式声明,避免后期 type conflict
示例片段:
import pyarrow as pa
import pyarrow.csv as pc
import pyarrow.parquet as pq
<p>table = pc.read_csv(
"data.csv",
read_options=pc.ReadOptions(use_threads=True, block_size=1024*1024),
convert_options=pc.ConvertOptions(
strings_to_nulls=True,
column_types={"id": pa.int64(), "desc": pa.string()}
)
)
pq.write_table(table, "data.parquet", compression="snappy")</p>
批量处理时如何保证 schema 一致?
多个 CSV 结构稍有差异(比如新增列、列顺序不同、空值表示不同),直接并行转 Parquet 会导致下游查询失败。必须先“对齐 schema”:
- 抽一个代表性 CSV 文件,用
pyarrow.csv.read_csv(..., max_rows=1000)读少量行,生成基准 schema - 后续每个文件都用这个 schema 强制转换:
table.cast(baseline_schema, safe=False)(safe=False允许隐式类型转换,比如 string → int,但需确认业务可接受) - 遇到缺失列,用
pa.array([None] * len(table), type=baseline_schema.field("new_col").type)补空列
别依赖 infer_schema=True —— 它在不同文件上结果不稳定,是后续 Parquet 合并报错的根源。
写 Parquet 时哪些参数影响读取性能?
转完格式只是开始,读得快才是目的。这几个参数不调,Parquet 就白转了:
-
compression="snappy"是默认且推荐的:压缩率适中、解压极快,比gzip读取快 2–3 倍 -
use_dictionary=True(默认开启)对低基数字符串列很关键,能大幅减少体积和 I/O -
row_group_size=500000(而非默认的 1M)更适合 OLAP 查询:太大会增加内存压力,太小则元数据膨胀 - 绝对不要用
write_table(table, ..., use_legacy_dataset=False)的旧路径——新 dataset API 才支持 predicate pushdown 和 column pruning
最后提醒一句:如果原始 CSV 本身带 BOM 或编码混乱(比如 GBK 混 UTF-8),pyarrow.csv.read_csv() 会静默失败或乱码,务必提前用 chardet 检查并显式传 encoding 参数。这步漏掉,后面所有优化都白搭。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











