Polars vs Pandas 在生产 Pipeline 中的对比

简介: Pandas作为Python数据处理基石,设计于单核时代;而Polars以Rust+Arrow重构架构,支持多线程惰性执行、列式存储与查询优化,10GB数据处理快94倍,是GB级ETL的现代之选。

大多数 Python 数据工程师最早学的是 pandas。因为它是行业标准,能用而且一直够用,所以一般也没人质疑过它。

Pandas 设计于 2008 年,面向的是那个时代的数据问题:假设每个操作都要立即返回结果,假设单个 CPU 核心足够,假设数据能放进内存。这些假设成立了很多年。随着 Pipeline 规模增长,它们越来越站不住脚。

这里说的并不是 pandas 好不好,因为它很好而且我现在也在用。现在的问题是问题在于它是否还匹配你实际在跑的工作负载。

对于超过约 1 GB 的文件型 ETL 来说,并不是pandas 出了问题,而是整个生态已经为这类工作产出了一个结构上更优越的工具——Polars。两者之间的差距不是量级上的小打小闹。

Polars 为何在结构上更快

谈基准测试数据之前,先搞清楚原理。下图并排展示了两种执行模型。

Pandas 的单线程即时执行(eager execution)vs. Polars 的多线程惰性执行(lazy execution)。

Pandas 的执行模型

Pandas 运行在 NumPy 之上,大多数操作是单线程的。Python 的全局解释器锁(GIL)阻止了并行执行,即便有多个线程可用。每个操作都立即执行,所以无论是否需要中间结果都会被创建并存入内存。对一个 5 GB 的 DataFrame 做五步变换,就会产生五份中间副本,内存占用迅速叠加。

Pandas 默认将字符串存储为 NumPy object 数组:每个值都是一个 Python 对象指针,每个唯一字符串大约占 67 字节,而列式存储只需 4 字节的额外开销。100 万个 18 字符的字符串,在 pandas object dtype 下约占 75 MB,同样的列在 Arrow 格式下约占 22 MB。

Polars 的执行模型

Polars 用 Rust 编写,完全运行在 GIL 之外。数据以 Apache Arrow 列式格式存储:每列是一块连续的类型化内存,对 CPU 缓存友好,也支持 SIMD 指令。调用

scan_parquet

时,不会读取任何数据。Polars 先构建逻辑执行计划,在触碰任何字节之前完成优化:谓词下推到扫描层、未使用的列在读取前裁剪、Join 顺序重排以提升效率。

执行采用 morsel 驱动模式。每个 CPU 线程取一块输入,用自己独立的本地状态处理,最后合并结果,计算过程中没有锁竞争,所有核心同时运行。

处理一个 10 GB Parquet 文件时,pandas 读取全部 10 GB,在单核上顺序处理,并在途中产生中间副本。同样的 Pipeline 在 Polars 中,只读取满足过滤和聚合所需的列和行组,在所有核心上并行处理,不会实体化任何不必要的中间结果。每次给 Pipeline 计时,这种差距都能感受到:pandas 已经在读取时,Polars 还在规划,但 Polars 先完成。

基准测试

下表汇总了不同规模和负载类型下的结果。

三个独立基准测试,分别对比了查询 Pipeline、生产 ETL 迁移和调度负载场景下 Polars 与 pandas 的表现。

PDS-H 基准测试

Polars 团队于 2025 年 5 月发布了更新的 PDS-H 测试结果,运行环境为 AWS c7a.24xlarge 实例(96 vCPU、192 GB RAM)。测试对 10 GB CSV 数据集运行全部 22 条 TPC-H 衍生查询。Polars 流式处理耗时 3.89 秒,pandas 2.2.3(启用 PyArrow dtype)耗时 365.71 秒——整条多步分析 Pipeline 下来,差距达到 94 倍。

在 Scale Factor 100(100 GB)的场景下,pandas 被直接排除在外。单线程执行加上缺乏查询优化器,导致在完成基准测试之前就触发了内存溢出(OOM)。Polars 流式处理耗时 23.94 秒。

这是厂商自己跑的基准测试,可以把它当作方向性参考。在普通硬件上的独立测试差距通常小一些,单个操作一般在 5 到 22 倍之间。那个标题级别的倍数是查询优化器和多线程在完整 Pipeline 中叠加后才出现的。

生产环境的实证

荷兰出行服务商 Check Technologies 在一个 Sprint 内将全部 100 多个 Airflow DAG 从 pandas 迁移到了 Polars。驱动力不是性能基准,而是最数据密集的 Pipeline 上反复出现的 OOM 错误。迁移耗时不到两周:最问题严重的 DAG 速度提升了 3.3 倍,几乎所有其他 DAG 提升约 2 倍,云基础设施成本降低了 25%。Check 的高级数据工程师 Paul Duvenage 表示,团队一旦切换到声明式表达式 API,迁移过程就非常顺畅。

