起因
pandas 是 Python 数据界 15 年的事实标准。但:
- 单线程(GIL),大数据慢
- 内存膨胀(同一列多份 copy)
- API 设计累赘(SettingWithCopyWarning、index 烦)
polars 是 Rust 写的 DataFrame,2020+ 起飞。Apache Arrow 内存格式 +
多线程 + lazy 执行。
2026 视角看,polars 在多个维度全面超越 pandas。
装
pip install polars
# 或者 uv add polars
句法对比
import polars as pl
import pandas as pd
# pandas
df = pd.read_csv('orders.csv')
result = (
df[df['country'] == 'US']
.groupby('product')
.agg({'amount': 'sum', 'qty': 'count'})
.reset_index()
.sort_values('amount', ascending=False)
.head(10)
)
# polars
df = pl.read_csv('orders.csv')
result = (
df.filter(pl.col('country') == 'US')
.group_by('product')
.agg([
pl.col('amount').sum(),
pl.col('qty').count(),
])
.sort('amount', descending=True)
.head(10)
)
polars 句法 method chain 顺。
明确的 pl.col(...) 比 pandas df['x'] 在复杂 expression 里清晰。
性能
我们一个 10 GB CSV / 80 列:
| 操作 | pandas | polars | polars-lazy |
|---|---|---|---|
| read_csv | 95s | 22s | 22s |
| filter + groupby + agg | 38s | 5s | 3s |
| join 两 10 GB | 90s (OOM 风险) | 18s | 12s |
| sort by 3 列 | 25s | 4s | 3s |
5-10x 快。32 核机器更明显(pandas 单核)。
内存:pandas 30 GB peak,polars 12 GB peak(Arrow columnar + zero-copy)。
lazy 执行
polars 杀手 feature:
# eager(每步实际跑)
df = pl.read_csv('big.csv')
result = df.filter(...).group_by(...).agg(...)
# lazy(构建 query plan,scan 时才执行)
result = (
pl.scan_csv('big.csv') # 注意 scan_ 而不是 read_
.filter(pl.col('x') > 0)
.group_by('y')
.agg(pl.col('z').sum())
.collect() # 触发执行
)
lazy 优势:
- predicate pushdown:filter 推到 CSV 读取阶段,只读符合行
- projection pushdown:只读用到的列
- CSE:重复 expression 算一次
- streaming:> 内存数据流式处理
result = (
pl.scan_csv('100GB.csv')
.filter(pl.col('date') > '2025-01-01')
.select(['user_id', 'amount']) # 只读这俩列
.group_by('user_id')
.agg(pl.col('amount').sum())
.collect(streaming=True) # 流式,不全加载
)
100 GB CSV 在 16 GB 机器跑得动。pandas 没 streaming 直接 OOM。
SQL interface
ctx = pl.SQLContext()
ctx.register('orders', df)
result = ctx.execute("""
SELECT country, SUM(amount)
FROM orders
WHERE qty > 5
GROUP BY country
""").collect()
熟 SQL 但不熟 polars expression → 写 SQL。
跟 pandas 互转
df_pd = pl.DataFrame(...).to_pandas()
df_pl = pl.from_pandas(df_pd)
零拷贝(用 Arrow buffer 共享)。混用方便。
与 pandas 2.x(Arrow backend)对比
pandas 2.x 加了 pyarrow backend:
df = pd.read_csv('data.csv', dtype_backend='pyarrow')
性能改善但仍单线程。
比 polars 还差一截(polars 多核 + lazy + native rust)。
与 spark / dask 对比
| pandas | polars | dask | spark | |
|---|---|---|---|---|
| 内存模型 | row | columnar (Arrow) | partition | columnar |
| 并行 | 单线程 | 多线程 | 多进程/集群 | 集群 |
| 数据规模 | < RAM | > RAM (streaming) | TB | PB |
| 学习曲线 | 低 | 中 | 中 | 高 |
| 启动 | 0.1s | 0.1s | 1s | 30s+ |
- < 10 GB → polars
- 10 GB - 1 TB → polars streaming / dask
-
1 TB → spark / dask 集群
实际项目迁移
我们 ETL pipeline 30 个 script,pandas → polars:
1. read_csv → scan_csv:1 行换
2. df[df.x > 5] → df.filter(pl.col('x') > 5):手动改
3. groupby().agg({}) → group_by().agg([]):手动改
4. .reset_index() → 删(polars 无 index 概念)
5. lambda apply → 改成 polars expression
大约 30 - 50% 行需要改。但跑速从 2 小时 → 12 分钟,值得。
LLM 辅助迁移很方便,pandas 到 polars 是 well-defined 转换。
API 缺点 / 注意
- 没 index(这是 feature 不是 bug,但 pandas 老用户要适应)
- merge → join(语义稍不同,pandas merge 默认 inner,polars join 默认 inner,OK)
- pivot / melt 等也有 + 语义略不同
- 没 multi-index column
90% workflow polars OK。某些特殊 transformation(时间序列 resample 加
multi-index)pandas 仍胜。
用什么场景
- 新 ETL → polars 默认
- 现有 pandas codebase → 看痛点决定,不必全迁
- notebook 探索性分析 → 二选一都行,polars 性能优势更大
- DataFrame for ML 输入 → sklearn 仍 pandas 友好;polars 转 numpy
传 sklearn
我的工作流
- 数据 ingestion / heavy ETL:polars
- ML feature engineering:polars
- 给 sklearn / pytorch 时:
.to_numpy()或.to_pandas() - 临时小数据:pandas(生态广)
踩过的坑
-
expression 错位:
pl.col('x') + 5 - pl.col('y')vs
pl.col('x') + (5 - pl.col('y'))。运算符优先级跟 Python 一致,
但容易看走眼。 -
lazy collect 慢:忘了
.collect()一直 lazy。debug 时
.head(10).collect()看数据。 -
datetime 时区:polars 严格 timezone aware / naive 区分。
pandas 经常混。从 pandas 来的 dataframepl.from_pandas时
timezone 信息可能丢。 -
null 处理:polars 用 Arrow null bit,跟 pandas NaN 不同。
pl.col('x').is_null()不是x != x。 -
groupby 后默认按 key 排序:pandas 默认排,polars 默认不排。
要 sort 显式.sort()。
登录后参与评论。