Parquet 批量读取性能分析报告(python) Parquet 批量读取性能分析报告python1. 测试背景1.1 测试目标评估多种方案批量读取 Parquet 文件并合并为 pandas DataFrame 的性能差异找到最优读取方案。1.2 数据规格项目数值文件数量103 个 parquet 文件磁盘占用~483 MB总行数22,242,728 行列数12 列数值列8 列 (TradingDay, Open, Close, Settle, PrevClose, PrevSettle, Multiple, TickPrice)字符串列4 列 (FuturesCode, ContractId, ExchangeContractId, Exchange)1.3 测试环境组件版本Python3.11.6OSWindows 11 25H2CPU(待补充)RAM(待补充)1.4 测试方法每种方案运行 3 次取中位数使用time.perf_counter()高精度计时每次运行前执行gc.collect()减少 GC 干扰2. 版本对比pandas 2.3.2 vs 3.0.32.1 环境配置组件环境 A环境 Bpandas2.3.23.0.3pyarrow23.0.024.0.0polars1.18.01.42.1duckdb1.3.21.5.42.2 性能对比结果方案pandas 2.3.2pandas 3.0.3提升倍数pa.dataset - to_pandas()4.25s0.73s5.8xpa.read_table loop - to_pandas()4.58s1.20s3.8xpd.read_parquet loop concat4.29s1.45s3.0xstrings_to_categorical2.35s1.83s1.3xnumeric cols only0.59s0.37s1.6xArrowDtype wrap only0.37s0.37s1.0xArrowDtype full materialize6.86s6.15s1.1xpolars - to_pandas()5.89s1.26s4.7xpolars Arrow wrap1.38s1.25s1.1xduckdb - df()10.62s12.51s0.85x2.3 关键发现pandas 3.0.3 pyarrow 24 的to_pandas()转换有巨大优化传统全量转换从4.25s 降至 0.73s提升5.8 倍字符串列转换效率大幅提升可能是优化了 Arrow string - Python string 的转换路径pyarrow 24 本身也有性能改进polars 读取也同步提升3. 方案分类与适用场景3.1 方案分类类别方案耗时 (pandas 3.0.3)输出类型特点惰性包装ArrowDtype wrap0.37sArrow-backed零拷贝按需转换列裁剪numeric cols only0.37snumpy-backed无字符串列最快全量转换pa.dataset - to_pandas()0.73snumpy-backed立即可用全量转换strings_to_categorical1.83scategory字符串转分类惰性物化ArrowDtype materialize6.15snumpy-backed等价于全量3.2 场景推荐场景推荐方案耗时说明全量数据分析pa.dataset - to_pandas()0.73s立即可用pandas 3.0.3 推荐只需要数值列to_table(columnsNUMERIC_COLS)0.37s列裁剪最快延迟按需访问to_pandas(types_mapperpd.ArrowDtype)0.37s惰性包装字符串基数低strings_to_categoricalTrue1.83s节省内存4. ArrowDtype 惰性物化机制4.1 原理to_pandas(types_mapperpd.ArrowDtype) ↓ 为每列创建 ArrowExtensionArray 包装 ↓ ArrowExtensionArray._pa_array 原始 Arrow ChunkedArray 的引用 ↓ 零拷贝DataFrame 直接引用 Arrow buffer无数据复制 ↓ 调用 .to_numpy() 时才触发真正的 Arrow → numpy 转换4.2 实测验证操作耗时说明to_table()0.42s读取 parquet 到 Arrow 内存to_pandas(ArrowDtype)0.002s仅包装零拷贝col.to_numpy() 首次0.08s此时才转换col.to_numpy() 再次0.08s每次调用都转换Buffer 地址验证DataFrame 列 buffer:0x1a4b34d0080Table 列 buffer:0x1a4b34d0080地址相同 零拷贝确认4.3 注意事项转换开销未消失只是推迟— 如果最终需要全部列转 numpy总成本不变真正加速来自少转换— 列裁剪 ArrowDtype 组合pandas 操作可直接在 Arrow 后端运行— groupby/filter/sort 无需转 numpy5. 最佳实践5.1 推荐代码pandas 3.0.3importpyarrow.datasetasdsimportpandasaspd DATA_DIRpath/to/parquet/files# 方案 A全量读取推荐0.73sdatasetds.dataset(DATA_DIR,formatparquet)dfdataset.to_table().to_pandas()# 方案 B只读数值列0.37sNUMERIC_COLS[TradingDay,Open,Close,Settle,PrevClose,PrevSettle,Multiple,TickPrice]dfdataset.to_table(columnsNUMERIC_COLS).to_pandas()# 方案 C惰性包装0.37s按需转换dfdataset.to_table().to_pandas(types_mapperpd.ArrowDtype)5.2 类型兼容性注意场景问题解决方案传入 sklearn/scipy不兼容 ArrowDtype用.to_numpy()转换np.isnan()对 string[pyarrow] 报错改用pd.isna()matplotlib 绘图部分兼容必要时.to_numpy()5.3 版本升级建议当前版本建议pandas 2.3.x升级到 3.0.3性能提升显著pyarrow 23.x升级到 24.0.0配合 pandas 3.0.3polars 1.18.x升级到最新版to_pandas() 优化明显6. 性能瓶颈分析6.1 瓶颈分布pandas 2.3.2全量 to_pandas() 4.25s ├── 数值列转换: ~0.3s (零拷贝快) └── 字符串列转换: ~3.9s (创建 8800 万 Python 对象慢) └── 占比: 92%6.2 pandas 3.0.3 优化点字符串转换从 3.9s 降至 ~0.4s可能采用了批量字符串分配或更高效的内存布局具体实现细节需查阅 pandas 3.0 release notes7. 结论7.1 最终推荐优先级方案条件1pandas 3.0.3 pyarrow 24全量 to_pandas() 0.73s2列裁剪只需数值列时 0.37s3ArrowDtype 惰性按需访问场景7.2 关键数字指标pandas 2.3.2pandas 3.0.3全量可用 DataFrame4.25s0.73s只读数值列0.59s0.37sArrowDtype 包装0.37s0.37s结论升级到 pandas 3.0.3 pyarrow 24 是最佳方案全量读取 2200 万行数据仅需0.73 秒。附录完整测试代码测试脚本位于bench_version_compare.py- 版本对比专用bench_read_parquet_v4.py- 全方案对比test_lazy_materialization.py- 惰性物化验证数据目录D:\quantinv_code\...\data\opt_daily_quote