
polars 1.10.0+ 支持列表列与标量列的原生广播算术运算,可直接使用 `pl.col("lst") + pl.col("val")` 实现逐元素相加,无需 `map_elements` 或嵌套 `list.eval`,简洁高效且性能优异。
在 Polars 中对列表列(list[i64])执行“按行广播”式算术运算(例如将 val 列中每行的标量值加到对应 lst 列中每个列表元素上),曾长期受限于 list.eval 不支持跨列引用的限制——如 pl.col('lst').list.eval(pl.element() + pl.col('val')) 会报错 named columns are not allowed in list.eval。
好消息是:自 Polars v1.10.0 起,这一需求已原生支持!
现在只需一行表达式即可优雅、向量化地完成操作:
import polars as pl
df = pl.DataFrame({
'lst': [[0, 1], [9, 8]],
'val': [3, 4]
})
result = df.with_columns(
pl.col("lst") + pl.col("val")
)
print(result)输出:
shape: (2, 2) ┌───────────┬─────┐ │ lst ┆ val │ │ --- ┆ --- │ │ list[i64] ┆ i64 │ ╞═══════════╪═════╡ │ [3, 4] ┆ 3 │ │ [13, 12] ┆ 4 │ └───────────┴─────┘
✅ 原理说明:Polars 自动将标量列 val 按行广播(row-wise broadcast)至同行列的列表中每个元素,等效于对每个 (lst_i, val_i) 执行 [x + val_i for x in lst_i],全程零 Python 循环,纯 Rust 后端执行,性能远超 map_elements。
⚠️ 注意事项:
- 此特性仅适用于 Polars ≥ 1.10.0(发布于 2024 年 4 月)。请通过 polars.__version__ 确认版本,旧版本需升级。
- 支持所有基础算术运算符:+, -, *, /, //, %, **,行为一致。
- 若列表长度不一(如 [[1], [2, 3, 4]]),广播仍正常工作——每个列表独立与其对应标量运算。
- 不支持混合类型(如 list[str] + i64),类型需兼容(如 list[i64] + i64)。
? 备选方案(兼容旧版本或特殊场景):
若暂无法升级,可借助 list.to_struct() + struct 广播(要求列表长度一致或指定 n_field_strategy="max_width"):
# 仅当列表长度统一时推荐;否则需先填充/截断
df.with_columns(
(pl.col("lst").list.to_struct(n_field_strategy="max_width")
+ pl.struct(pl.col("val"))).alias("lst")
)但该方式返回 struct 类型,后续需 .struct.unnest() 才能还原为列表,不如原生广播直观。
? 总结:优先使用 pl.col("lst") + pl.col("val") —— 它是 Polars 官方推荐、高性能、声明式、且完全符合直觉的解决方案。升级到 1.10.0+ 后,告别 map_elements 的黑盒开销与类型隐患,让列表列算术真正“融入” Polars 的表达式引擎。










