
本文介绍如何在 Polars 中高效、安全地对 DataFrame 列中每个嵌套列表(如 List[bool])独立应用滑动窗口求和并取最大值,避免跨列表边界错误,核心是结合 list.eval() 与 rolling_sum()。
本文介绍如何在 polars 中高效、安全地对 dataframe 列中每个嵌套列表(如 `list[bool]`)独立应用滑动窗口求和并取最大值,避免跨列表边界错误,核心是结合 `list.eval()` 与 `rolling_sum()`。
在 Polars 中处理嵌套结构(如 List[bool] 或 List[i64])时,常见的误区是直接使用 explode() + 全局 rolling() —— 这会破坏原始列表的逻辑边界,导致窗口跨越不同列表元素,产生错误结果。正确的做法是在列表内部逐个执行窗口计算,这正是 list.eval() 的设计目的:它将表达式作用于每个子列表的元素级上下文,完全隔离各列表的计算范围。
以下是最简洁、高效且语义正确的实现:
import polars as pl
from polars import Boolean, List
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": List(Boolean)},
)
# 步骤1:对每个子列表内执行长度为3的滑动窗口求和(返回同长度列表,前2项为null)
rolling_sums = src.with_columns(
pl.col("c2").list.eval(pl.element().rolling_sum(3))
)
# 步骤2:进一步取每个子列表中滚动和的最大值(自动忽略null)
result = src.with_columns(
pl.col("c2").list.eval(pl.element().rolling_sum(3).max()).alias("c2_max_rolling_sum")
)
print(result)
输出:
shape: (4, 2) ┌─────┬─────────────────┐ │ c1 ┆ c2_max_rolling_sum │ │ --- ┆ --- │ │ str ┆ i64 │ ╞═════╪═════════════════╡ │ a ┆ null │ │ b ┆ 1 │ │ c ┆ 3 │ │ d ┆ 2 │ └─────┴─────────────────┘
✅ 关键优势说明:
- list.eval() 确保每个子列表独立运算,严格遵守原始列表边界,无跨列表污染;
- pl.element() 在 list.eval 内代表当前子列表的每个元素,rolling_sum(3) 对其进行局部窗口聚合;
- .max() 自动跳过 null(由窗口不足导致的前置缺失值),无需额外 drop_nulls() 或 fill_null();
- 整个链式操作纯惰性、零拷贝(底层优化),性能远超 explode → group_by → rolling → pivot 等复杂重构方案。
⚠️ 注意事项:
- rolling_sum(n) 要求子列表长度 ≥ n 才能产生非-null 结果;长度
- 若需统一填充默认值(如 0),可在 .max() 后追加 .fill_null(0);
- 支持任意 Expr 组合:例如改用 rolling_mean(3).round(2) 或配合 filter() 提取特定条件窗口。
总之,list.eval() 是 Polars 处理嵌套序列滑动窗口的首选原语——它语义清晰、边界安全、性能卓越,彻底规避了手动 explode-restore 的复杂性与风险。










