Учитывая фрейм данных 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()
Следующее не работает. Я ищу группировку и применяю "fun()" по мере поступления:
df.groupby(['classes', 'names', 'numbers']).rolling(window=500).apply(
lambda x: fun(net=x['net'], gross=x['gross'])
)
Спасибо.
PS: настоящая fun() намного сложнее, чем здесь, поэтому я не могу вычислить ".mean()" и ".std()" непосредственно в groupby.
rolling.apply
работает для каждого столбца, поэтому напрямую использовать несколько столбцов невозможно.
Вместо этого вы можете разрезать один столбец и использовать побочный эффект, разрезав исходный 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']))
)
Но имейте в виду, что операция может быть очень медленной!
Замена «net=df.loc[x.index, 'net']» на «net=x» будет работать так же хорошо и быстрее, верно?
@AlexSB вы можете передать x
в функцию fun(x)
, но тогда вам придется разрезать DataFrame в функции. Просто пройти (net=x, gross=x)
не получится.
не оба. только сеть = x. Поскольку прокрутка и применение уже выполняются на основе столбца ['net']?
Если я не правильно понял, что вы имели в виду, нет, вы не сможете пройти x
, если не измените функцию для выполнения внутри нее нарезки фрейма данных.
Как уже отмечалось, .groupby().rolling().apply()
работает со столбцами, поэтому у вас нет доступа к другим столбцам в groupby
. Вы можете попробовать разложить групповые индексы и перейти к широкому кадру данных:
wide_df = df.unstack(['classes','names','numbers'])
window = 5
fun(wide_df['net'].dropna().rolling(window).mean(),
wide_df['gross'].dropna().rolling(window).std())
rolling.apply
работает для каждого столбца, поэтому напрямую использовать несколько столбцов невозможно.