如何在Pandas滚动计算中排除NaN,取最近10个有效值的均值
解决方案:基于最近10个有效值的滚动平均
你的核心需求是忽略NaN,取当前位置之前的最近10个有效值计算平均,原rolling(10)的问题在于它是按固定行数窗口计算,而非按有效值数量,所以无法满足需求。以下是两种可行方案:
方案一:高效向量化实现(适合大数据量)
利用merge_asof实现跨多行匹配最近的10个有效值,避免逐行循环的性能损耗:
import pandas as pd # 1. 提取所有有效值及其原始索引,并生成序号 valid_df = df[df['AST_Hit'].notna()].reset_index().rename(columns={'index': 'valid_index'}) valid_df['seq'] = valid_df.index + 1 # 为每个有效值分配递增序号 # 2. 计算每个有效值对应的前10个有效值的平均(shift(1)排除当前值) valid_df['rolling_10_mean'] = valid_df['AST_Hit'].rolling(window=10, min_periods=10).mean().shift(1) # 3. 将结果匹配回原DataFrame的所有行 all_indices = df.reset_index()[['index']].sort_values('index') result = pd.merge_asof( all_indices, valid_df[['valid_index', 'rolling_10_mean']].sort_values('valid_index'), left_on='index', right_on='valid_index', direction='backward' # 匹配当前索引之前最近的有效值结果 ) # 4. 将最终结果合并到原DataFrame df['rolling_10_valid_mean'] = result.set_index('index')['rolling_10_mean']
方案二:逐行自定义计算(适合小数据量,逻辑直观)
如果数据量不大,可直接通过apply实现逻辑,更易理解:
import pandas as pd import numpy as np # 提取所有有效值及其索引 valid_vals = df['AST_Hit'].dropna() def calc_last_10_mean(current_idx): # 筛选当前索引之前的所有有效值 prior_vals = valid_vals[valid_vals.index < current_idx] # 取最后10个,不足10个返回NaN if len(prior_vals) < 10: return np.nan return prior_vals.tail(10).mean() # 应用到每个行索引 df['rolling_10_valid_mean'] = df.index.map(calc_last_10_mean)
关键说明
- 原代码问题:
rolling(10)是固定行数窗口,哪怕窗口内有效值不足10个也会计算平均,且无法跨窗口去补够10个有效值;min_periods仅控制窗口内最少需要多少有效值才输出结果,无法改变窗口的行数限制。 - 不建议删除NaN行:删除会破坏原DataFrame的索引结构,丢失NaN行对应的其他列数据,对于大数据量来说也没必要做额外的数据清理。
内容的提问来源于stack exchange,提问作者Rain Maker
相关产品推荐
相关产品推荐

