Polars vs Pandas:2026 年你该用哪个 DataFrame 库?
Updated on
如果你在 Python 里做数据工作,几乎肯定在 Pandas 上“撞过墙”。你加载一个 2 GB 的 CSV 文件,然后看着机器卡到几乎不能动。对 5000 万行做一次 GroupBy 聚合,本以为几秒钟就能结束,结果却跑了几分钟。你试图把流水线并行化,才发现 Pandas 在根子上基本是单线程的。这个曾经把 Python 表格数据分析带入主流的库,从来就不是为现代团队每天要处理的数据规模而设计的。
这并不是小麻烦。慢的数据流水线会阻塞整支团队。数据科学家等着 notebook 跑完而不是迭代分析。工程师只能堆 workaround——把数据分块、为本该在笔记本电脑上完成的任务启动 Spark 集群,或者把 Python 重写成 SQL。代价体现在:生产力损失的小时数、洞察延迟,以及随着数据增长得更快的基础设施账单。
Polars 已经成为最强的替代方案。它从零开始用 Rust 构建,以 Apache Arrow 作为内存底座,常常能比 Pandas 快 5 到 30 倍,同时只用一小部分内存。它支持惰性求值、自动多线程执行,以及会重写你的代码以高效运行的查询优化器。但 Pandas 也没有停下——2.x 版本引入了 Arrow-backed dtypes 并带来显著性能提升;围绕 Pandas 的生态依然无人能及。
本文会在 2026 年对 Polars 和 Pandas 做一次直接、实用的对比:涵盖语法差异、性能基准、内存使用、生态兼容性,并给出清晰的选型建议。
Pandas 是什么?
Pandas 是 Python 的基础数据处理库。它由 Wes McKinney 于 2008 年发布,把 DataFrame 抽象引入 Python,并成为数据清洗、转换与分析的标准工具。截至 2026 年,Pandas 在 GitHub 上拥有超过 45,000 颗 star,几乎所有数据科学项目都会把它作为依赖安装。
Pandas 的关键特性:
- 即时求值(Eager evaluation):每个操作在调用时立刻执行
- NumPy 底层数组:传统上底层使用 NumPy 数组(自 2.0 起可选 Arrow backend)
- 单线程执行:默认只使用一个 CPU 核心
- 成熟 API:文档完善、教程海量,与 scikit-learn、matplotlib、seaborn 以及整个 PyData 生态深度集成
- 可变 DataFrames(Mutable):支持原地修改
Pandas 2.x 引入了可选的 PyArrow-backed dtypes,提升了以字符串为主的数据的内存效率,也让与其他 Arrow 系工具的互操作更顺畅。但其核心执行模型仍是单线程 + 即时求值。
Polars 是什么?
Polars 是一个用 Rust 编写的 DataFrame 库,由 Ritchie Vink 于 2020 年创建。它使用 Apache Arrow 作为内存中的列式格式,从而可以与其他 Arrow 兼容工具进行 zero-copy 的数据共享。Polars 从架构层面就为解决 Pandas 的性能瓶颈而生。
Polars 的关键特性:
- 惰性与即时求值:两种模式都支持;惰性模式可在执行前进行查询优化
- Apache Arrow 内存模型:列式存储,缓存利用率更高
- 自动多线程:无需用户干预即可把操作并行到所有可用 CPU 核心
- 查询优化器:重写并优化执行计划(predicate pushdown、projection pushdown、join reordering)
- 流式执行(Streaming execution):可以处理大于 RAM 的数据集
- 不可变 DataFrames(Immutable):所有操作返回新的 DataFrame;不做原地修改
- GPU 支持:可选的 NVIDIA GPU 加速(面向内存内工作负载)
Polars 提供 Python API 和原生 Rust API。其 Python API 对 Pandas 用户很友好,但采用方法链与表达式(expression)语法,使优化器可以更有效地工作。
Polars vs Pandas:完整对比表
| Feature | Pandas | Polars |
|---|---|---|
| Language | Python (C/Cython internals) | Rust (Python bindings via PyO3) |
| Memory Backend | NumPy (Arrow optional in 2.x) | Apache Arrow (native) |
| Execution Model | Eager only | Eager and Lazy |
| Multi-threading | Single-threaded | Automatic parallel execution |
| Query Optimizer | No | Yes (predicate pushdown, projection pushdown) |
| Streaming (out-of-core) | No (manual chunking required) | Yes (built-in streaming engine) |
| Memory Efficiency | Higher memory usage, copies on many operations | 30-60% less memory on typical workloads |
| CSV Read Speed | Baseline | 3-5x faster |
| GroupBy Speed | Baseline | 5-10x faster |
| Sort Speed | Baseline | 10-20x faster |
| Join Speed | Baseline | 3-8x faster |
| Index Support | Row index (central to API) | No index (uses columns for all operations) |
| Missing Values | NaN (float-based) and pd.NA | null (Arrow native, distinct from NaN) |
| String Handling | Object dtype (slow) or Arrow strings | Arrow strings (fast, memory-efficient) |
| GPU Support | No native support | NVIDIA GPU acceleration (optional) |
| Ecosystem Integration | Deep (scikit-learn, matplotlib, seaborn, etc.) | Growing (DuckDB, Arrow ecosystem, converters) |
| Learning Curve | Moderate (extensive resources) | Moderate (familiar concepts, new syntax) |
| Maturity | 17+ years, extremely stable | 5+ years, rapidly maturing |
| Package Size | Lightweight | Larger binary (includes Rust runtime) |
语法对比:并排代码示例
理解实际差异的最好方式,是把同一类操作在两套库里分别写出来。下面的示例覆盖了常见数据任务。
读取 CSV 文件
Pandas:
import pandas as pd
df = pd.read_csv("sales_data.csv")
print(df.head())Polars:
import polars as pl
df = pl.read_csv("sales_data.csv")
print(df.head())对于简单的 CSV 读取,语法几乎一致。Polars 更快,是因为它会并行读取列,并直接使用 Arrow 的内存格式。在 1 GB CSV 上,Polars 通常 2 秒内完成,而 Pandas 往往需要 8–10 秒。
读取 Parquet 文件
Pandas:
df = pd.read_parquet("sales_data.parquet")Polars(lazy —— 只读取需要的列):
df = pl.scan_parquet("sales_data.parquet")
# No data loaded yet -- just a query plan
result = df.select("product", "revenue", "date").collect()这正是 Polars 的强项。scan_parquet 会创建一个 lazy frame,只读取你实际使用的列与行。如果 Parquet 有 100 列而你只需要 3 列,Polars 会直接跳过其余 97 列;而 Pandas 会把 100 列全部读入内存。
行过滤(Filtering Rows)
Pandas:
# Filter rows where revenue > 1000 and region is "North"
filtered = df[(df["revenue"] > 1000) & (df["region"] == "North")]Polars:
# Filter rows where revenue > 1000 and region is "North"
filtered = df.filter(
(pl.col("revenue") > 1000) & (pl.col("region") == "North")
)Polars 使用 pl.col() 的表达式系统,而不是方括号索引。这不只是语法偏好——表达式使查询优化器能够把过滤下推到数据源,并行执行求值。
GroupBy 聚合
Pandas:
result = df.groupby("category").agg(
total_revenue=("revenue", "sum"),
avg_price=("price", "mean"),
order_count=("order_id", "count")
)Polars:
result = df.group_by("category").agg(
total_revenue=pl.col("revenue").sum(),
avg_price=pl.col("price").mean(),
order_count=pl.col("order_id").count()
)两者都支持命名聚合。Polars 的表达式语法更显式、可组合。例如你可以在聚合里轻松链式处理:pl.col("revenue").filter(pl.col("status") == "completed").sum()——在 Pandas 里通常需要更绕的写法。
连接两个 DataFrame(Join)
Pandas:
merged = pd.merge(
orders, customers,
left_on="customer_id",
right_on="id",
how="left"
)Polars:
merged = orders.join(
customers,
left_on="customer_id",
right_on="id",
how="left"
)Join 语法相似。Polars 更快的原因在于它能跨多线程并行 hash/probe,并且在 lazy 模式下还能为最优执行重排 join 顺序。
添加与变换列
Pandas:
df["profit_margin"] = (df["revenue"] - df["cost"]) / df["revenue"] * 100
df["year"] = pd.to_datetime(df["date"]).dt.year
df["category_upper"] = df["category"].str.upper()Polars:
df = df.with_columns(
profit_margin=((pl.col("revenue") - pl.col("cost")) / pl.col("revenue") * 100),
year=pl.col("date").cast(pl.Date).dt.year(),
category_upper=pl.col("category").str.to_uppercase()
)Polars 使用 with_columns() 在一次调用中添加或变换多列。上面三个变换会并行执行;而 Pandas 中每一行会顺序执行,并创建中间拷贝。
链式操作(完整流水线)
Pandas:
result = (
df[df["status"] == "completed"]
.groupby("product_category")
.agg(total_revenue=("revenue", "sum"))
.sort_values("total_revenue", ascending=False)
.head(10)
)Polars(lazy mode):
result = (
df.lazy()
.filter(pl.col("status") == "completed")
.group_by("product_category")
.agg(total_revenue=pl.col("revenue").sum())
.sort("total_revenue", descending=True)
.head(10)
.collect()
)Polars 的 lazy pipeline 会先构建执行计划并在运行前优化。优化器可能把 filter 下推到 scan 阶段、只投影需要的列,或为了效率重排操作。你只需要在开头调用 .lazy()、在末尾调用 .collect() 就能自动获得这些优化。
性能基准(Performance Benchmarks)
真实世界的 benchmark 经常显示 Polars 在多个维度上明显领先 Pandas。以下数字来自 2025 年公开 benchmark 与 Polars PDS-H benchmark suite。
CSV 加载(1 GB 文件,约 10M 行)
| Library | Time | Memory Used |
|---|---|---|
| Pandas | 8.2s | 1.4 GB |
| Polars | 1.6s | 0.18 GB |
Polars 读取 CSV 快 5 倍,内存使用约少 87%。原因在于:Polars 并行读取列,并以 Arrow 列式格式存储数据,比 Pandas 的行式 NumPy 数组(加上 Python object 开销)更紧凑。
GroupBy 聚合(10M 行,5 组)
| Library | Time |
|---|---|
| Pandas | 1.8s |
| Polars | 0.22s |
Polars 的 group-by 通常快 5–10 倍。核心原因是:在所有 CPU 核心上并行的基于 hash 的聚合;而 Pandas 在单线程上顺序处理每个 group。
排序(10M 行)
| Library | Time |
|---|---|
| Pandas | 3.4s |
| Polars | 0.29s |
排序往往是差距最大的场景——Polars 可快到 11 倍。排序是 Pandas 的主要瓶颈之一,因为它依赖单线程的 NumPy sort 实现。
Join(两个 DataFrame,10M 与 1M 行)
| Library | Time |
|---|---|
| Pandas | 2.1s |
| Polars | 0.35s |
Polars 的 join 通常快 3–8 倍,取决于 join 类型与 key 的基数。并行 hash join 对于分析型工作负载中常见的大规模 fact-dimension join 尤其有效。
性能要点总结
当数据量小于 100,000 行时,两者都几乎“瞬间完成”。性能差距通常从 100 万行左右开始显著,并随数据规模增加而扩大。如果你经常在单机上处理 1000 万行以上数据,Polars 仅靠减少等待时间就能带来明显的生产力提升。
内存使用:Polars 如何保持轻量
内存效率是 Polars 最突出的优势之一:
-
Apache Arrow 列式格式:每列以连续内存块存储,更 cache-friendly;相比 Pandas 的 block-manager 方式,也避免了字符串与混合类型的 Python object 开销。
-
惰性求值避免中间拷贝:在 Pandas 中,每个链式操作往往都会创建新拷贝。一个五步流水线可能分配出五份 DataFrame。Polars 的 lazy mode 会构建优化后的计划,尽量减少中间分配。
-
Projection pushdown:从 Parquet 读取或惰性扫描时,Polars 只加载查询真正用到的列;Pandas 会全部加载。
-
Predicate pushdown:过滤条件会下推到数据源。如果你把 Parquet 过滤到 10% 行,Polars 只从磁盘读取匹配的 row groups;Pandas 则先读入所有行再在内存中过滤。
-
Streaming execution:当数据大于可用 RAM 时,Polars 可以分批流式处理,而不需要把整个数据集放入内存。
实际效果是:在 16 GB 机器上会让 Pandas OOM 的流水线,Polars 可能用 4–6 GB 就能稳定跑完。
惰性求值:Polars 查询优化器
惰性求值是 Polars 与 Pandas 最本质的分水岭。当你对 Polars DataFrame 调用 .lazy()(或使用 scan_csv / scan_parquet)时,操作不会立刻执行。Polars 会构建一个逻辑计划——由操作组成的有向图——并在执行前进行优化。
优化器会自动做多种变换:
# This lazy pipeline gets automatically optimized
result = (
pl.scan_parquet("huge_dataset.parquet") # 100 columns, 500M rows
.filter(pl.col("country") == "US") # Optimizer pushes this to file scan
.select("name", "revenue", "country") # Optimizer projects only 3 columns
.group_by("name")
.agg(pl.col("revenue").sum())
.sort("revenue", descending=True)
.head(20)
.collect()
)优化器对这段 pipeline 会做什么:
- Projection pushdown:只读取 Parquet 中的 "name"、"revenue"、"country"(忽略另外 97 列)
- Predicate pushdown:在 Parquet 的 row-group 层应用
country == "US"过滤,跳过完全不含 US 记录的数据块 - Common subexpression elimination:当同一表达式多次出现时复用计算结果
- Join reordering:当有多个 join 串联时,选择最高效的顺序
你可以在执行前查看优化后的 plan:
plan = (
pl.scan_parquet("data.parquet")
.filter(pl.col("value") > 100)
.select("id", "value")
)
print(plan.explain(optimized=True))Pandas 没有等价机制。每个 Pandas 操作都是即时执行,任何优化都需要开发者手动完成。
生态与兼容性(Ecosystem and Compatibility)
Pandas 在生态上的优势
Pandas 经过 17 年积累,生态无可匹敌:
- scikit-learn:通常期望输入是 Pandas DataFrames。Polars 虽可转成 Pandas 再训练,但多一步就多一层摩擦。
- matplotlib and seaborn:可以直接接收 Pandas DataFrame/Series 做绘图;Polars 需要转换。
- statsmodels:基于 Pandas 与 NumPy 构建,没有原生 Polars 支持。
- Jupyter integration:Pandas DataFrame 在 notebook 里原生渲染;Polars 也渲染得不错,但部分 notebook 扩展默认假设是 Pandas。
- 文件格式支持:Pandas 支持 Excel、HDF5、SQL databases、clipboard、fixed-width text 等大量格式。Polars 支持 CSV、Parquet、JSON、IPC/Arrow、Avro、数据库,但不原生支持 Excel(需要转换)。
- Google Colab / 云端 notebook:多数云端数据科学环境默认预装并假设你使用 Pandas。
Polars 正在追赶的方向
Polars 的生态正在快速增长:
- DuckDB integration:DuckDB 可以通过 SQL 直接查询 Polars DataFrames 且无需拷贝数据,把 SQL 与表达式工作流结合起来。
- Streamlit:已加入原生 Polars 支持,可直接把
pl.DataFrame传给 Streamlit 的展示函数。 - Arrow ecosystem:任何支持 Apache Arrow 的工具(包括 Spark、DuckDB、DataFusion 等)都能与 Polars zero-copy 交换数据。
- 转换方法:
df.to_pandas()与pl.from_pandas()让在两者间切换非常直接。
使用 PyGWalker 做可视化探索
一个能打通 Polars 与 Pandas 体验差距的工具是 PyGWalker (opens in a new tab)。它是一个开源 Python 库,可以把任意 DataFrame 变成可交互、类似 Tableau 的可视化界面,并直接嵌入 Jupyter notebooks。PyGWalker 原生支持 Pandas 与 Polars DataFrames,因此无论你用哪个库做处理,都能进行可视化探索。
import pygwalker as pyg
# Works with Pandas
import pandas as pd
df_pandas = pd.read_csv("data.csv")
pyg.walk(df_pandas)
# Also works with Polars
import polars as pl
df_polars = pl.read_csv("data.csv")
pyg.walk(df_polars)这在 Polars 工作流里尤其有价值:你用 Polars 高速处理数据,然后需要快速、可视化地探索模式、异常值或分布,而不想写绘图代码。PyGWalker 提供了基于现有 DataFrame 的拖拽式图表构建能力。
学习曲线(Learning Curve)
从 Pandas 迁移到 Polars
如果你已经会 Pandas,学习 Polars 通常需要大约一到两周的实际使用才能熟练。核心概念——DataFrames、列、过滤、分组、连接——是相同的;变化主要在语法与心智模型:
需要内化的关键差异:
-
没有 index:Polars DataFrames 没有行索引。如果你在 Pandas 中大量依赖
.loc[]、.iloc[]或set_index(),需要调整思路。Polars 一切都通过filter()与列选择实现。 -
表达式 API:不再是
df["col"],而是pl.col("col")。表达式可组合且可被优化。 -
方法链优于赋值:Polars 鼓励用 method chaining 构建 pipeline,而不是一行行原地修改 DataFrame。
-
文件 scan 默认惰性:
scan_csv()与scan_parquet()返回 lazy frames,需要.collect()才会执行。 -
严格类型:Polars 对 dtype 更严格;不像 Pandas 的 object dtype 那样“混着也能放”。
从零开始
对完全新手而言,Polars 可能反而更好学:表达式 API 更一致(不会遇到 Pandas 各版本间 groupby/agg 模式变化带来的困惑)。没有 index 也减少了一类常见坑(对齐导致的意外行为、reset index、多级索引的复杂性)。
但 Pandas 的学习资源远多于 Polars:书籍、大学课程、Stack Overflow 答案与教程数量巨大。Polars 文档写得不错,但体量更小。
什么时候用 Pandas?
Pandas 适合这些场景:
- 数据能放进内存且少于 100 万行:Pandas 已足够快,生态支持也最完整。
- 需要深度 ML 生态集成:scikit-learn、statsmodels 以及许多 ML 库默认接受 Pandas DataFrames。
- 团队已经熟悉 Pandas:对小数据来说,培训成本可能超过性能收益。
- 经常处理 Excel:Pandas 的
read_excel()与to_excel()久经考验。 - 需要冷门 I/O 格式:HDF5、Stata、SAS、SPSS、fixed-width text 等——Pandas 支持而 Polars 不一定支持。
- 小数据的交互式 notebook 探索:快速、临时的分析任务中,Pandas 的熟悉度与生态集成使其更务实。
什么时候用 Polars?
Polars 适合这些场景:
- 数据经常超过 100 万行:性能差距开始真正“有感”,且随规模增长而扩大。
- 在构建数据流水线:惰性求值与查询优化无需手动调参就能让 pipeline 更快、更省资源。
- 内存是约束:Polars 内存占用显著更低,同样硬件可处理更大数据。
- 需要并行但不想引入复杂性:Polars 自动并行;不需要
multiprocessing、不需要dask、也不必改基础设施。 - 大量使用 Parquet:Polars 在 Parquet 上的 predicate/projection pushdown 是巨大的效率优势。
- 新项目且没有遗留 Pandas 代码:没有迁移成本,Polars API 更干净一致。
- 要把数据交给 Arrow 兼容的下游工具:DuckDB、Spark、DataFusion 等都能与 Polars zero-copy 交换数据。
两者能一起用吗?混合方案(Hybrid Approach)
很多团队会采用混合策略:用 Polars 做重度数据处理,再转成 Pandas 做可视化或 ML 训练。两者转换很轻量且速度快。
import polars as pl
import pandas as pd
# Process data with Polars (fast)
processed = (
pl.scan_parquet("large_dataset.parquet")
.filter(pl.col("year") >= 2024)
.group_by("category")
.agg(
pl.col("revenue").sum().alias("total_revenue"),
pl.col("orders").count().alias("order_count")
)
.sort("total_revenue", descending=True)
.collect()
)
# Convert to Pandas for ML or visualization (small result set)
pandas_df = processed.to_pandas()
# Use with scikit-learn
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(pandas_df[["order_count"]], pandas_df["total_revenue"])这种模式让你在数据整理阶段享受 Polars 的速度,在下游阶段继续使用 Pandas 的生态。当聚合后结果集较小时,转换开销几乎可以忽略。
使用 RunCell 做 AI 辅助数据分析
无论你选择 Polars 还是 Pandas,在 Jupyter notebooks 里做数据工作都可以用 AI 加速。RunCell (opens in a new tab) 是一个面向 Jupyter 的 AI agent,能帮助数据科学家编写、调试和优化数据分析代码。它同时理解 Pandas 与 Polars 的语法,并能针对你的任务推荐更高效的做法——包括指出哪些场景下 Polars pipeline 会明显优于 Pandas 等价写法。如果你经常在两者间切换,一个同时懂两套体系的 AI 编程助手能显著降低摩擦。
迁移指南:从 Pandas 到 Polars
如果你在考虑把现有 Pandas 代码迁移到 Polars,下面是常用操作的速查表:
| Pandas | Polars |
|---|---|
df["col"] | df.select("col") or pl.col("col") |
df[df["col"] > 5] | df.filter(pl.col("col") > 5) |
df.groupby("col").sum() | df.group_by("col").agg(pl.all().sum()) |
df.sort_values("col") | df.sort("col") |
df.merge(other, on="key") | df.join(other, on="key") |
df["new"] = df["a"] + df["b"] | df.with_columns((pl.col("a") + pl.col("b")).alias("new")) |
df.dropna() | df.drop_nulls() |
df.fillna(0) | df.fill_null(0) |
df.rename(columns={"a": "b"}) | df.rename({"a": "b"}) |
df.apply(func) | df.select(pl.col("col").map_elements(func)) |
pd.read_csv("file.csv") | pl.read_csv("file.csv") |
pd.read_parquet("file.parquet") | pl.scan_parquet("file.parquet").collect() |
df.to_csv("out.csv") | df.write_csv("out.csv") |
df.head() | df.head() |
df.describe() | df.describe() |
两个库的未来(The Future of Both Libraries)
Pandas 不会消失。其 2.x 版本持续改进性能,可选 Arrow backend 也在某些操作上缩小与 Polars 的差距。基于 Pandas 构建的大量工具生态,将保证它在未来多年依然重要。
Polars 的势头正在快速上升。它背后有专门的公司(Polars Inc.)、定期发布、社区贡献增长,并且在生产级数据工程流水线中的采用率不断提升。GPU 加速、更完善的 SQL 支持、更深度的生态集成也在路线图上。
趋势很明确:Python 数据生态正在向 Apache Arrow 这一通用内存格式靠拢,而两者都在向该标准收敛。这意味着 Polars、Pandas、DuckDB 以及其他工具之间的互操作只会越来越好。
FAQ
结论(Conclusion)
2026 年在 Polars 与 Pandas 之间做选择,并不是因为其中一个在所有场景都更好,而是要把工具匹配到任务上。
Pandas 仍然是小到中等规模数据、依赖 scikit-learn 的 ML 工作流、快速探索分析,以及团队熟悉度比极致性能更重要的项目的最佳选择。它的生态无可替代,Pandas 2.x 也在持续进步。
当性能成为关键因素时,Polars 更合适:大数据集、数据流水线、内存受限环境,以及希望利用惰性求值与自动并行的新项目。它的速度优势不是“略快一点”——往往是数量级差距。
对许多团队而言,最有效的方式是两者结合使用:在需要速度的环节用 Polars 处理数据,在生态要求的环节转回 Pandas,并使用像 PyGWalker (opens in a new tab) 这样同时兼容两种 DataFrame 的工具进行可视化探索。Python 数据生态正在向 Apache Arrow 收敛,使这种互操作每年都更容易。
无论你怎么选,Python 开发者如今拥有一个真正高性能的 Pandas 替代方案——而且无需离开 Python 生态——这本身就是数据分析领域的重要进步。