pandas中用shift(k).rolling(n)可正确构造滞后滚动统计特征,先shift再rolling确保窗口仅含历史数据;需注意min_periods、索引对齐及NaN处理。

用 pandas.DataFrame.shift() 配合 rolling() 是最直接的路
别绕弯子,shift() 负责“错位”,rolling() 负责“窗口聚合”,两者嵌套就能生成滞后滚动统计特征。比如想构造“过去5天收盘价的均值,且该均值要滞后1天(即不包含当天)”,就得先 shift(1) 再 rolling(5),顺序不能反。
常见错误是把 rolling().mean() 直接套在原始列上,结果得到的是含当天的窗口,不符合“滞后”定义;或者先 rolling 后 shift,导致窗口计算本身被平移,语义混乱。
-
df['price'].shift(1).rolling(5).mean()→ 正确:先让数据整体后退1天,再对这组“已滞后”的序列做5期滚动均值 -
df['price'].rolling(5).mean().shift(1)→ 错误:先算出含当天的5日均值,再整体后移——第6行的值其实是原第2–6天的均值,不是你想要的“第1–5天均值” - 窗口大小
n和滞后步数k独立控制:shift(k).rolling(n)表示“用前n个历史点(不含当前)计算,且整个特征再往后推k步”
用 sklearn.preprocessing.FunctionTransformer 批量封装更省事
当你要同时生成多个滞后滚动特征(比如价格的3/5/10日均值滞后1天、成交量的7日标准差滞后2天),硬写多行 shift().rolling() 易出错、难维护。用 FunctionTransformer 把逻辑包装成可复用的转换器,能直接塞进 Pipeline,训练/预测时自动对齐。
注意:必须设 validate=False,否则它会强制检查输入是否二维数组,而 rolling 操作天然作用于 Series;另外函数内部要显式处理 NaN 填充逻辑,因为滚动窗口初期必然缺失值。
立即学习“Python免费学习笔记(深入)”;
- 示例函数体:
lambda x: x.shift(1).rolling(5, min_periods=3).mean().fillna(method='ffill') - 传入
FunctionTransformer时加参数:validate=False,否则报ValueError: Expected 2D array, got 1D array - 若用于时间序列交叉验证(如
TimeSeriesSplit),确保该 transformer 不会向未来泄漏——它只依赖历史,这点安全
警惕 rolling() 的 min_periods 和时序对齐陷阱
rolling(5) 默认要求窗口内至少5个非空值才输出结果,前4行全为 NaN。但实际业务中,可能希望只要凑够3个就计算(比如刚上市股票数据少),这时必须显式设 min_periods=3。更隐蔽的问题是:如果原始时间索引有跳空(比如跳过周末或停牌日),rolling(5) 仍按行数算,不是按自然日数——它数的是“最近5行”,而非“最近5个交易日”。
- 解决跳空问题:先用
asfreq('D')或reindex()补齐日期索引,再rolling('5D')(字符串窗口)——但注意这要求索引是DatetimeIndex且无重复 -
rolling(5, min_periods=1)会让第一行就出值(单点均值),但要注意这和后续窗口统计口径不一致,模型可能学偏 - 使用
closed='left'可显式指定窗口不包含右边界(即当前行),配合shift()更易理解,但非必需
性能敏感时,避免在循环里反复调用 rolling
如果你在一个 for 循环里,每轮都对同一列做不同窗口大小的 rolling().mean()(比如网格搜索 n=3,5,7,10),pandas 会重复扫描数据,O(N×k) 复杂度。换成 numpy.lib.stride_tricks.sliding_window_view(Python 3.9+)一次切好所有窗口,再用向量化操作,快一个数量级。
不过要注意:sliding_window_view 返回的是视图,内存友好,但它不处理 NaN,也不支持 min_periods,得自己写聚合逻辑;另外它不能直接替代带时间偏移的场景(比如先 shift 再滚动),得手动调整切片起始位置。
- 基础用法:
from numpy.lib.stride_tricks import sliding_window_view; windows = sliding_window_view(df['price'], window_shape=5) - 然后
np.mean(windows, axis=1)得到5日均值数组,长度比原数组少4,天然对应“滞后”效果(因最后窗口末尾对齐原数组倒数第5个元素) - 若需滞后k步,可对结果再
np.pad前补k个np.nan,或切片时用windows[:-k]控制对齐
真正麻烦的从来不是写一行 shift().rolling(),而是确认这个“滞后窗口”在业务时间线上是否真的指代你想表达的那个意思——比如“过去5个交易日均值,且不包含今天”,这句话在代码里要拆解成索引对齐、空值策略、窗口定义三个动作,漏掉任一环,特征就悄悄漂移了。


















