
本文介绍如何在 Polars 中为每个以 "Life" 开头、以 "Death" 结尾的连续数据段,提取指定列(如 Column A)的最大值,并仅将该值填充至对应 "Life" 行的新增列中,其余行置为 null。
本文介绍如何在 polars 中为每个以 "life" 开头、以 "death" 结尾的连续数据段,提取指定列(如 `column a`)的最大值,并仅将该值填充至对应 "life" 行的新增列中,其余行置为 `null`。
在 Polars 中实现“区间最大值映射”——即对由特定标记(如 "Life" 起始、"Death" 终止)界定的逻辑子段,分别计算某列的最大值并仅赋给起始行——不能直接使用常规窗口函数,而需通过构造分组标识(group ID)+ 分组聚合 + 条件赋值三步完成。其核心在于:将非连续的语义区间(Life → … → Death)转化为可被 .over() 操作识别的数值分组标签。
✅ 解决思路拆解
- 识别关键事件行:用布尔表达式标记 "Life" 和 "Death" 出现的位置;
- 构建累积分组号:利用 .cum_sum() 生成随每个 "Life" 或 "Death" 递增的序列,再通过 .forward_fill() 填充空值,形成初步分组;
- 修正分组边界:因 "Death" 行本身不属于当前 Life 区间,需将其 group_id 向前平移(.shift()),使整个 Life→Death 区间(含中间 null 行)共享同一 ID;
- 条件聚合与赋值:仅对 "Life" 行应用 .max().over(group_id),其余行保持 null。
? 完整代码实现
import polars as pl
df = pl.DataFrame({
"Column A": [2, 3, 1, 4, 1, 3, 3, 2, 1, 0],
"Column B": ["Life", None, None, None, "Death", None, "Life", None, None, "Death"]
})
# 步骤 1:定义事件布尔列
is_life = pl.col("Column B") == "Life"
is_death = pl.col("Column B") == "Death"
# 步骤 2 & 3:构造精准 group_id(自动对齐 Life-Death 区间)
group_id = (
(is_life.cum_sum() + is_death.cum_sum())
.forward_fill()
.fill_null(0) # 防止首行为 null 导致 fill 失败(可选)
)
group_id = pl.when(is_death).then(group_id.shift()).otherwise(group_id)
# 步骤 4:仅在 Life 行注入区间最大值,生成 Column C
result = df.with_columns(
pl.when(is_life)
.then(pl.col("Column A").max().over(group_id))
.alias("Column C")
)
print(result)输出结果:
shape: (10, 3) ┌──────────┬──────────┬──────────┐ │ Column A ┆ Column B ┆ Column C │ │ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ i64 │ ╞══════════╪══════════╪══════════╡ │ 2 ┆ Life ┆ 4 │ │ 3 ┆ null ┆ null │ │ 1 ┆ null ┆ null │ │ 4 ┆ null ┆ null │ │ 1 ┆ Death ┆ null │ │ 3 ┆ null ┆ null │ │ 3 ┆ Life ┆ 3 │ │ 2 ┆ null ┆ null │ │ 1 ┆ null ┆ null │ │ 0 ┆ Death ┆ null │ └──────────┴──────────┴──────────┘
⚠️ 注意事项与最佳实践
- forward_fill() 必不可少:原始 cum_sum() 在 null 行处会生成 null,必须填充才能参与后续算术运算;
- shift() 默认向前移位:.shift() 将当前行的 group_id 替换为上一行值,确保 "Death" 行不开启新区间;
- 避免整列 .max() 泄露:务必使用 .over(group_id) 实现分组内聚合,否则会计算全列最大值;
- 类型一致性:若 Column A 为浮点型,.max() 返回 f64;若需统一为 f64 输出(如示例目标),可显式 .cast(pl.Float64);
- 扩展性提示:该模式可轻松适配其他起止标记(如 "Start"/"End")、多列聚合(.agg([pl.col("A").max(), pl.col("B").first()])),或结合 .map_batches() 处理更复杂逻辑。
此方法完全向量化、无 Python 循环,符合 Polars 高性能设计哲学,适用于百万级数据的高效区间分析场景。










