Применить функцию к групповой и скользящей группе

Учитывая фрейм данных 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 работает для каждого столбца, поэтому напрямую использовать несколько столбцов невозможно.
mozway 09.04.2024 15:47
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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 09.04.2024 16:08

@AlexSB вы можете передать x в функцию fun(x), но тогда вам придется разрезать DataFrame в функции. Просто пройти (net=x, gross=x) не получится.

mozway 09.04.2024 16:11

не оба. только сеть = x. Поскольку прокрутка и применение уже выполняются на основе столбца ['net']?

AlexSB 09.04.2024 16:48

Если я не правильно понял, что вы имели в виду, нет, вы не сможете пройти x, если не измените функцию для выполнения внутри нее нарезки фрейма данных.

mozway 09.04.2024 16:50

Как уже отмечалось, .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())

Другие вопросы по теме