Pandas分组后用expanding应用自定义函数报KeyError的解决问询
你遇到的KeyError: 'x1'本质是因为**expanding().apply()的默认行为和普通groupby.apply()不同**:即使设置了raw=False,它传递给自定义函数的是单列Series,而不是你预期的DataFrame子集,所以函数里访问_df['x1']自然找不到对应的键。
问题原因拆解
当你执行df.groupby('group').expanding().apply(foo, raw=False)时,Pandas会对每个expanding窗口的每一列单独处理,把单列的Series传给foo,而不是整个窗口的DataFrame。这就导致你的函数试图访问Series的'x1'键时触发KeyError。
正确实现方式(兼容复杂自定义函数)
因为你提到实际函数逻辑复杂无法拆分,最稳妥的办法是先通过groupby.apply()拿到完整的组DataFrame,再在组内手动实现expanding窗口的逻辑,确保传递给foo的是真正的DataFrame子集:
import pandas as pd # 初始化你的DataFrame df = pd.DataFrame.from_dict( { 'group': ['A','A','A','B','B','B'], 'time': [1,2,3,1,2,3], 'x1': [10,40,30,100,200,300], 'x2': [1,0,1,2,0,3] } ).sort_values('time') # 你的自定义函数(示例) def foo(_df): return _df['x1'].max() * _df['x2'].iloc[-1] # 定义组内的expanding处理逻辑 def process_group(group): # 遍历每个行的位置,生成截至当前行的expanding窗口 expanding_results = [] for idx in range(1, len(group) + 1): # 截取从第一行到当前行的窗口 window_df = group.iloc[:idx] expanding_results.append(foo(window_df)) # 将结果赋值给组内的新列 group['foo_result'] = expanding_results return group # 应用到分组数据,group_keys=False避免额外的分组索引 df = df.groupby('group', group_keys=False).apply(process_group) print(df)
运行结果
执行后会得到符合预期的输出(注:你的示例中A组第二行x2值为0,所以结果是40*0=0,和你写的期望结果80略有出入,应该是笔误,但代码逻辑完全符合需求):
group time x1 x2 foo_result 0 A 1 10 1 10 3 B 1 100 2 200 1 A 2 40 0 0 4 B 2 200 0 0 2 A 3 30 1 40 5 B 3 300 3 900
为什么这个方法有效?
通过groupby('group').apply(process_group),我们可以拿到每个分组的完整DataFrame,然后手动遍历每个行的位置,截取截至当前行的窗口(完全模拟expanding的行为),再把这个窗口DataFrame传给你的自定义函数foo,这样就不会出现KeyError,同时完全兼容任何复杂的函数逻辑。
内容的提问来源于stack exchange,提问作者Itamar Mushkin

