Skip to content
王佳晨
返回博客

Polars 与 pandas 在 Tick 级数据上的对比

把一条期货 tick 级数据流水线从 pandas 重写为 Polars 之后,内存、查询代价和踩过的坑。

2026-04-107 分钟阅读
PythonData Engineering

我最近把一条期货 L1/L2 数据入库流水线从 pandas 重写成 Polars。数据集大概 200 GB 的 Parquet,覆盖四年限价单簿快照。下面记录真正影响显著的点和没那么重要的点。

Polars 明显胜出的地方

  • Lazy API 把 filter + group_by + join 合并成一次扫描,峰值内存从 32 GB 降到 6 GB 以内。
  • 基于 Arrow 列的向量化运算比 pandas 同样的 group_by 聚合快 5–15 倍。
  • Streaming 引擎可以处理超过内存的文件,无需手动分块。

提升有限的地方

对于需要 lookahead-aware 滚动窗口的逐行特征工程(例如对非对称窗口做因果滚动偏度),Polars 要求你以表达式的方式思考,最终代码长度和 pandas 版本差不多。这里赢在正确性,不在速度。

值得知道的坑

  • Polars 默认 join 在不同版本间并不保证稳定——如果顺序重要,join 之后务必按确定性 key 重新排序。
  • 处理不规则的 tick 时间时,pl.Expr.rolling 必须显式以时间而非行数指定窗口。
  • dtype 之间的转换可能悄无声息地实例化整列;多用 explain()。
import polars as pl

lf = pl.scan_parquet("ticks/*.parquet")
(
    lf.filter(pl.col("symbol").is_in(liquid_symbols))
      .group_by(["symbol", pl.col("ts").dt.truncate("1m")])
      .agg(
          pl.col("price").last().alias("close"),
          pl.col("price").std().alias("vol"),
      )
      .collect(streaming=True)
)

我什么时候仍然用 pandas

小规模的交互式探索、Jupyter 上的草稿,以及任何需要只能接收 DataFrame 的旧库(部分老的 statsmodels 路径)。实践中的正确答案是两者并用:Polars 负责重活入库,pandas 负责混乱的建模对话。