Polars 2.0 预发布版本
摘要
Polars 2.0 的预发布版本将流式处理引擎设为惰性求值查询的默认引擎,显著降低了内存使用并将性能提升最高可达5倍,同时引入了更严格的错误处理并为用户提供了迁移指南。
暂无内容
查看缓存全文
缓存时间: 2026/09/03 08:55
# Polars 2.0 预发布版本
来源:https://pola.rs/posts/announcing-polars-2/
今天我们发布了 Polars 2.0 的首个发行候选版本。正式的 2.0 版本将在接下来的几周内发布。我们并不打算将 Polars 2.0 做成一个功能大版本更新。事实上,我们希望对您来说这是一次平稳的升级。我们提升这个主要版本号的原因是,我们可以摆脱过去做出的那些目前阻碍我们的设计决策,并将默认设置更改为更合理的选项,以惠及更广泛的用户群体。最大的默认更改是,现在所有 `LazyFrame` 查询都将在流式引擎上运行。因此,普通 Polars 用户可以预期在内存使用率和性能方面获得巨大的提升。总体而言,我们预计流式引擎将轻松快上 5 倍 (https://pola.rs/posts/benchmarks/)。
为了帮助用户过渡到 2.0,我们发布了一份完整的迁移指南 (https://docs.pola.rs/releases/upgrade/2/)。本文将介绍其中一些重点内容。
## 流式引擎成为默认选项
这是 2.0 版本影响最大的变更。现在对 `LazyFrame` 调用 `collect` 默认将使用流式引擎,从而在大多数查询中为用户带来巨大的内存和性能改进。之所以需要一个主要版本升级,是因为流式引擎对于某些操作(`join`、`group_by`、`unpivot` 等)默认不保证行顺序。如果您在这些操作中需要可观察的行顺序,可以通过设置 `maintain_order=True` 来选择启用。
对于希望继续使用“内存中”引擎作为默认设置的用户,可以通过设置引擎亲和性来实现。
```python
lf = pl.LazyFrame({"k": [2, 1, 0], "v": ["a", "b", "c"]})
other = pl.LazyFrame({"k": [0, 1, 2], "r": ["x", "y", "z"]})
# 2.0 版本:engine="auto" 现在解析为流式引擎。
# 对于 joins, group_by, unpivot 等操作,不再保证行顺序。
(
lf
.join(other, on="k", how="left")
.collect()
)
# ┌─────┬─────┬─────┐
# │ k ┆ v ┆ r │ <- 顺序可能与 `lf` 的原始行顺序不匹配
# └─────┴─────┴─────┘
# 为此查询选择启用可观察的顺序:
(
lf
.join(other, on="k", how="left", maintain_order="left")
.collect()
)
# 或者在整个应用范围内保持旧的内存中引擎为默认:
pl.Config.set_engine_affinity("in-memory")
# ... 或者针对每个查询:
(
lf
.join(other, on="k", how="left")
.collect(engine="in-memory")
)
```
## 更严格的 Polars
Polars 的目标是严格并快速失败。错误理想情况下应该立即抛出,而不是在流程运行了 20 分钟后才出现。在数据不匹配时的隐式行为应该是可选的,而不是默认设置,因为这些不匹配可能隐藏缺陷。随着 AI 驱动开发的兴起,这种严格性变得更加重要。智能体可以通过调用 `collect_schema()` 提前验证查询的结构,这能在不物化任何数据的情况下解析类型并捕获模式级别的不匹配。这确保了智能体获得快速反馈,意味着它们可以更快地迭代。并非所有错误都能在查询计划编译阶段被捕捉到,有些错误依赖于数据。在这些情况下,Polars 默认采用更严格的行为以确保不一致性被捕捉,而不是默默产生不同的结果。
以下是 Polars 变得更严格的一些示例:
### `is_in` 无损类型强制
如果您在不同的数据类型上运行 `is_in` 表达式,Polars 过去会将两种类型强制转换为其公共超类型,即使这种转换是有损的。下面是一个由于隐式数据类型不匹配而可能导致错误的用户 ID 示例。
```python
# 检查一个用户 ID 是否在“已标记”的账户 ID 列表中
# (flagged_ids 从 JSON 导出中加载,其中大 ID 变成了浮点数)
flagged_ids = pl.Series([9007199254740992.0])
user_id = pl.Series([9007199254740993]) # Int64 -> 一个不同的 ID,相差 1
user_id.is_in(flagged_ids)
```
在 2.0 之前,`user_id` 会被强制转换为 `Float64` 以匹配 `flagged_ids`。但 9007199254740993 超过了 2^53 (9007199254740992),这是 float64 能精确表示的最大整数,所以它被默默向下舍入为 9007199254740992.0,导致假阳性结果。
在 2.0 中,这将引发:`InvalidOperationError: 'is_in' cannot check for Int64 values in List(Float64) data.`,用户应显式转换类型来处理有损的类型转换。
### 严格的连接操作
水平连接现在将检查长度,而不是默默用 `null` 填充。
```python
# 将每日交易计数与每日欺诈标记计数连接
transactions = pl.DataFrame({"day": [1, 2, 3, 4, 5], "count": [120, 98, 143, 87, 156]})
# 第 5 天的上游作业默默失败
fraud_flags = pl.DataFrame({"flagged": [2, 0, 5, 1]}) # 只有 4 行
pl.concat([transactions, fraud_flags], how="horizontal")
```
```
shape: (5, 2)
┌─────┬───────┬─────────┐
│ day ┆ count ┆ flagged │
│ 1 ┆ 120 ┆ 2 │
│ 2 ┆ 98 ┆ 0 │
│ 3 ┆ 143 ┆ 5 │
│ 4 ┆ 87 ┆ 1 │
│ 5 ┆ 156 ┆ null │ <- 第 5 天默默没有标记计数
└─────┴───────┴─────────┘
```
在 2.0 中,这将引发:
```
ShapeError: cannot concat dataframes with different heights in 'strict' mode
```
如果您需要填充,您必须使用 `how="horizontal_extend"` 显式选择启用。这向读者明确了该意图。
### 移除类型转换,转而使用专用方法/构造函数
另一个值得一提的变化是移除了许多模糊或应该通过其专用解析表达式应用的类型转换,从而为解析数据提供了一种明确的方式。
#### 枚举/分类类型 与 整数相互转换
```python
pl.Series([None, 1, 0, 2], dtype=pl.UInt32).cast(pl.Enum(["a", "b", "c"]))
# ComputeError: casting from u32 to enum is not supported.
```
请改用:`.cat.to(dtype)` 从整数到分类类型,`.cat.physical()` 从分类类型到整数。
#### 将字符串解析为时间数据类型
```python
pl.Series(["2022-08-30"]).cast(pl.Date)
# InvalidOperationError: casting from string to date is not supported.
```
请改用:`.str.to_date()` / `.str.to_datetime()`。这些方法允许您应用解析格式,从而更好地控制数据的解析方式。
这些只是几个例子,但我们实施了更多严格性改进。所有内容请参阅迁移指南。
## 提供详尽的错误信息
我们投入了大量精力,确保您(作为用户或您的智能体)在使用不再支持的旧参数时能够继续工作。为此我们新增了两个类型的异常:`polars.exceptions.AttributeRemovedError` 和 `polars.exceptions.ArgumentRemovedError`,分别处理已移除的属性和方法以及已移除的参数。
错误信息应该指引您找到新的 API。下面我们展示两个例子。
```python
>>> lf.melt(id_vars="a", value_vars="b")
polars.exceptions.AttributeRemovedError: `melt` was removed in version 2.0;
use `LazyFrame.unpivot` instead, with `index` instead of `id_vars`
and `on` instead of `value_vars`
```
```python
>>> df.join(df, on="a", join_nulls=True)
polars.exceptions.ArgumentRemovedError: the argument 'join_nulls' for
'DataFrame.join' was deprecated in version 1.24 and has been removed
in 2.0.0. It was renamed to 'nulls_equal' in version 2.0.
```
大多数被移除的功能已被弃用很长时间,如果您一直保持更新,希望它没有影响到您的流程。如果您认为我们应该保留您依赖的某些功能,请联系我们。
## 结语
Polars 2.0 关乎更好的默认设置(最重要的是流式引擎)和更好的 API。我们希望这次版本更新相当平稳。我们不会将新功能限制在主要版本更新之后发布,一旦功能就绪,我们就会将其发布。
不要误会,Polars 2.x 将比 1.x 好得多。有许多我们尚未充分公开讨论的功能正在开发中:流式引擎的正确磁盘溢出支持、新的 IO 插件设计、我们认为将是最快的 S3 读取器、重大的 SQL 覆盖范围改进、基于成本的规划器、联接重排序,以及移除 mmap,这将使我们的流程实现完全异步端到端处理。
通过安装 `pip install polars==2.0rc1` 来试用发行候选版。在此试用并联系我们:https://github.com/pola-rs/polars/issues 或在 Discord 上联系我们:https://discord.gg/4UfP5cfBE7。
相似文章
Making Postgres 300x faster for analytics: batching, operator fusion, and SIMD
pgrust 0.2 rebuilds the Postgres query engine with batching, operator fusion, and SIMD, achieving 300x faster analytical queries and 30% faster OLTP than Postgres.
Gemini 2.5 Pro 预览版:编码性能再创新高
Google 发布 Gemini 2.5 Pro 预览版(I/O 版本),编码能力显著提升,在 WebDev Arena 前端开发排行榜上排名第一,并支持视频转代码等高级功能。
使用 Rust 表达式插件扩展 Polars
本文解释了 fenic 为何以及如何使用 Rust 表达式插件来扩展 Polars,以在引擎原生执行文本操作(分块、提示模板化、模糊匹配等),从而避免 Python UDF 的性能和组合问题。
@Greptime: GreptimeDB 的扁平格式查询现在可以在任何列上预过滤——标签、字段、时间戳——而不仅仅是主键。而…
GreptimeDB 的扁平格式查询现在支持在任何列(标签、字段、时间戳)上预过滤,而不仅仅是主键,性能提升高达 4.5 倍。此外,mito2 存储引擎移除了其遗留扫描路径,清理了约 1800 行代码。
@robertnishihara:试试Ray 2.56!
Ray 2.56已发布,包含Ray Data的稳定性改进以及Ray Serve的重构,以提升LLM服务的性能。