
polars 1.10.0+ 支持列表列与标量列的直接算术广播(如 `lst + val`,无需 `map_elements` 或嵌套 `list.eval`,简洁高效地实现逐元素加法。
在 Polars 中,对列表列(list[i64])执行“按行广播”运算(例如将每行 val 的值加到对应 lst 中的每个元素上)曾长期受限于 list.eval 不支持跨列引用的限制。但自 Polars v1.10.0 起,原生引入了列表与标量/同长度列之间的自动广播算术能力,使该操作变得极其简洁。
✅ 推荐方案(v1.10.0+,最优雅)
直接使用 + 运算符即可完成广播加法,Polars 会自动将 val 列的每个标量值加到对应行 lst 列中所有元素上:
import polars as pl
df = pl.DataFrame({
'lst': [[0, 1], [9, 8]],
'val': [3, 4]
})
result = df.with_columns(
lst = pl.col("lst") + pl.col("val")
)
print(result)输出:
shape: (2, 2) ┌───────────┬─────┐ │ lst ┆ val │ │ --- ┆ --- │ │ list[i64] ┆ i64 │ ╞═══════════╪═════╡ │ [3, 4] ┆ 3 │ │ [13, 12] ┆ 4 │ └───────────┴─────┘
✅ 优势:零额外开销、类型安全、惰性求值兼容、完全向量化。
⚠️ 兼容性说明与替代思路(v1.9.x 及更早)
若你尚未升级至 v1.10.0+,以下方法可作为临时替代(但仍优于 map_elements):
方案 1:通过 struct 中转(支持变长列表)
利用 Polars 对 struct 列的广播支持,先将列表转为 struct,再相加(需指定字段策略以处理不等长列表):
result = df.with_columns(
lst = (pl.col("lst")
.list.to_struct(n_field_strategy="max_width") # 关键:适配不同长度
+ pl.struct(pl.col("val")))
.struct.unnest() # 可选:展开回列表(需后续转换)
)
# 注意:此时 lst 是 struct 类型;如需恢复 list,需额外 map 或 explode-implode(不推荐)方案 2:使用 list.eval + pl.int_range(仅限等长列表)
若所有列表长度一致,可通过索引间接取值(较繁琐,不推荐):
# ❌ 不通用,且性能较差,仅作技术参考
df.with_columns(
pl.col("lst").list.eval(
pl.element() + pl.col("val").take(pl.int_range(0, 1))
)
)? 注意事项
- 版本要求:pl.col("lst") + pl.col("val") 仅在 Polars ≥ 1.10.0 中可用,请通过 pl.__version__ 确认;
- 类型一致性:两列需兼容(如 list[i64] + i64 ✅,list[f64] + i64 会自动提升,但 list[str] + i64 ❌);
- 空列表/Null 安全:广播逻辑天然保留 null 行行为(如某行 lst 为 null,结果仍为 null);
- 性能提示:避免 map_elements —— 它强制 Python 层循环,丧失 Polars 的 Rust 后端加速优势。
✅ 总结
升级至 Polars 1.10.0+ 后,pl.col("lst") + pl.col("val") 是唯一推荐的、符合 Polars 设计哲学的解决方案:它语义清晰、性能最优、代码最简,真正实现了“所写即所想”的表达力。对于旧版本用户,建议优先升级,而非采用结构体迂回或低效映射方案。










