Parquet 批量读取性能分析报告(python)

Parquet 批量读取性能分析报告(python)

1. 测试背景

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.6
OSWindows 11 25H2
CPU(待补充)
RAM(待补充)

1.4 测试方法

  • 每种方案运行 3 次,取中位数
  • 使用 time.perf_counter() 高精度计时
  • 每次运行前执行 gc.collect() 减少 GC 干扰

2. 版本对比:pandas 2.3.2 vs 3.0.3

2.1 环境配置

组件环境 A环境 B
pandas2.3.23.0.3
pyarrow23.0.024.0.0
polars1.18.01.42.1
duckdb1.3.21.5.4

2.2 性能对比结果

方案pandas 2.3.2pandas 3.0.3提升倍数
pa.dataset -> to_pandas()4.25s0.73s5.8x
pa.read_table loop -> to_pandas()4.58s1.20s3.8x
pd.read_parquet loop + concat4.29s1.45s3.0x
strings_to_categorical2.35s1.83s1.3x
numeric cols only0.59s0.37s1.6x
ArrowDtype wrap only0.37s0.37s1.0x
ArrowDtype + full materialize6.86s6.15s1.1x
polars -> to_pandas()5.89s1.26s4.7x
polars Arrow wrap1.38s1.25s1.1x
duckdb -> df()10.62s12.51s0.85x

2.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(columns=NUMERIC_COLS)0.37s列裁剪最快
延迟按需访问to_pandas(types_mapper=pd.ArrowDtype)0.37s惰性包装
字符串基数低strings_to_categorical=True1.83s节省内存

4. ArrowDtype 惰性物化机制

4.1 原理

to_pandas(types_mapper=pd.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: 0x1a4b34d0080
  • Table 列 buffer: 0x1a4b34d0080
  • 地址相同 = 零拷贝确认

4.3 注意事项

  1. 转换开销未消失,只是推迟 — 如果最终需要全部列转 numpy,总成本不变
  2. 真正加速来自少转换 — 列裁剪 + ArrowDtype 组合
  3. pandas 操作可直接在 Arrow 后端运行 — groupby/filter/sort 无需转 numpy

5. 最佳实践

5.1 推荐代码(pandas 3.0.3+)

import pyarrow.dataset as ds
import pandas as pd

DATA_DIR = "path/to/parquet/files"

# 方案 A:全量读取(推荐,0.73s)
dataset = ds.dataset(DATA_DIR, format="parquet")
df = dataset.to_table().to_pandas()

# 方案 B:只读数值列(0.37s)
NUMERIC_COLS = ["TradingDay", "Open", "Close", "Settle", 
                "PrevClose", "PrevSettle", "Multiple", "TickPrice"]
df = dataset.to_table(columns=NUMERIC_COLS).to_pandas()

# 方案 C:惰性包装(0.37s,按需转换)
df = dataset.to_table().to_pandas(types_mapper=pd.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.3
polars 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 notes

7. 结论

7.1 最终推荐

优先级方案条件
1pandas 3.0.3 + pyarrow 24全量 to_pandas() 0.73s
2列裁剪只需数值列时 0.37s
3ArrowDtype 惰性按需访问场景

7.2 关键数字

指标pandas 2.3.2pandas 3.0.3
全量可用 DataFrame4.25s0.73s
只读数值列0.59s0.37s
ArrowDtype 包装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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值