
本文介绍如何在 pandas 中实现真正灵活的滚动窗口计算——无需显式循环、保留 dataframe 列名访问能力,并支持输出任意数量/名称的新列。核心方案结合 `numpy.sliding_window_view` 与轻量级临时 dataframe 构建,兼顾可读性、表达力与性能。
Pandas 原生的 .rolling().apply() 在处理复杂窗口逻辑时存在明显限制:启用 raw=True 后函数接收的是 numpy.ndarray,无法通过列名(如 window["A"])访问数据;而关闭 raw 时又强制要求返回值形状与输入列数一致,难以生成多于或少于原列数的新特征。
一个简洁、可靠且生产就绪的替代方案是使用 NumPy 的 sliding_window_view ——它能以零拷贝方式生成滑动窗口视图,再配合极轻量的 pd.DataFrame(..., copy=False) 构造临时子表。这样既保持了列名语义(df_tmp["A"].mean()),又完全解耦输入列数与输出结构。
以下为完整可运行示例:
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)
})
# 定义窗口大小(行数 × 列数),此处为 2 行 × 全部 3 列
window_rows, window_cols = 2, df.shape[1]
cols = df.columns.tolist()
# 预分配结果列表,首行为 NaN 占位(因窗口大小为 2,第 0 行无前置窗口)
results = [tuple([np.nan] * 4)] # 对应新列 D, E, F, G
# 遍历每个滑动窗口(形状为 (2, 3) 的二维数组)
for window_arr in sliding_window_view(df.values, window_shape=(window_rows, window_cols)):
# 构建无拷贝临时 DataFrame,保留原始列名
df_tmp = pd.DataFrame(window_arr[0], columns=cols, copy=False) # window_arr[0] 取首窗口块(2×3)
# ✅ 自由使用列名进行任意计算(支持向量化操作)
D = df_tmp["A"].sum() # 标量:A 列窗口和
E = (df_tmp["A"] + df_tmp["B"]).mean() # 标量:A+B 的窗口均值
F = (df_tmp["C"] - 1).prod() # 标量:C-1 的窗口乘积
G = (df_tmp["B"] * 2).sum() # 标量:B×2 的窗口和
results.append((D, E, F, G))
# 批量拼接新列
new_cols_df = pd.DataFrame(results, columns=["D", "E", "F", "G"])
df_result = pd.concat([df, new_cols_df], axis=1)
print(df_result)✅ 关键优势说明:
- 列名友好:df_tmp["A"] 直观可读,支持 .groupby, .agg, 自定义函数等全部 DataFrame 语法;
- 输出自由:返回任意长度元组 → 自动映射为指定列名,不受原始列数约束;
- 内存高效:sliding_window_view 返回视图(view),copy=False 确保临时 DataFrame 不复制底层数据;
- 可扩展性强:可轻松嵌入 def compute_features(df_slice): ... 封装复杂逻辑,便于单元测试与复用。
⚠️ 注意事项:
- sliding_window_view 要求 NumPy ≥ 1.20;旧版本可用 np.lib.stride_tricks.as_strided 手动实现,但需谨慎处理内存边界;
- 窗口对齐默认为右对齐(即窗口覆盖 [i-1, i] 得到第 i 行结果),若需左对齐或居中,可调整 results 初始化与索引偏移;
- 对超大规模数据(千万行+),可考虑分块处理或转向 Dask/Polars,但本方案在百万级数据上通常优于纯 Python 循环 10–50 倍。
该方法在保持代码清晰性的同时,突破了 .rolling().apply() 的固有瓶颈,是构建时间序列特征工程、滚动统计指标或模型滑动预测 pipeline 的推荐实践。










