如何在反转的MultiIndex层级上应用pandas反向expanding窗口计算?
在MultiIndex DataFrame上实现反向Expanding计算
没问题,我来帮你搞定这个需求!要对每个Level-0索引(比如'A'、'B'、'C')执行反向expanding计算(也就是从当前行往最后一行累积,而非从第一行开始),核心思路是先反转组内的时间序列,执行普通的expanding操作后再反转回来。下面结合你的示例数据一步步实现:
步骤说明与代码实现
首先回顾你的示例数据生成代码(方便上下文理解):
import numpy as np import pandas as pd np.random.seed(456) j = [(a, b) for a in ['A','B','C'] for b in pd.date_range('2018-01-01', periods=5, freq='W')] i = pd.MultiIndex.from_tuples(j, names=['Name','Num']) df = pd.DataFrame(np.random.randn(15), i, columns=['Vals']) df.loc[df['Vals'] < 0] = np.nan
方法1:通用反向Expanding(支持任意聚合函数)
这个方法适用于所有expanding支持的聚合操作(sum、mean、max、min等),甚至自定义函数:
# 按Level-0的Name分组,对每个组执行反向expanding求和 reverse_expanding_result = df.groupby(level='Name').apply( lambda group: # 1. 反转组内的行顺序(从最后一行开始往前) group.iloc[::-1] # 2. 执行普通的expanding聚合(这里用sum,可替换为mean/max/min等) .expanding().sum() # 3. 再次反转行顺序,恢复原时间序列的顺序 .iloc[::-1] )
代码解释:
groupby(level='Name'):将DataFrame按Level-0索引(Name字段)拆分,每个组对应一个类别(A/B/C)的时间序列。group.iloc[::-1]:把当前组的行反转,让最后一行变成第一行,这样后续的expanding就相当于从原数据的末尾开始累积。.expanding().sum():对反转后的序列执行正向expanding求和(你可以把sum()换成任意需要的聚合函数,比如mean()、max())。- 最后一次
iloc[::-1]:把计算结果反转回来,恢复原数据的时间顺序,此时每行的值就是从当前行到该组最后一行的累积值。
方法2:针对求和的高效写法(用cumsum)
如果你的需求只是累积求和,那么可以用cumsum()替代expanding().sum(),速度会更快:
reverse_expanding_sum = df.groupby(level='Name').apply( lambda group: group.iloc[::-1].cumsum().iloc[::-1] )
这个代码的效果和方法1完全一致,但cumsum()是专门的累积求和函数,性能更优。
验证结果
比如查看'A'组的计算结果:
print(reverse_expanding_result.loc['A'])
你会看到每行的Vals值是从当前日期到该组最后一个日期的累积求和(自动忽略NaN值)。
扩展说明
如果需要指定最小有效数据量(比如至少1个非NaN值才计算),可以在expanding()中添加min_periods参数:
# 确保至少有1个非NaN值才返回结果 reverse_expanding_result = df.groupby(level='Name').apply( lambda group: group.iloc[::-1].expanding(min_periods=1).mean().iloc[::-1] )
内容的提问来源于stack exchange,提问作者feetwet
相关产品推荐
相关产品推荐

