PySpark含缺失周的时间序列滚动平均高效计算方案咨询
最优解决方案:用PySpark范围窗口函数避免补全缺失数据
你的痛点我完全理解——在大数据量下补全缺失周会显著增加数据体积,拖慢计算性能。其实我们可以不用显式补全任何数据,直接通过范围窗口函数来实现需求,性能会高效很多。
核心思路
因为缺失周的销量默认是0,我们只需要计算当前周及往前2周范围内的销量总和,再除以3即可得到3周滚动平均。这里的关键是用rangeBetween而非rowsBetween,因为我们要基于周编号的数值范围来确定窗口,而不是基于数据行的位置(毕竟缺失周没有对应的行)。
具体实现步骤
假设你的原始DataFrame名为df,包含wk_id(字符串类型,如201801)和sellout列:
将周编号转换为整数
先把wk_id转成整数类型,这样才能正确计算范围:from pyspark.sql import functions as F from pyspark.sql.window import Window df = df.withColumn("wk_id_int", F.col("wk_id").cast("int"))定义范围窗口
如果你的数据是按品类等维度分组的,记得加上partitionBy(比如partitionBy("product_id"))。窗口范围设置为当前周往前推2周(rangeBetween(-2, 0)):# 全局窗口(如果无分组维度) window_spec = Window.orderBy("wk_id_int").rangeBetween(-2, 0) # 带分组的窗口(如有品类等维度) # window_spec = Window.partitionBy("product_id").orderBy("wk_id_int").rangeBetween(-2, 0)计算3周滚动平均
对窗口内的sellout求和后除以3,并用round保留3位小数:result_df = df.withColumn( "moving_avg_3w", F.round(F.sum("sellout").over(window_spec) / 3, 3) )
验证结果
运行上面的代码后,得到的结果和你的预期完全一致:
| wk_id | sellout | moving_avg_3w |
|---|---|---|
| 201801 | 1.0 | 0.333 |
| 201802 | 5.0 | 2.000 |
| 201803 | 3.0 | 3.000 |
| 201805 | 1.0 | 1.333 |
| 201806 | 5.0 | 2.000 |
为什么这个方案更优?
- 无需补全数据:不会生成额外的缺失行,避免了大数据量下的数据膨胀问题。
- 性能高效:窗口函数是PySpark的优化算子,执行效率远高于补全后再计算滚动平均。
- 逻辑简洁:直接基于周编号的范围计算,逻辑清晰易懂,维护成本低。
注意事项
- 确保
wk_id是连续的周编号格式(如YYYYWW),这样rangeBetween(-2, 0)才能正确覆盖前2周到当前周的范围。如果你的周编号是其他格式(比如日期字符串),需要先转换为对应的周数再计算。 - 如果
wk_id原本就是整数类型,可以跳过第一步的类型转换。
内容的提问来源于stack exchange,提问作者pexmar
相关产品推荐
相关产品推荐

