给定一个 pandas 数据框,将其按 3 个级别进行分组,并应用一个滚动地采用两列作为参数的函数:
创建数据框的代码:
import pandas as pd
import numpy as np
df_index = pd.DataFrame({
'classes': ['A']*3*3 + ['B']*3*3 +['C']*3*3,
'names': ['Alex']*3 + ['Alan']*3 + ['Ash']*3 + ['Bart']*3 + ['Blake']*3 + ['Beth']*3 + ['Charlie']*3 + ['Cristine']*3 + ['Cameron']*3,
'numbers': [0, 1, 2] * 9
})
df_values = pd.DataFrame(data=np.random.normal(), index=pd.date_range(start='2020-01-01', end='2024-01-01'), columns=['net', 'gross']).rename_axis('date').reset_index(drop=False)
df = pd.DataFrame()
for i in range(len(df_index)):
_df = df_values.copy()
_df['classes'] = df_index.iloc[i]['classes']
_df['names'] = df_index.iloc[i]['names']
_df['numbers'] = df_index.iloc[i]['numbers']
df = pd.concat((df, _df), axis=0)
df.set_index(['classes','names','numbers','date'], inplace=True)
部分功能:
def fun(net, gross):
return net.mean() / gross.std()
以下内容不起作用。我正在寻找 groupby,并滚动应用“fun()”:
df.groupby(['classes', 'names', 'numbers']).rolling(window=500).apply(
lambda x: fun(net=x['net'], gross=x['gross'])
)
谢谢。
PS:真正的 fun() 比这里的复杂得多,所以我无法直接向 groupby 计算“.mean()”和“.std()”。
rolling.apply
按列进行操作,因此无法直接使用多列。
相反,您可以对one列进行切片,并通过根据窗口的索引对原始DataFrame进行切片来使用副作用:
(df.groupby(['classes', 'names', 'numbers']).rolling(window=500)['net']
.apply(lambda x: fun(net=df.loc[x.index, 'net'],
gross=df.loc[x.index, 'gross']))
)
但请注意,操作可能会非常慢!
如评论所述,
.groupby().rolling().apply()
适用于列,因此您无权访问groupby
中的其他列。您可以尝试取消堆叠组索引并在宽数据帧上滚动:
wide_df = df.unstack(['classes','names','numbers'])
fun(wide_df['net'].dropna().rolling(5).mean(), wide_df['gross'].dropna().rolling(5).std())