
本文介绍如何在 Polars 中使用纯表达式(无 Python UDF)高效计算嵌套列表(list[list[i64]])中每个子列表的乘积,填补 list.product() 缺失的空白,并给出可复用的通用模式。
本文介绍如何在 polars 中使用纯表达式(无 python udf)高效计算嵌套列表(list[list[i64]])中每个子列表的乘积,填补 `list.product()` 缺失的空白,并给出可复用的通用模式。
在 Polars 中,pl.Expr.list.sum() 可直接对嵌套列表中的每个子列表求和,但截至 v1.19,尚无原生的 list.product() 方法。若尝试用 pl.reduce 在 list.eval() 内部做累积乘法,常因作用域混淆(如 pl.element() 未正确绑定到内层列表)而失效——正如问题中所示,pl.reduce(lambda e1, e2: e1 * e2, pl.element()) 实际未触发元素级计算,仅返回原始结构。
正确解法依赖双层 list.eval() 嵌套,精准分离“外层遍历子列表”与“内层计算子列表乘积”两个逻辑层级:
- 外层
pl.col("values").list.eval(...):遍历values列中每个顶层列表项(如[[2, 3], [5, 6]]); - 内层
pl.element().list.eval(pl.element().product()):对当前顶层项中的每个子列表(如[2, 3]和[5, 6]),调用pl.element().product()计算其乘积; - 最后通过
.list.first()将结果从list[list[i64]]提升为list[i64](因list.eval()默认保持外层 list 结构,而product()返回标量,需显式展平一层)。
完整代码如下:
import polars as pl
df = pl.DataFrame({"values": [[[1]], [[2, 3], [5, 6]]]})
result = df.with_columns(
product=pl.col("values")
.list.eval(
pl.element() # 当前顶层子列表,如 [[2, 3], [5, 6]]
.list.eval(
pl.element().product() # 对每个子列表 [2,3]→6, [5,6]→30
)
.list.first() # 将 [[6, 30]] → [6, 30]
)
)
print(result)
输出:
shape: (2, 2) ┌──────────────────┬───────────┐ │ values ┆ product │ │ --- ┆ --- │ │ list[list[i64]] ┆ list[i64] │ ╞══════════════════╪═══════════╡ │ [[1]] ┆ [1] │ │ [[2, 3], [5, 6]] ┆ [6, 30] │ └──────────────────┴───────────┘
⚠️ 关键注意事项:
- 不可省略内层
list.eval()——pl.element().product()仅对单个数值序列有效,必须先用list.eval()进入子列表上下文; -
.list.first()是必需的“结构降维”操作,否则结果类型为list[list[i64]](如[[6, 30]]),不符合预期; - 此模式可轻松扩展至其他聚合操作(如
mean,max),只需替换product()即可; - 若需处理空列表或含
null值的情况,建议前置pl.element().fill_null(1)或添加pl.element().filter(pl.element().is_not_null())确保鲁棒性。
该方案完全基于 Polars 原生表达式引擎,零 Python 循环、零 UDF 开销,兼具性能与可读性,是处理嵌套列表聚合任务的标准实践。










