
本文介绍一种高效、无显式循环的方法,利用 pd.factorize 和 numpy.vstack 对 pandas dataframe 中每一行按其指定的窗口长度(来自另一列)动态计算 column a 的滚动求和。
本文介绍一种高效、无显式循环的方法,利用 pd.factorize 和 numpy.vstack 对 pandas dataframe 中每一行按其指定的窗口长度(来自另一列)动态计算 column a 的滚动求和。
在标准 Pandas 中,rolling(window=n) 要求窗口大小为固定整数或时间偏移量,无法直接支持“每行使用不同窗口长度”的需求(例如:某行 Column B = 3,则需对前 3 行(含自身)的 Column A 求和)。若采用 apply + iloc 循环实现,性能较差,尤其在大数据集上不可取。
幸运的是,可通过向量化技巧规避显式循环:核心思路是将所有可能的窗口长度预先计算出完整的滚动结果矩阵,再按需索引对应行。具体步骤如下:
- 提取唯一窗口长度并编码:使用 pd.factorize(df['B']) 获取 Column B 中各值的唯一标识(idx)及其对应的实际窗口大小数组(vals);
- 批量预计算所有窗口的滚动和:对 vals 中每个唯一窗口长度 v,调用 df['A'].rolling(v, min_periods=1).sum(),生成一个形状为 (len(df), len(vals)) 的结果列表;
- 堆叠并智能索引:用 np.vstack 将所有滚动结果垂直堆叠成二维数组,再通过 idx(行索引)和 np.arange(len(df))(列索引)进行高级索引,精准提取每行所需窗口的计算结果。
示例代码如下:
import pandas as pd
import numpy as np
# 构造示例数据
df = pd.DataFrame({
'A': [1, 2, 1, 3, 2],
'B': [1, 2, 3, 2, 4]
})
# 动态滚动求和(无循环)
idx, vals = pd.factorize(df['B'])
df['C'] = np.vstack([
df['A'].rolling(v, min_periods=1).sum()
for v in vals
])[idx, np.arange(len(df))]
print(df)
输出:
A B C 0 1 1 1.0 1 2 2 3.0 2 1 3 4.0 3 3 2 4.0 4 2 4 8.0
✅ 注意事项:
- min_periods=1 确保首行也能返回有效值(避免 NaN),可根据业务需要调整;
- 此方法依赖 Column B 中窗口长度为正整数且不超数据长度;若存在 0 或负数,需提前清洗;
- 内存开销与 len(df) × len(unique(B)) 成正比,当 B 取值过多时(如连续浮点数或大量离散值),建议先分桶或改用 numba 加速;
- 若需同时计算多个统计量(如均值、标准差),可扩展为 agg(['sum', 'mean']) 并配合 np.dstack 解包(见答案中的变体)。
该方案兼顾简洁性与性能,在中等规模数据(百万级以下)中表现优异,是 Pandas 动态窗口计算的经典实践之一。











