Polars中如何按列分组计算滚动EWM指数加权移动均值?
在Polars中实现分组滚动EWM均值的正确方式
你遇到的问题是因为错误地将ewm_mean嵌套在了rolling.agg中,导致每个滚动窗口内的EWM值被聚合为列表,而非每行对应一个累积的EWM均值。以下是两种符合预期的实现方式:
方法一:使用窗口函数over(推荐,与Pandas transform逻辑一致)
先确保数据按分组和时间戳排序(如果原始数据未排序),然后通过over指定分组,直接计算每个行对应的EWM均值:
# 先按symbol和ts排序(确保时间序列顺序正确) portfolios_sorted = portfolios.sort(["symbol", "ts"]) # 添加EWM均值列 result = portfolios_sorted.with_columns( pl.col("signal_0").ewm_mean(half_life=10).over("symbol").alias("signal_0_mean") )
方法二:分组聚合后展开
通过group_by分组聚合,同时保留时间戳和原始字段,最后展开得到每行结果:
portfolios_sorted = portfolios.sort(["symbol", "ts"]) result = portfolios_sorted.group_by("symbol", maintain_order=True).agg( pl.col("ts"), pl.col("signal_0"), pl.col("signal_0").ewm_mean(half_life=10).alias("signal_0_mean") ).explode(["ts", "signal_0", "signal_0_mean"])
错误代码的问题解析
你原来的代码使用rolling("ts", group_by="symbol", period="1d").agg(...),这会为每个时间点创建一个包含过去1天数据的滑动窗口,然后在窗口内对signal_0计算所有EWM值并聚合为列表。但EWM本身是累积式的指数加权平均,不需要滑动窗口,直接在分组内按时间顺序计算即可得到每行对应的实时均值,这与Pandas中groupby.transform(lambda x: x.ewm(...).mean())的逻辑完全匹配。
结果示例
执行上述正确代码后,会得到与Pandas一致的输出:
┌─────────────────────┬────────┬───────────┬───────────────────┐ │ ts ┆ symbol ┆ signal_0 ┆ signal_0_mean │ │ --- ┆ --- ┆ --- ┆ --- │ │ datetime[μs] ┆ str ┆ f64 ┆ f64 │ ╞═════════════════════╪════════╪═══════════╪═══════════════════╡ │ 2022-02-14 09:20:00 ┆ A ┆ -1.704301 ┆ -1.704301 │ │ 2022-02-14 09:50:00 ┆ A ┆ 1.040125 ┆ -0.664176 │ │ 2022-02-14 10:20:00 ┆ A ┆ 1.934686 ┆ 0.630255 │ │ 2022-02-14 10:50:00 ┆ A ┆ 2.073418 ┆ 1.351832 │ │ 2022-02-14 11:20:00 ┆ A ┆ 2.088835 ┆ 1.720334 │ │ 2022-02-14 09:20:00 ┆ AA ┆ -1.181743 ┆ -1.181743 │ │ 2022-02-14 09:50:00 ┆ AA ┆ 0.776798 ┆ -0.404945 │ │ 2022-02-14 10:20:00 ┆ AA ┆ 1.480892 ┆ 0.537973 │ │ 2022-02-14 10:50:00 ┆ AA ┆ 1.623698 ┆ 1.079836 │ │ 2022-02-14 11:20:00 ┆ AA ┆ 1.741544 ┆ 1.400690 │ └─────────────────────┴────────┴───────────┴───────────────────┘
内容的提问来源于stack exchange,提问作者OneRaynyDay
相关产品推荐
相关产品推荐

