Polars 0.19 升级指南:水平聚合拆分、all/any 语义变更与 groupby/apply 重命名迁移详解
本文基于 Polars 官方升级文档 0.19.md,系统讲解 Polars 0.19 版本的全部破坏性变更与弃用事项,并结合当前仓库源码逐条印证:聚合函数如何从“垂直+水平”双模式拆分为独立的 *_horizontal 函数族、all/any 对 null 的处理语义变化、错误类型从 ValueError 向 TypeError 的收敛、表达式输入解析逻辑的统一,以及 shuffle/sample 改用内部随机种子后的 set_random_seed 用法;同时覆盖 groupby 更名为 group_by、apply 全面并入 map_* 命名族的迁移方式,读完即可在 0.19 及之后的版本中完成平滑升级。
破坏性变更(Breaking changes)
聚合函数不再支持水平计算
这是 0.19 中最核心的行为变更。在此之前,sum、min、max 等聚合函数被“重载”设计:传入多列时执行水平(横向)计算,传入单列时执行垂直(纵向)计算。0.19 引入了专门负责水平计算的新函数族,并弃用旧的重载行为——聚合函数从此只保留垂直计算语义,水平计算必须显式使用 *_horizontal 变体(如 sum_horizontal)。
变更前(0.18 及以前,多列输入触发水平计算):
>>> df = pl.DataFrame({'a': [1, 2], 'b': [11, 12]})
>>> df.select(pl.sum('a', 'b')) # horizontal computation
shape: (2, 1)
┌─────┐
│ sum │
│ --- │
│ i64 │
╞═════╡
│ 12 │
│ 14 │
└─────┘
变更后(0.19,同一写法变为垂直计算,形状从 (2, 1) 变为 (1, 2)):
>>> df = pl.DataFrame({'a': [1, 2], 'b': [11, 12]})
>>> df.select(pl.sum('a', 'b')) # vertical computation
shape: (1, 2)
┌─────┬─────┐
│ a ┆ b │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═════╪═════╡
│ 3 ┆ 23 │
└─────┴─────┘
需要水平求和时,改用新函数即可恢复旧行为:
>>> df.select(pl.sum_horizontal('a', 'b'))
从源码结构看,水平聚合函数族集中定义在 horizontal.py 中,共提供 6 个函数:all_horizontal(第 20 行)、any_horizontal(第 65 行)、max_horizontal(第 110 行)、min_horizontal(第 145 行)、sum_horizontal(第 180 行)与 mean_horizontal(第 220 行)。其中值得注意的参数细节是:
sum_horizontal与mean_horizontal额外提供ignore_nulls: bool = True参数:默认忽略 null(第三行3 + null结果为3);设为False时,只要输入存在 null,该行输出即为 null;all_horizontal/any_horizontal采用 Kleene 逻辑(三值逻辑)处理 null:列中存在 null 且没有False(或True)时,输出为 null 而非布尔值;- 所有水平函数统一通过
parse_into_list_of_expressions解析输入:字符串按列名解析,其他非表达式输入按字面量解析,最终调用 Rust 侧的plr.*_horizontal内核函数。
而垂直聚合入口(如 pl.sum)则位于 vertical.py,两者在 aggregation/init.py 中合并导出,这就是“同一函数名只保留一种语义、水平计算走独立命名空间”这一 API 设计的代码体现。
all / any 语义更新
0.19 中 all 不再把 null 当作 False 处理,而是默认忽略 null 值。以 [True, None] 为例:
变更前:
>>> pl.Series([True, None]).all()
False
变更后:
>>> pl.Series([True, None]).all()
True
同时,any 与 all 的 drop_nulls 参数被重命名为 ignore_nulls,并改为**仅关键字(keyword-only)**参数;原参数设为 False 时在某些情况下错误地返回 None 的 bug 也一并修复。要恢复旧行为,需设置 ignore_nulls=False 并自行检查 None 输出:
>>> pl.Series([None, True]).all(ignore_nulls=False) # 启用 Kleene 逻辑,返回 None
当前仓库源码 series.py 中 Series.any(第 1750 行)与 Series.all(第 1792 行)均已签名化为 ignore_nulls: bool = True 的关键字参数,文档字符串明确提示“Enable Kleene logic by setting ignore_nulls=False”,与升级文档描述完全一致。
大量方法的错误类型改善
Polars 对错误消息的改进是持续性的工作。0.19 对 Python 代码库做了一次全面排查,重点变化是许多 ValueError 被修正为 TypeError,使异常类型与实际错误性质(如传入了不支持的类型)对应得更准确:
变更前:
>>> pl.Series(values=15)
...
ValueError: Series constructor called with unsupported type; got 'int'
变更后:
>>> pl.Series(values=15)
...
TypeError: Series constructor called with unsupported type 'int' for the `values` parameter
如果你的代码依赖 except ValueError 捕获 Polars 的构造/参数错误,升级后这类捕获会失效,应改为捕获 TypeError 或更宽泛的 Exception。
表达式输入解析逻辑更新
select、with_columns 等方法接受一个或多个表达式,同时也接受字符串、整数、列表等其他输入并尝试将其解释为表达式。0.19 统一了内部解析逻辑,使不同输入类型的解释更加一致。文档给出的例子恰好说明了一个“隐式行为消失”的变化——传入 None 不再被静默忽略,而是被解析为一个 null 字面量列:
变更前:
>>> pl.DataFrame({'a': [1, 2]}).with_columns(None)
shape: (2, 1)
┌─────┐
│ a │
│ --- │
│ i64 │
╞═════╡
│ 1 │
│ 2 │
└─────┘
变更后:
>>> pl.DataFrame({'a': [1, 2]}).with_columns(None)
shape: (2, 2)
┌─────┬─────────┐
│ a ┆ literal │
│ --- ┆ --- │
│ i64 ┆ null │
╞═════╪═════════╡
│ 1 ┆ null │
│ 2 ┆ null │
└─────┴─────────┘
这意味着过去“传入 None 悄悄不产生任何列”的用法现在会真正增加一列;若依赖该隐式行为,升级后应显式传入空表达式列表或不传参。
shuffle / sample 改用 Polars 内部随机种子
此前 shuffle、sample 等随机操作会受 Python 内置 random.seed 的影响,升级后该控制方式失效,取而代之的是新的全局函数 pl.set_random_seed:
变更前:
import random
random.seed(1)
变更后:
import polars as pl
pl.set_random_seed(1)
当前仓库中该函数实现于 random.py(第 9–22 行):接收一个小于 2⁶⁴ 的非负整数作为种子,透传至 Rust 内核 plr.set_random_seed,用于决定 shuffle 顺序等随机行为。由于种子作用在 Polars 内部而非 Python 的 random 模块上,依赖 random.seed 复现 Polars 随机结果的脚本必须整体改为 pl.set_random_seed。
弃用事项(Deprecations)
官方文档说明,0.19 带来了大量命名调整,升级时大概率会遇到 DeprecationWarning。如果想先升级、之后再逐步清理弃用警告,可在代码中临时添加:
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
groupby 更名为 group_by
文档明确指出这并非轻率的改动:“group by” 本是两个独立的词,而 Polars 的命名规范要求用下划线分隔,因此 groupby 统一更名为 group_by。官方建议直接用全局查找替换完成迁移:
- 查找:
.groupby( - 替换:
.group_by(
从当前仓库源码确认,frame.py 第 7075 行定义的正是 DataFrame.group_by,group_by.py 模块则承载了 GroupBy / LazyGroupBy 的实现,旧名仅作为弃用别名存在,语义完全不变。
apply 更名为 map_*
文档指出 apply 是 Polars API 中最容易被误用的部分——大量用户来自 pandas,而 pandas 中 apply 的含义完全不同。0.19 将所有用户自定义函数相关能力统一收敛到 map 命名族,具体对应关系如下(此表直接继承自升级文档,迁移时应逐条对照):
| Before | After |
|---|---|
Series/Expr.apply |
map_elements |
Series/Expr.rolling_apply |
rolling_map |
DataFrame.apply |
map_rows |
GroupBy.apply |
map_groups |
apply(GroupBy 模块级) |
map_groups |
map |
map_batches |
当前仓库源码与该对照表一一对应:
DataFrame.map_rows定义于 frame.py 第 8651 行;GroupBy.map_groups、LazyGroupBy.map_groups等定义于 group_by.py(第 378、1042、1253 行);- Rust 侧类型桩 _plr.pyi 中可见
map_elements(第 116 行)、map_rows(第 673 行)、map_batches(第 1155 行)、rolling_map(第 1843 行)、map_groups(第 2434 行)的完整签名,确认各旧名在新版本中均已映射到对应的map_*入口。
迁移要点小结
结合 0.19.md 与上述源码证据,升级到 0.19 时的行动清单为:
- 审计聚合调用:搜索传入多列的
pl.sum/pl.min/pl.max/pl.mean调用,将其中本意为“按行横向计算”的场景改写为sum_horizontal等函数(源码位于 horizontal.py); - 检查布尔断言:依赖
all遇 null 得False的代码需改为显式处理,或改用ignore_nulls=False保留旧语义; - 修正异常捕获:将针对构造/参数错误的
except ValueError调整为TypeError; - 排查隐式
None表达式:with_columns(None)等写法现在会产生字面量 null 列; - 替换随机种子:
random.seed全部替换为pl.set_random_seed(random.py); - 批量重命名:
.groupby(→.group_by(,apply/rolling_apply/map→ 上表中的map_*对应项; - 如需先升级再清理,可临时
warnings.filterwarnings("ignore", category=DeprecationWarning)屏蔽弃用警告。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00