承接《Pandas apply / map / applymap》,本篇讲解 rolling 滑动窗口,包含移动平均、滑动求和、窗口统计、窗口参数、扩展窗口expanding、指数加权ewm、分组滚动,是时序指标、行情、订单滚动统计核心工具,保持系列教程统一风格。
前置导入与示例数据
import pandas as pd
import numpy as np
df = pd.DataFrame({
"date": pd.date_range(start="2025-01-01", periods=10, freq="D"),
"sales": [120, 90, 150, 130, 180, 160, 200, 170, 140, 190]
})
df = df.set_index("date")
print(df)
一、rolling 基础概念
滚动窗口(滑动窗口):设置窗口大小 window,沿着序列向前滑动,在窗口内做聚合计算。
窗口必须是有序序列,时序数据一般以时间为索引;顺序乱了结果完全错误。
# 语法:df["sales"].rolling(window=N)
roll = df["sales"].rolling(window=3)
# 窗口内求和
df["roll_sum_3"] = roll.sum()
# 窗口内平均值(移动平均MA3)
df["roll_mean_3"] = roll.mean()
window=3:每次取当前行 + 前面2行,一共3条数据。
前两行数据窗口不足,默认返回 NaN。
二、常用聚合函数
s = df["sales"].rolling(window=3)
df["roll_max"] = s.max() # 窗口最大值
df["roll_min"] = s.min() # 窗口最小值
df["roll_std"] = s.std() # 窗口标准差
df["roll_count"] = s.count()# 窗口内非空数量
df["roll_median"] = s.median() # 窗口中位数
三、核心参数详解
1. window
- 数字:行数窗口(固定条数)
- 时间字符串(如
5D、2H):时间窗口,索引必须是DatetimeIndex
# 时间窗口:最近5天的滚动统计
df["sales"].rolling(window="5D").sum()
2. min_periods
最小有效观测数,窗口内满足多少条数据才输出结果,默认等于window
# window=3,min_periods=1:只要>=1条就计算,不足3条不再是NaN
df["roll_mean_3p1"] = df["sales"].rolling(window=3, min_periods=1).mean()
3. center
窗口居中对齐,默认 center=False(窗口靠右,当前行是窗口最后一个)
center=True:当前行放在窗口中间
df["roll_center"] = df["sales"].rolling(window=3, center=True).mean()
居中窗口适合平滑绘图,不适合业务回溯统计(报表、回测一般不用center)
4. closed
窗口区间闭合方式,时序窗口高频参数
right(默认):包含右端,不包含左端left:包含左端,不包含右端both:两端都包含neither:两端都不包含
四、自定义窗口函数 apply
rolling 内置函数不够用时,可以传入自定义函数
# 窗口内极差(最大值-最小值)
def peak_range(x):
return x.max() - x.min()
df["roll_range"] = df["sales"].rolling(window=3).apply(peak_range)
⚠️ 自定义rolling.apply性能较差,大数据慎用。
五、expanding 扩展窗口(累计窗口)
expanding 是从第一条一直累积到当前行,窗口不断变大,又叫累计窗口
# 累计平均值
df["exp_mean"] = df["sales"].expanding().mean()
# 累计总和
df["exp_sum"] = df["sales"].expanding().sum()
区别 rolling:rolling是固定大小滑动;expanding持续扩大。
六、ewm 指数加权窗口(EWMA)
指数加权移动平均,越近的数据权重越高,适合行情、传感器平滑
# alpha=0.3 衰减系数
df["ewm_mean"] = df["sales"].ewm(alpha=0.3, adjust=False).mean()
七、分组滚动(groupby + rolling,业务高频)
多分组时序,每个分组独立滚动,比如每个商品、每个渠道单独算3日滚动销售额
df2 = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=12, freq="D"),
"cate": ["A","A","A","A","B","B","B","B","A","A","B","B"],
"sales": [120,90,150,130,180,160,200,170,140,190,110,135]
})
# 按品类分组,每组独立3日滚动平均
df2["roll_3_cate"] = df2.groupby("cate")["sales"].rolling(window=3, min_periods=1).mean().reset_index(drop=True)
print(df2)
重点:groupby后rolling返回多层索引,记得 reset_index(drop=True) 再接回原df。
八、滚动窗口 + shift 搭配(前N期指标)
# 3日滚动均值,整体向后偏移1行,代表上一周期的滚动均值
df["roll_mean_3_last"] = df["sales"].rolling(3).mean().shift(1)
九、高频坑清单
- 索引无序,rolling直接算出错误结果,时序数据先排序
df.sort_index() - 时间窗口window=”5D”,索引不是datetime会报错
- min_periods忘记设置,前面大量NaN
- groupby rolling 多层索引,直接赋值会报长度不匹配
- center=True 用于统计时容易理解错窗口含义
- rolling.apply 自定义函数速度慢,优先内置sum/mean/std
- 缺失值会被窗口自动忽略,配合min_periods控制输出
十、综合实战模板
def add_rolling_feature(df, col, window=3):
"""增加滚动指标:均值、求和、最大值"""
df = df.sort_index()
roll = df[col].rolling(window=window, min_periods=1)
df[f"{col}_roll_mean_{window}"] = roll.mean()
df[f"{col}_roll_sum_{window}"] = roll.sum()
df[f"{col}_roll_max_{window}"] = roll.max()
return df
df = add_rolling_feature(df, "sales", window=3)
print(df)
十一、窗口函数速查表
| API | 类型 | 特点 |
|---|---|---|
| rolling(window) | 滑动窗口 | 固定窗口大小,向前滑动 |
| expanding() | 扩展窗口 | 从第一条开始不断累积 |
| ewm(alpha) | 指数加权 | 近值权重更高,平滑时序 |
| groupby + rolling | 分组滚动 | 每个分组独立计算窗口 |
0 条笔记