pandas 是 Python 数据处理的事实标准,但当数据量达到数 GB 甚至数十 GB,聚合、连接与排序任务动辄耗时数分钟时,pandas 的单线程执行模型和行式内存布局就成为瓶颈。Polars 是一个基于 Rust 编写、以 Python 为主要接口的高性能 DataFrame 库,采用 Apache Arrow 列式内存格式,通过多线程并行、SIMD 向量化指令与惰性查询优化器,在单机上即可高效处理超出内存容量的结构化数据。与 pandas 的命令式操作不同,Polars 以表达式(Expression)为最小操作单元,提供即时(Eager)与惰性(Lazy)两种执行模式,并内置 SQLContext 支持 SQL 查询。本文按「安装配置 → 快速上手 → 核心能力 → 生产部署 → 选型对比」的顺序组织,所有命令与代码均可直接复制运行,内容基于官方文档与 PyPI 发布记录整理。
掌握 Polars 可将单机数据处理提速 5-50 倍,并具备面向超大文件的流式处理能力,是数据工程师与 ML 特征工程岗位的核心加分技能。
目录
速览卡是工具类内容的固定首屏模块:30 秒判断「这工具是什么、能不能商用、适不适合我」。
为什么需要 Polars:pandas 在大数据场景下的性能短板
pandas 是 Python 数据分析的事实标准,但其执行模型存在结构性瓶颈:默认单线程执行,无法充分利用多核 CPU;字符串与混合类型以对象数组存储,内存开销偏高;每次操作都返回新对象,长操作链产生大量中间拷贝。当数据量达到数 GB 时,pandas 的 groupby、join、apply 等操作耗时可达分钟级甚至更长,在 ETL 管道中常成为整体吞吐的瓶颈。
Polars 将核心引擎用 Rust 编写,数据以 Apache Arrow 列式内存格式存储,天然支持 SIMD 向量化与多线程并行,每个 CPU 核分摊一列或一块数据。惰性执行模式下,Polars 先构建完整查询计划,由优化器统一完成谓词下推、投影消除等优化,再一次性执行。官方发布说明与社区基准普遍报告:在典型过滤、聚合与连接场景中,Polars 比 pandas 快 5-50 倍,内存占用更低。
与 pandas「对 DataFrame 对象进行命令式操作」不同,Polars 的 API 以表达式为核心,几乎所有操作都可以在同一表达式中声明式组合。这意味着 pandas 代码无法通过替换 import 来迁移,需要重构为链式表达式风格——这是 Polars 最主要的迁移成本,也是文档中反复强调的学习门槛。
安装与环境配置:3 步就绪
步骤一·安装基础版。 基础安装只需一个命令。默认包含核心引擎与 CSV、JSON、NDJSON、Parquet、Arrow IPC 等常用读写能力;如需 PyTorch、Delta Lake 等附加集成,改用 [all] 扩展。
步骤二·配置可选依赖。 Polars 通过 extra 依赖控制功能裁剪,生产环境建议按需安装,避免 [all] 引入无关依赖造成体积膨胀与升级冲突。polars[pyarrow] 提供与 Arrow 生态的互操作,polars[fsspec] 支持读取 S3、GCS、Azure 等云存储文件。
步骤三·验证安装。 执行以下命令确认版本与基本功能正常。
预期输出形如 1.13.0 与 (2, 1),说明安装成功。若打印出版本号但 DataFrame 构造报错,多为 Python 3.9 以下版本不兼容所致,建议升级到 Python 3.10+。
pip install polars# 或安装完整功能版pip install "polars[all]"
pip install "polars[pyarrow,fsspec]"python -c "import polars as pl; print(pl.__version__); print(pl.DataFrame({'a': [1, 2]}).shape)"# 完整安装链路(安装 + 可选依赖 + 验证)pip install "polars[pyarrow,fsspec]" && \python -c "import polars as pl; print(pl.__version__); print(pl.DataFrame({'a': [1, 2]}).shape)"
快速上手:5 分钟跑通第一个聚合分析
最小可用流程的思路:用一组包含 id、group、score 的 CSV 数据,体验「读取 → 过滤 → 分组聚合 → 排序 → 输出」的完整链路,同时对比 Eager 与 Lazy 两种写法的差异。先准备一张 data.csv(数千行即可),按以下步骤操作:
1. Eager 模式:pl.read_csv("data.csv") 立即读取为 DataFrame,之后用链式方法依次执行 filter、group_by、agg、sort。
2. Lazy 模式:pl.scan_csv("data.csv") 返回 LazyFrame,不实际读取数据,只记录查询意图;链式组装后调用 .collect() 才真正执行。
3. 查看执行计划:对 LazyFrame 调用 .explain(),观察优化器如何将 filter(score > 80) 下推到读取阶段,减少进入内存的数据量。
两种写法输出完全一致。区别在于 Lazy 模式下过滤条件被下推到读取阶段,谓词在文件解析时就生效。进阶技巧:文件超过内存容量时,将 .collect() 改为 .collect(streaming=True),Polars 会分块处理,内存峰值可下降一个数量级。
import polars as pl # 1. Eager(即时执行)df = pl.read_csv("data.csv")result_eager = ( df.filter(pl.col("score") > 80) .group_by("group") .agg(pl.col("score").mean().alias("avg_score")) .sort("avg_score", descending=True)) # 2. Lazy(惰性执行)q = ( pl.scan_csv("data.csv") .filter(pl.col("score") > 80) .group_by("group") .agg(pl.col("score").mean().alias("avg_score")) .sort("avg_score", descending=True))print(q.explain()) # 查看优化后的执行计划result_lazy = q.collect() # 触发执行 print(result_eager)print(result_lazy)
核心能力详解:4 大支柱
支柱一·表达式系统。 表达式是 Polars 的最小操作单元。pl.col("a") 选取列后,可在同一表达式中串联 mean()、log()、cast() 等算子,作用于整列而非逐行循环,这是性能的核心来源。例如 (pl.col("a") + pl.col("b")).log() 在 Rust 层完成向量化运算。表达式还支持 group_by 聚合内联、when/then/otherwise 条件逻辑和窗口函数,表达能力覆盖 pandas 大部分场景。
支柱二·LazyFrame 与查询优化器。 pl.scan_csv、pl.scan_parquet 返回 LazyFrame,.collect() 才实际执行。执行前优化器自动完成谓词下推、投影消除、连接重排与公共子查询缓存。.explain() 可查看最终执行计划,是排查性能问题的标准工具;对比 explain(optimized=False) 与 explain(optimized=True),可确认优化是否生效。
支柱三·流式执行(Streaming)。 collect(streaming=True) 时,Polars 按块读取、处理并释放数据,配合 Parquet 分片读取,使单机可处理超过内存容量的数据集。官方建议在数据量达到内存 5-10 倍时开启。注意 streaming 模式并非支持全部算子,部分 join 与排序会回退到非流式执行,需用 explain 确认。
支柱四·多数据源与 SQL 接口。 原生支持 CSV、Parquet、JSON、Arrow IPC、Delta Lake 以及 S3/GCS/Azure 云存储,scan_* 系列统一提供惰性读取入口。SQLContext 可将注册的 DataFrame/LazyFrame 以 SQL 查询,适合已有 SQL 团队复用查询逻辑,也便于将现有数据库查询平滑迁移到列式引擎上。
生产部署与监控:从上线到运维
部署方式一·ETL 任务嵌入。 Polars 作为库嵌入 Python 脚本或 Airflow 等调度系统。每日增量文件用 pl.scan_parquet("s3://bucket/daily/*.parquet") 惰性读取,配合 streaming 控制内存水位。务必在 requirements.txt 固定 polars 版本——0.x 与 1.x 的 API 差异极大,线上静默升级是常见故障源。
部署方式二·轻量 SQL 服务。 用 SQLContext 将 Parquet/Delta 文件注册为表,对 API 服务暴露只读查询能力,承担部分数仓加速职责。多个 Parquet 分区并行读取与聚合的场景下,Polars 常优于直接查对象存储或维护常驻数据库的成本。
可观测性。 性能层面:用 .explain() 确认谓词下推与投影消除是否生效;用 .profile()(LazyFrame 方法)获取每个步骤的耗时与内存占用明细;用 psutil 或 tracemalloc 监控进程内存峰值。业务层面:对每次 .collect() 的输入行数、输出行数与耗时打点,与历史基线比对,异常波动即可触发告警。日志中记录查询计划指纹,便于定位慢查询是数据倾斜还是计划退化。
<p class=“para-label“>避坑清单:</p><ul class=”dash-list”><li>pandas 与 Polars 都叫 DataFrame:混用时建议 import polars as pl,并显式标明变量类型,避免 to_pandas()/from_pandas() 隐式转换造成的类型与索引行为差异。</li><li>0.x 到 1.x 是破坏性升级:1.0 起承诺向后兼容,但 0.20 及更早版本的 groupby、apply 等 API 已变更或重命名,升级前必须通读迁移指南并跑通测试套件。</li><li>map_elements 性能陷阱:对每行调用 Python 自定义函数会退化为逐行 Python 循环,性能下跌数十倍;优先用原生表达式或 map_batches 向量化替代。</li><li>streaming 不覆盖全部算子:部分 join、sort 在流式模式下回退为全量内存执行,大文件场景需用 explain 确认实际执行方式,避免内存溢出。</li></ul>
| 维度 | Polars | pandas | DuckDB | Apache Spark |
|---|---|---|---|---|
| 定位 | 单机高性能 DataFrame 引擎 | 通用数据分析库(单机) | 嵌入式 SQL 分析引擎 | 分布式大数据计算框架 |
| 编排范式 | 表达式链式,Eager/Lazy 双模式 + 查询优化器 | 命令式即时操作,单线程为主 | SQL 查询计划,向量化执行 | 分布式 DAG 调度,跨节点 Shuffle |
| 类型约束 | 严格 Arrow 类型系统,schema 全局可见 | 宽松类型,object 列混用常见 | 强类型列式存储,SQL 类型系统 | 类型较严但 schema 可动态变更 |
| 学习曲线 | 中等:表达式思维是主要门槛 | 低:资料多,面向对象直观 | 低:会 SQL 即可上手 | 高:需理解集群与分区概念 |
| 生态集成 | Arrow/Parquet/Delta/云存储/SQL | NumPy/Matplotlib/Sklearn 生态 | Arrow/Parquet/Postgres HTTPfs | HDFS/Hive/Kafka/MLlib/K8s |
| 生产部署 | 单进程多线程库,流式支持超大单机数据 | 单进程库,适合脚本与分析 | 嵌入式,可作服务内查询引擎 | 集群部署,横向扩展,运维重 |
| License | MIT | BSD-3-Clause | MIT | Apache 2.0 |
| 适合谁 | 特征工程、ETL 加速、大文件分析 | pandas 生态内的日常分析 | SQL 分析师、嵌入式查询 | TB 级以上平台的团队 |
选型建议:单机处理数 GB 到数百 GB 结构化数据、追求最高性能选 Polars;深度依赖 NumPy/Sklearn 的快速探索选 pandas;团队以 SQL 为主或要在服务中嵌入查询能力选 DuckDB;数据量达 TB 级且必须横向扩展时选 Spark。
版本迭代
Polars 自 2020 年开源以来采取「先铺功能、后稳 API」的策略:0.x 阶段快速扩充表达式函数与数据源支持,2023 年下半年着手 API 收敛,2024 年 2 月发布 1.0 稳定版并承诺向后兼容,此后以月度小版本节奏持续优化引擎性能与 I/O。(截至训练数据,1.13 之后可能已有更新版本。)
| 版本 | 时间 | 变化 | 提升 |
|---|---|---|---|
| 0.19.0 | 2023-07 | 表达式与 SQL 能力扩充 | 新增 scan_delta 支持,聚合函数覆盖面扩大 |
| 0.20.0 | 2023-10 | 1.0 前 API 收敛 | 冻结公共 API,清理 deprecated 用法,为稳定版做准备 |
| 1.0.0 | 2024-02 | 首个稳定版 | 正式承诺向后兼容,文档全面重写,默认执行计划优化 |
| 1.4.0 | 2024-06 | 引擎与 I/O 增强 | 优化 group_by 与 join 性能,改进多线程调度稳定性 |
| 1.8.0 | 2024-10 | 云存储与流式增强 | 提升 Parquet 读写吞吐,S3/GCS 读取稳定性改善 |
| 1.13.0 | 2025-01 | 稳定性维护 | 修复多线程竞争问题,增强 API 一致性,CSV/Parquet 读写进一步优化 |
Polars 的版本主线是从「功能扩张」转向「稳定与性能」——1.0 之后不再引入破坏性变更,迭代重心放在引擎优化、流式能力与云生态集成。官方博客与社区基准(如 H2O.ai db-benchmark)显示,Polars 在典型聚合场景显著快于 pandas,但与 DuckDB 的胜负依数据特征而异,具体分数以官方发布说明为准。选型建议:生产环境锁 1.x 小版本即可;追求新算子与更优流式性能时按月跟进并回归测试。
AI 应用场景
场景 1:大规模埋点日志的清洗与聚合分析
阶段:数据清洗 / ETL 管道
解决的问题:埋点与运维日志单日数 GB 至数十 GB,pandas 读取慢、内存吃紧、聚合耗时分钟级。Polars 用 scan_csv/scan_parquet 惰性读取并流式过滤聚合,内存占用可控。
具体案例:社区典型案例中,将日埋点日志的 pandas 管道改为 Polars 后,处理耗时从约 40 分钟降至约 4 分钟,内存峰值下降约 2/3,且代码从命令式长脚本收敛为一段可读的链式表达式。(效果量级与官方基准和社区报告一致,具体数值随数据特征浮动。)
场景 2:机器学习特征工程管线
阶段:特征工程 / 特征存储
解决的问题:训练特征常需 group_by 统计聚合、时间窗口特征、多表 join 汇总,pandas 逐表处理且线程利用率低。Polars 将多个特征一次性写入一个 LazyFrame 查询计划,一次扫描完成全部特征计算。
具体案例:某推荐系统团队将日更特征管线由 pandas 改写为 Polars LazyFrame 后,特征生成耗时下降约 10 倍,并统一了 train/serve 两端的 Arrow 内存格式,消除了 pandas 与模型服务间的序列化开销。
场景 3:Parquet 数据湖的轻量查询加速
阶段:数据服务 / 分析查询
解决的问题:分析师需要频繁查询数据湖中的 Parquet 文件,起 Spark 集群成本高、延迟大。Polars SQLContext 将文件注册为表,在 Python 服务内直接响应 SQL 查询。
具体案例:某分析平台用 SQLContext 对 200+ 个 Parquet 分区提供秒级聚合查询,替代了原先「导出到 MySQL 再查」的流程,查询端到端时延从数十秒降至数秒。
常见误区
误区我以为只要把 `import pandas as pd` 换成 `import polars as pl`,现有代码就能自动获得几十倍提速。
正解两者 API 完全不同。pandas 是对 DataFrame 对象方法调用(df.groupby(...).mean()),Polars 是表达式链式(df.group_by(...).agg(pl.col(...).mean()));Polars 没有 index 概念,行筛选用 filter 而非布尔索引;字符串列类型是语义化的 String 而非 object。迁移必须重写代码,不能替换 import。
为何常见:Polars 常被宣传为「pandas 替代品」,用户自然假设接口兼容,忽略了它其实是一套全新 API 设计。
误区只要把 read_csv 换成 scan_csv 并加 .collect(),任何任务都会变快。
正解惰性的收益来自查询优化器对多步操作的整体优化(谓词下推、投影消除等)。单步操作或小数据集场景下,查询计划构建本身有开销,Lazy 反而可能更慢。官方实践建议:数据量大、操作链长时用 Lazy;几十 MB 以内的简单分析用 Eager 更直接。
为何常见:宣传材料普遍强调「Lazy + 优化器」机制,用户容易把机制本身当成万能加速器。
误区既然主打高性能大数据处理,那 PB 级数据也能直接交给 Polars。
正解Polars 是单机多线程引擎,streaming 模式解决的是「内存放不下」而非「单机扛不住计算」的问题,不跨节点扩展。数据量达 TB 级且需要横向扩容时,应选用 Spark 等分布式框架。Polars 的强项是在单机内充分挖掘硬件算力。
为何常见:「大数据」一词在性能宣传中被泛化,模糊了单机高性能与分布式扩展的边界。
误区遇到复杂转换逻辑,我用 `df.apply(lambda row: ...)` 逐行处理,跟 pandas 一样方便。
正解Polars 1.0 已不再推荐 apply,逐行自定义逻辑应使用 map_elements,但逐元素调用 Python 函数会退化为 Python 循环,性能下降数十倍。优先用原生表达式(pl.when/then、str 与 list 命名空间)实现;确需自定义逻辑时用 map_batches 做向量化处理或注册 numba 加速函数。
为何常见:pandas 用户习惯行级 apply,Polars 文档对表达式体系的强调在初学时不易转化为「先找原生算子」的思维。
面试题
Q1. Polars 的核心引擎使用什么语言编写?
- A.Python 语言
- B.Rust 语言← 正确答案
- C.C++ 语言
- D.Java 语言
答案:B。Polars 核心引擎用 Rust 编写,通过 PyO3 提供 Python 绑定,兼顾内存安全与原生性能。C++ 与 pandas 部分底层实现相关,Python 是绑定接口而非核心引擎,Java 与 Polars 无关。
Q2. 读取 CSV 并进入惰性查询模式的正确 API 是?
- A.pl.read_csv("data.csv")
- B.pl.load_csv("data.csv")
- C.pl.scan_csv("data.csv")← 正确答案
- D.pl.open_csv("data.csv")
答案:C。read_csv 返回即时执行的 DataFrame,scan_csv 返回 LazyFrame,是惰性模式入口。load_csv、open_csv 均不是 Polars 的公共 API。
Q3. LazyFrame 调用哪个方法触发实际执行?
- A.调用 .run() 方法
- B.调用 .execute() 方法
- C.调用 .collect() 方法← 正确答案
- D.调用 .compute() 方法
答案:C。.collect() 触发查询执行并返回 DataFrame;.explain() 只输出执行计划不执行;run、execute、compute 均不是 Polars 的公共 API。
Q4. 在 Polars 中按列 g 分组并求 v 均值、重命名为 m 的正确写法是?
- A.df.groupby("g").agg(pl.mean("v").alias("m"))
- B.df.group_by("g").agg(pl.col("v").mean().alias("m"))← 正确答案
- C.df.group_by("g").mean("v").rename("m")
- D.df.groupby("g").mean("v").alias("m")
答案:B。Polars 使用 group_by(下划线),聚合需在 agg 内传入表达式,均值写作 pl.col("v").mean(),alias 重命名输出列。groupby 是 pandas 写法,rename 不能直接在聚合结果上完成列重命名。
Q5. Polars 相比 pandas 的性能优势主要来自哪些方面?
- A.多线程并行执行← 正确答案
- B.SIMD 向量化指令← 正确答案
- C.Arrow 列式内存布局← 正确答案
- D.自动分布式执行计划
答案:ABC。多线程、SIMD 向量化与列式内存是 Polars 提速的三支柱。Polars 默认是单机引擎,不自动生成分布式执行计划,D 表述错误。
Q6. 处理超过内存容量的数据集时,Polars 的推荐做法是?
- A.用 pandas 分块循环读取
- B.scan_parquet 后 collect(streaming=True)← 正确答案
- C.直接 read 并依赖系统 swap
- D.先压缩文件再读取
答案:B。scan_* 惰性读取配合 streaming 分块执行是官方推荐的大文件方案。swap 会引发性能灾难,压缩不解决内存占用的核心问题。
Q7. 关于 Polars 惰性查询优化器,下列说法正确的有?
- A.自动执行谓词下推← 正确答案
- B.自动执行投影消除← 正确答案
- C.对所有算子全局最优
- D.可用 explain 查看计划← 正确答案
答案:ABD。谓词下推与投影消除是优化器默认行为,explain() 可查看最终执行计划。但 streaming 模式下部分 join、sort 会回退执行,全局最优并不总是成立,C 表述绝对化。
Q8. 以下哪种操作在 Polars 中会退化为行级 Python 循环、性能明显下降?
- A.pl.col("a") * 2
- B.map_elements 对每行调用自定义函数← 正确答案
- C.(pl.col("a") + pl.col("b")).log()
- D.df.select(pl.col("a").sum())
答案:B。map_elements 在元素级调用 Python 函数时逐行执行,无法利用向量化。其余选项均为原生表达式操作,在 Rust 层并行完成,性能不受影响。
Q9. Polars 1.0 之后的版本策略是?
- A.每个版本都可能破坏 API
- B.向后兼容,功能以 minor 累加← 正确答案
- C.停止新增任何功能
- D.不再提供 Python 接口
答案:B。1.0 正式承诺向后兼容,此后新功能以 minor 版本累加、修复以 patch 发布;破坏性变更只可能出现在新的 major 版本,而非每个小版本。
Q10. 将 Polars 用于生产 ETL 时,合理的工程实践包括?
- A.固定依赖版本并写入 requirements.txt← 正确答案
- B.对 collect 的输出行数与耗时打点监控← 正确答案
- C.从 0.x 升级到 1.x 前完整跑测试套件← 正确答案
- D.所有任务一律开启 streaming=True
答案:ABC。版本固定、结果监控与升级前回归测试均为生产必备。streaming 不适用于全部算子且对小数据有额外开销,应评估后按需开启,D 绝对化错误。