DB Systel(德国铁路子公司)用 Polars 0.20 重写了一个列车调度重处理任务。该任务原本需要 96 分钟,Polars 版本只需 5.5 分钟——在真实生产负载上提升了 17.5 倍,而不是在合成基准上。

在小数据上,差距基本消失有时甚至会反转。Polars 的查询优化器存在规划开销,当数据集小于几百 MB 时,这个开销会超过计算节省。对于小 DataFrame 上的快速脚本pandas 写起来更快,跑起来也够快。

安装与运行

安装只需一条命令:

 # 创建项目并添加 Polars
 uv init polars-pipeline
 cd polars-pipeline
 uv add polars pyarrow      # 标准安装
 uv run pipeline.py

无需编译,无需系统依赖。Polars 以预编译 wheel 形式发布Rust 运行时已打包在内。

⚠️ 开始之前有一点值得注意:在 Apple Silicon(M 系列 Mac)上,标准 polars wheel 会触发 CPU 兼容性警告,并建议使用运行时兼容版本。

 # Apple Silicon:改用这个
 uv add "polars[rtcompat]" pyarrow

Linux 和 Intel Mac 上使用标准安装即可。在 M 系列硬件上,

polars[rtcompat]

可避免警告,并确保使用适合该处理器的正确 SIMD 指令。

并排对比:两个库实现相同操作

第一次看 Polars 代码时,语法感觉很陌生。表达式 API 需要一天时间适应,适应之后,发现它比 pandas 的等价写法更易读,而不是更难读。下面的代码执行完全相同的 ETL 变换:读取 Parquet 文件、过滤行、按类别聚合、排序结果。先是 pandas 版本,然后是 Polars LazyFrame 版本。

 # pandas 版本
importpandasaspd
importtime

start=time.perf_counter()

df=pd.read_parquet("sales.parquet")
result= (
    df[df["revenue"] >1000]
    .groupby("category")
    .agg(
        total_revenue=("revenue", "sum"),
        avg_price=("price", "mean"),
        order_count=("order_id", "count"),
    )
    .sort_values("total_revenue", ascending=False)
    .reset_index()
)

print(f"pandas: {time.perf_counter() -start:.3f}s")
 print(result)
 # Polars 版本——带谓词下推和投影下推的惰性执行
importpolarsaspl
importtime

start=time.perf_counter()

result= (
    pl.scan_parquet("sales.parquet")          # 此时不读取任何数据
    .filter(pl.col("revenue") >1000)         # 下推到扫描层
    .group_by("category")
    .agg(
        total_revenue=pl.col("revenue").sum(),
        avg_price=pl.col("price").mean(),
        order_count=pl.col("order_id").count(),
    )
    .sort("total_revenue", descending=True)
    .collect()                                 # 执行在这里发生
)

print(f"Polars: {time.perf_counter() -start:.3f}s")
 print(result)

结构上的关键差异在于

scan_parquet

对比

read_parquet

。Pandas 版本立即读取整个文件;Polars 版本构建执行计划,只读取满足过滤和聚合所需的行和列。对于一个 1 GB、20 列、实际只需要 3 列的文件,Polars 读取的字节数可能不到 pandas 的 20%。

流式处理超出内存容量的数据

当数据集超过可用内存时,在

collect

中加入

engine="streaming"

。Polars 以称为 morsel 的批次处理数据,自适应地溢出到磁盘,始终不将完整数据集保留在内存中。

 result= (
     pl.scan_parquet("large_dataset/*.parquet")
     .filter(pl.col("status") =="active")
     .group_by("region")
     .agg(pl.col("amount").sum())
     .sort("amount", descending=True)
     .collect(engine="streaming")              # 核外执行
 )

如需直接写入磁盘而完全不在内存中实体化,使用

sink_parquet

 (
     pl.scan_parquet("raw/*.parquet")
     .filter(pl.col("error_code").is_null())
     .sink_parquet("clean/output.parquet")     # 分批流式写入磁盘
 )

生产环境注意事项

在生产环境中显式设置线程数,以避免与其他进程争抢资源:

 importos
 os.environ["POLARS_MAX_THREADS"] ="8"       # 必须在导入 polars 之前设置
 importpolarsaspl                           # Polars 在导入时初始化线程池——
                                               # 导入后再设置此变量无效

pyproject.toml

中锁定 Polars 版本。1.x API 已稳定,但小版本更新会引入新特性,在边缘情况下可能改变行为。锁定版本,在与生产 OS 一致的容器中测试,有意识地升级。

Pandas 仍然占优的场景

Polars 是 1 GB 以上文件型 ETL 的更好选择,下图按数据规模和负载类型梳理了决策框架。

按数据规模和负载类型选择工具,并明确标注了机器学习生态系统的约束。

大多数机器学习库以 pandas DataFrame 作为原生输入格式。scikit-learn、statsmodels 以及很多绘图库,要么明确要求 pandas,要么默认如此。为了避免一次

.to_pandas()

调用而重写整个技术栈,这笔买卖不值得做。

