
本文介绍如何在 pandas 中实现真正的滚动窗口(rolling window)自定义函数计算,突破 `rolling().apply()` 的限制(如强制 raw=true、输入输出列数绑定),通过 `numpy.sliding_window_view` 构建可按列名操作、灵活返回多列结果的高性能滚动处理流程。
在 Pandas 中,DataFrame.rolling().apply() 是最直观的滚动计算接口,但它存在两个关键限制:
- 当 raw=True 时,传入函数的是 numpy.ndarray,无法直接使用列名(如 window["A"])进行语义化操作;
- 当 raw=False 时,虽可获得 Series 或子 DataFrame,但返回值必须与原始列数严格一致,且仅支持标量或同长数组,无法自然生成新增列(如输入 3 列 → 输出 4 列)。
官方 API 目前没有内置方法直接支持“带列名的滚动 DataFrame 切片 + 任意结构输出”。但借助 NumPy 底层能力,我们可以构建一个既保持代码清晰性、又兼顾性能的替代方案——numpy.lib.stride_tricks.sliding_window_view。
该函数可在不复制内存的前提下,将二维数组(如 df.values)按指定窗口形状(如 (2, 3) 表示 2 行 × 3 列)生成滑动视图,再逐个封装为轻量 pd.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)
})
# 定义滚动窗口大小(行数)和参与列
window_size = 2
cols = ["A", "B", "C"]
# 初始化结果列表:首行为 NaN(因窗口不足)
results = [tuple([np.nan] * 4)] # 假设输出 4 列:D, E, F, G
# 核心滚动处理:生成 (window_size, len(cols)) 形状的滑动块
for window_arr in sliding_window_view(df[cols].values, window_shape=(window_size, len(cols))):
# 将当前窗口数组转为临时 DataFrame(零拷贝)
window_df = pd.DataFrame(window_arr[0], columns=cols, copy=False)
# ✅ 现在可直接用列名操作!逻辑清晰、可读性强
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)✅ 优势总结:
- 列名友好:window_df["A"] 等写法完全兼容 Pandas 风格,便于调试与维护;
- 输出自由:返回任意长度元组,轻松扩展新列(不限于输入列数);
- 性能可控:sliding_window_view 为视图操作,copy=False 避免中间数据拷贝;
- 边界清晰:首行自动填充 NaN,符合滚动窗口语义(也可按需改为 min_periods 逻辑)。
⚠️ 注意事项:
- sliding_window_view 要求 NumPy ≥ 1.20;若版本较低,可用 np.lib.stride_tricks.as_strided 手动实现(需谨慎处理内存安全);
- 对超大规模数据(如百万行+),Python 层 for 循环可能成为瓶颈,此时建议将核心计算逻辑向量化(如用 np.sum(window_arr[:, 0], axis=0) 替代 window_df["A"].sum())或改用 Numba 加速;
- 若需时间序列对齐(如按日期索引滚动),请先确保 df 按时间排序,并在 sliding_window_view 前提取 .values,索引对齐由最终 concat 保证。
该方案在可读性、灵活性与性能之间取得了良好平衡,是目前 Pandas 生态中处理“复杂滚动计算 + 列名依赖 + 多输出”场景的推荐实践。









