如何在Pandas中计算带滚动掩码的滚动平均值(排除指定区间)
搞定带自定义掩码的滚动窗口计算需求
刚好碰到过类似的场景,咱们来一步步解决这个问题——核心就是要在滚动窗口里先过滤掉指定区间的数据,再做聚合计算,不管是简单均值还是复杂的非线性运算都能搞定。
最通用的方案:用rolling().apply()自定义过滤逻辑
这方法完全适配你说的非线性计算场景,思路很直接:给每个滚动窗口加个“过滤器”,先把要排除的数据筛掉,再执行你想要的计算。
针对你的需求,直接上代码:
import pandas as pd import numpy as np # 先生成你给的示例数据 idx = pd.date_range(start='2000', end='2001') df = pd.DataFrame(np.random.normal(size=(len(idx), 2)), index=idx) def masked_window_calc(window): """ 自定义滚动窗口计算函数:先排除指定区间,再算均值(可替换成任何计算) """ # 拿到当前窗口的最后一个日期(也就是你说的每个目标日期t) end_date = window.index[-1] # 定义要排除的日期范围:t往前10天±2天,也就是t-12到t-8 exclude_start = end_date - pd.Timedelta(days=12) exclude_end = end_date - pd.Timedelta(days=8) # 生成掩码:把排除区间内的数据标记为False,剩下的保留 mask = ~((window.index >= exclude_start) & (window.index <= exclude_end)) # 过滤后计算均值——这里换成ewm.mean()或者其他非线性计算都可以 filtered_data = window[mask] return filtered_data.mean() # 把自定义函数应用到60天滚动窗口上 df_avg = df.rolling(window=60).apply(masked_window_calc)
小细节说明:
- 如果你的排除规则是基于窗口内的位置(比如固定排除窗口里第48到52位,不管日期),那可以改成用位置掩码,效率更高:
def masked_window_calc(window): # 窗口长度是60,末尾是第59位(0索引),排除t-52到t-48对应位置是48到52 mask = np.ones(len(window), dtype=bool) mask[48:53] = False # 闭区间所以切片到53 filtered_data = window[mask] return filtered_data.mean() # 加raw=True,直接传NumPy数组,速度更快 df_avg = df.rolling(window=60).apply(masked_window_calc, raw=True) - 要是想换成指数加权平均,直接把
filtered_data.mean()改成filtered_data.ewm(alpha=0.1).mean().iloc[-1]就行,完全不用改整体逻辑。
大数据集优化:预生成掩码矩阵
如果你的数据量很大,apply()可能有点慢,这时候可以用预生成掩码矩阵的方法,用NumPy向量化操作提速:
window_size = 60 # 生成一个(len(df), window_size)的矩阵,全是True,代表默认保留所有数据 mask_matrix = np.ones((len(df), window_size), dtype=bool) # 把要排除的位置(第48到52列)改成False mask_matrix[:, 48:53] = False # 把DF转换成滚动窗口的数组,再用掩码过滤后求平均 rolling_array = df.rolling(window=window_size).apply(lambda x: x, raw=True).values.reshape(-1, window_size, 2) df_avg = pd.DataFrame( np.mean(rolling_array * mask_matrix[window_size-1:][:, :, np.newaxis], axis=1), index=df.index[window_size-1:] )
这个方法适合窗口长度固定的场景,速度比apply()快不少,因为用了NumPy的批量计算。
为啥不推荐“两个均值相减”?
你提到的那种方法,虽然在均值场景下能凑合用,但缺点很明显:
- 只能处理线性计算(比如均值、求和),碰到中位数、方差或者自定义非线性函数就歇菜了
- 规则稍微复杂一点(比如排除区间和窗口有重叠),很容易算错
- 代码可读性差,以后维护起来得花时间理清楚逻辑
所以还是用上面两种通用方法更靠谱,不管需求怎么变都能适配。
内容的提问来源于stack exchange,提问作者stan
相关产品推荐
相关产品推荐