在每一个需要给模型喂数据的 Pipeline 中,实践中的做法是混合方案:用 Polars 做繁重的计算工作,最后一步再转换:

 # 用 Polars 做重活
features= (
    pl.scan_parquet("events/*.parquet")
    .group_by("user_id")
    .agg([
        pl.col("session_duration").mean().alias("avg_session"),
        pl.col("purchase").sum().alias("total_purchases"),
        pl.col("event_date").max().alias("last_seen"),
    ])
    .collect(engine="streaming")
)

# 在边界处零拷贝转换
importsklearn
 X=features.to_pandas()                     # 基于 Arrow,几乎瞬间完成

转换几乎瞬间完成,因为 Polars 和新版 pandas 共享 Apache Arrow 内存格式——当两边都使用 Arrow 类型时,不会发生数据拷贝。

Pandas 3.0 带来了什么变化

2026 年 1 月发布的 pandas 3.0,将 PyArrow 支持的字符串设为字符串列的默认类型,并将写时复制(Copy-on-Write)设为唯一执行模式。这些改动使字符串密集型数据集的内存占用降低了最多 70%,并消除了一类静默的变异 bug。

这是真实的改进。它在 I/O 和内存方面实质性地缩小了差距。但多核执行的差距依然存在——pandas 的聚合操作在任何版本中仍然是单线程、即时执行的,也没有查询优化器。如果瓶颈是计算而非内存,3.0 版本并没有改变这一判断。

总结

有三个信号说明一条 Pipeline 已经到了该迁移的时候:

第一:在生产规模的数据上触发 OOM,或者已经为了绕过内存限制加入了分块逻辑。第二:本该几秒完成的任务跑了好几分钟,性能分析显示 pandas 的 groupby 或 join 操作占据了主要运行时间。第三:运行在一台多核机器上,执行期间那些 CPU 核心基本闲着。

迁移的时候可以选最慢的那条Pipeline或者最常 OOM 的那条。只用 Polars LazyFrame 重写计算密集的部分,在输出端保留 pandas 边界以衔接机器学习或绘图,然后在生产规模的数据上基准测试,再推上线。如果在超过 1 GB 的数据集上看不到至少 3 倍的提升瓶颈可能根本不在 DataFrame 库上。

Pandas 不是遗留技术,它是精准的专用工具;Polars 是另一种工作的精准专用工具。为每项工作选择合适的,不是迁移项目,是工程判断力。

https://avoid.overfit.cn/post/da28f678d72c42729ccef7af469c1731

作者:Nevenka Lukic

目录
相关文章
|
5天前
|
人工智能 定位技术 SEO
我学 GEO 第 15 天:终于知道AI GEO该如何做?
我是暴走的莉莉酱,边旅行边研究AI GEO的数字游民。专注普通人如何提升“AI可见度”——让AI在回答用户问题时准确识别、理解并推荐你。不讲玄学,只做可测、可调、可持续的GEO实践。
403 125
|
7天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
686 4
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
5天前
|
缓存 人工智能 运维
阿里云618百炼大模型Qwen3.7-Max功能、免费试用、订阅计费、配置接入详解
Qwen3.7-MAX是阿里云百炼平台推出的通义千问3.7系列旗舰大语言模型,专为智能体时代复杂任务打造,依托阿里云全域算力与自研技术,在逻辑推理、长文本处理、代码工程、长周期自主执行等领域达到行业顶尖水平。2026年618期间,该模型推出多重免费试用权益、按量计费5折、订阅套餐优惠等专属福利,覆盖个人开发者、团队与企业全场景需求,以下从核心功能、免费试用、订阅计费、配置接入四方面展开详细解析。
398 123
|
3天前
|
人工智能 自然语言处理 API
阿里云Token Plan团队版解析:功能、三档套餐与省钱订阅指南
阿里云百炼平台推出的Token Plan团队版,是面向企业与团队的AI大模型订阅服务,以Credits为统一计量单位,整合文本与图像生成模型,提供团队管理、数据安全、多工具兼容等核心能力,解决团队零散订阅AI服务的管理混乱、成本失控、数据安全等痛点。本文将从核心定位、套餐详情、计费规则、团队管理、工具兼容、便宜订阅技巧等方面,全面解析Token Plan团队版,帮助企业与团队高效、低成本地使用AI服务。
298 108
|
18天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
4天前
|
存储 人工智能 数据可视化
别再手动复制 Skill 了:多 Agent 时代的 Skill 管理方案
多 Agent 场景下 Skill 的统一管理与同步。
236 124
|
11天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
883 0
|
4天前
|
SQL 存储 运维
日志能不能改?SLS LogStore 原生支持更新和删除了
随着日志承载的业务语义越来越多,数据订正、回填、清理等需求变得越来越常见。SLS 现已为 LogStore 提供原生 update/delete 能力——支持按 RowID 精确修改,按查询条件批量操作,类似计费调账、标签刷新、反馈回填等场景都可以直接在 LogStore 内完成闭环。
203 124