
本文介绍如何在 Polars 中高效、安全地对 List 类型列的每个内部列表独立应用滑动窗口(如滚动求和),避免跨列表边界错误,并最终提取每组窗口的最大值。
本文介绍如何在 polars 中高效、安全地对 `list` 类型列的每个内部列表独立应用滑动窗口(如滚动求和),避免跨列表边界错误,并最终提取每组窗口的最大值。
在 Polars 中处理嵌套结构(如 List[bool] 或 List[i64])时,常见的误区是直接对展开(explode)后的扁平序列使用 rolling —— 这会破坏原始列表的逻辑边界,导致窗口跨越不同记录的内部数组,产生错误结果。正确的做法是保持结构层级不变,利用 .list.eval() 在每个内部列表上独立执行表达式计算。
.list.eval() 是 Polars 专为嵌套列表设计的核心 API:它将传入的表达式(pl.Expr)逐个应用于每个子列表,且完全隔离——即每个子列表的滚动操作互不影响,天然满足“窗口不跨列表”的约束。
以下为完整实现流程:
-
定义数据(含布尔型嵌套列表):
import polars as pl
src = pl.DataFrame( { "c1": ["a", "b", "c", "d"], "c2": [ [0, 0], [0, 1, 0, 0], [1, 0, 1, 1, 1], [1, 1, 0], ], }, schema_overrides={"c2": pl.List(pl.Boolean)}, )
2. **对每个内部列表执行长度为 3 的滚动求和,并取最大值**:
```python
result = src.with_columns(
c2_max_rolling_sum=pl.col("c2")
.list.eval(pl.element().rolling_sum(3).max())
)输出为:
shape: (4, 3) ┌─────┬───────────────────────┬───────────────────┐ │ c1 ┆ c2 ┆ c2_max_rolling_sum│ │ --- ┆ --- ┆ --- │ │ str ┆ list[bool] ┆ i64 │ ╞═════╪═══════════════════════╪═══════════════════╡ │ a ┆ [false, false] ┆ null │ │ b ┆ [false, true, … 0] ┆ 1 │ │ c ┆ [true, false, … 1] ┆ 3 │ │ d ┆ [true, true, false] ┆ 2 │ └─────┴───────────────────────┴───────────────────┘
⚠️ 注意事项:
- rolling_sum(3) 对长度不足 3 的前缀位置返回 null(如 [0,0] → [null, null]),因此 .max() 默认会跳过 null;若需控制空值行为,可加 .max(ignore_nulls=False) 显式指定。
- pl.element() 表示当前子列表中的每个元素,在 .list.eval() 上下文中自动绑定到该子列表的上下文,无需手动索引或分组。
- 此方法纯向量化、零 Python 循环,性能远优于 apply(lambda x: ...) 或多次 explode/roll/group_by 组合。
总结:当需对 List 列的每个内部数组独立进行滑动窗口计算时,*始终优先使用 .list.eval() + `pl.element().rolling_()` 组合**。它语义清晰、边界安全、性能优异,是 Polars 处理嵌套时间序列或局部聚合任务的标准范式。

















