
本文介绍如何在 pandas 中对 dataframe 执行滚动窗口计算,突破 `rolling().apply()` 的限制(如无法按列名访问、强制输入输出列数一致),结合 numpy 的 `sliding_window_view` 构建可读性强、性能可控、完全支持 dataframe 语义的操作方案。
在 Pandas 中,DataFrame.rolling(window).apply(func) 是最常用的滚动计算接口,但其存在两个关键局限:一是当启用 raw=True 时,传入函数的是 numpy.ndarray,丢失列名信息,无法使用 df["A"] 等语义;二是 apply 要求返回值形状必须与原始 DataFrame 列数严格匹配(即每列输出一个标量),难以支持“多输入 → 少/多输出列”的灵活场景(例如基于 A/B/C 三列计算出 D/E/F/G 四个新特征)。
官方 rolling 接口目前不支持原生的、带列名上下文且输出维度可变的滚动函数应用。method="table" 和 engine="numba" 仅优化底层计算,无法解决语义缺失问题。因此,需借助更底层但可控的工具——NumPy 的 sliding_window_view。
该函数可在不复制内存的前提下,将二维数组切分为一系列滑动窗口视图(shape = (n_windows, window_height, window_width)),再逐窗口构造轻量级临时 DataFrame(通过 copy=False 避免数据冗余),从而在保留列名访问能力的同时,自由定义任意逻辑并返回任意长度的结果元组。
以下是一个生产就绪的完整示例:
import pandas as pd
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view
# 原始数据
df = pd.DataFrame({
"A": range(10),
"B": range(10, 20),
"C": range(20, 30)
})
cols = ["A", "B", "C"]
# 初始化结果列表(首行为 NaN 占位,对应窗口未满)
results = [tuple([np.nan] * 4)] # 假设输出 4 列:D, E, F, G
# 滑动窗口遍历(窗口大小为 2 行 × 3 列)
for window_arr in sliding_window_view(df.values, window_shape=(2, 3)):
# 构造无拷贝临时 DataFrame,复用原始列名
window_df = pd.DataFrame(window_arr[0], columns=cols, copy=False)
# ✅ 自由使用列名进行复杂计算(支持向量化、agg、自定义逻辑)
D_val = window_df["A"].sum() # 例:A列和
E_val = (window_df["A"] + window_df["B"]).mean() # 例:A+B均值
F_val = (window_df["C"] - 1).prod() # 例:C-1连乘
G_val = (window_df["B"] * 2).sum() # 例:B×2之和
results.append((D_val, E_val, F_val, G_val))
# 合并结果到原 DataFrame
result_df = pd.DataFrame(results, columns=["D", "E", "F", "G"])
df_final = pd.concat([df, result_df], axis=1)
print(df_final)⚠️ 注意事项: sliding_window_view 要求 NumPy ≥ 1.20;若版本较低,可用 np.lib.stride_tricks.as_strided 手动实现,但需谨慎处理内存安全。 copy=False 仅保证 DataFrame 构造时不复制底层数据,但后续若对 window_df 进行 inplace=True 修改,可能影响原始数据——实践中应将其视为只读视图。 窗口大小 (2, 3) 表示取连续 2 行、全部 3 列;若只需沿行方向滚动(忽略列维度),可简化为 sliding_window_view(df.values, window_shape=(2,)) 并 reshape 处理。 对于超大规模数据(千万级+),可考虑分块处理或改用 Dask/Polars;但对百万行以内,此方法性能通常优于纯 Python 循环,且远胜 rolling(...).apply(..., raw=False)(后者会为每个窗口创建完整 DataFrame 副本,开销极大)。
总结而言,当标准 rolling.apply 无法满足列语义与输出灵活性需求时,sliding_window_view + 临时 DataFrame 是一种简洁、高效、可维护的替代范式。它既规避了显式索引与嵌套循环的代码噪音,又保留了 Pandas 最擅长的列导向表达能力,是中高级数据分析流程中的实用利器。







