Polars中链式调用.over()时rolling_mean与分步执行结果不一致,sum表现正常
Polars链式窗口调用中rolling_mean与sum的差异问题解答
这是Polars当前已知的窗口函数链式调用行为限制,并非Bug。下面来拆解原因和解决方法:
问题原因
差异的核心在于两类窗口操作的执行逻辑不同:
sum().over()属于聚合窗口函数,它的计算基于分组内的所有行,结果在分组内恒定,链式调用第二个.over()时,Polars能正确识别前一步的聚合结果作为后续计算的输入。rolling_mean().over()属于滑动窗口函数,它依赖分组内的行顺序和滑动窗口范围,结果逐行变化。当你在单个表达式中链式调用第二个.over()时,Polars的表达式优化器目前无法正确保留前一步滑动窗口的计算上下文,导致后续的rank().over()实际上是在原始列(而非滑动窗口的结果)上执行,最终返回null。
无临时列的解决方法
要保证正确的求值顺序,不需要引入临时列,可以使用.pipe()方法显式传递前一步的计算结果:
import polars as pl pldf = pl.DataFrame({'grp': ['a', 'a', 'b', 'b'], 'date':[1,2,1,2], 'val': [0.5] * 4}) # 使用pipe控制求值顺序 result = pldf.with_columns( pl.col('val') .rolling_mean(2) .over('grp') .pipe(lambda rolling_result: rolling_result.rank().over('date')) .alias('rank') ) print(result)
执行结果会和分步计算一致:
shape: (4, 4) ┌─────┬──────┬─────┬──────┐ │ grp ┆ date ┆ val ┆ rank │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ f64 ┆ f64 │ ╞═════╪══════╪═════╪══════╡ │ a ┆ 1 ┆ 0.5 ┆ 1.5 │ │ a ┆ 2 ┆ 0.5 ┆ 1.5 │ │ b ┆ 1 ┆ 0.5 ┆ 1.5 │ │ b ┆ 2 ┆ 0.5 ┆ 1.5 │ └─────┴──────┴─────┴──────┘
.pipe()的作用是把前一步的滑动窗口计算结果作为参数传给lambda函数,确保rank().over()是在正确的输入上执行,避免了上下文丢失的问题。
补充说明
这类窗口链式调用的限制是Polars开发团队已知的问题,后续版本可能会优化表达式优化器的逻辑,让滑动窗口的链式调用行为更符合直觉。在此之前,.pipe()是最简洁的无临时列解决方案。
内容的提问来源于stack exchange,提问作者noob_191
相关产品推荐
相关产品推荐

