Это действительно хорошая проблема, чтобы показать, как оптимизировать groupby.apply в пандах.Для решения этих проблем я использую два принципа:
- Любые вычисления, независимые от группы, не должны выполняться внутри группы
- Если имеется встроенныйгрупповой метод, сначала используйте его перед применением apply
Давайте пройдем построчно через вашу wmape_gr
функцию.
se_mape = abs(df_in[st_actual] - df_in[st_forecast]) / df_in[st_actual]
Эта строка полностью независима от любой группы.Вы должны сделать этот расчет за пределами заявки.Ниже я делаю это для каждого из столбцов прогноза:
df['actual_forecast_diff_1'] = (df['Actual'] - df['Forecast_1']).abs() / df['Actual']
df['actual_forecast_diff_2'] = (df['Actual'] - df['Forecast_2']).abs() / df['Actual']
df['actual_forecast_diff_3'] = (df['Actual'] - df['Forecast_3']).abs() / df['Actual']
df['actual_forecast_diff_4'] = (df['Actual'] - df['Forecast_4']).abs() / df['Actual']
Давайте посмотрим на следующую строку:
ft_actual_sum = df_in[st_actual].sum()
Эта строка зависит от группы, поэтому мы должны использоватьздесь, но нет необходимости помещать это в функцию применения.Это будет вычислено позже.
Давайте перейдем к следующей строке:
se_actual_prod_mape = df_in[st_actual] * se_mape
Это снова не зависит от группы.Давайте вычислим это для DataFrame в целом.
df['forecast1_wampe'] = df['actual_forecast_diff_1'] * df['Actual']
df['forecast2_wampe'] = df['actual_forecast_diff_2'] * df['Actual']
df['forecast3_wampe'] = df['actual_forecast_diff_3'] * df['Actual']
df['forecast4_wampe'] = df['actual_forecast_diff_4'] * df['Actual']
Давайте перейдем к последним двум строкам:
ft_actual_prod_mape_sum = se_actual_prod_mape.sum()
ft_wmape_forecast = ft_actual_prod_mape_sum / ft_actual_sum
Эти строки снова зависят от группы, но мы по-прежнемуне нужно использовать применять.Теперь у нас есть каждый из 4 столбцов «cast_wampe », независимо от группы.Нам просто нужно сложить каждый из них на группу.То же самое относится и к столбцу «Фактический».
Мы можем выполнить две отдельные групповые операции для суммирования каждого из этих столбцов, например:
g = df.groupby(['City', 'Person', 'DT'])
actual_sum = g['Actual'].sum()
forecast_wampe_cols = ['forecast1_wampe', 'forecast2_wampe', 'forecast3_wampe', 'forecast4_wampe']
forecast1_wampe_sum = g[forecast_wampe_cols].sum()
Мы получаем следующие Series и DataFrame, возвращаемые
Тогда нам просто нужно разделить каждый из столбцов в DataFrame наСерии.Нам нужно будет использовать метод div
, чтобы изменить ориентацию деления так, чтобы индексы совпали
forecast1_wampe_sum.div(actual_sum, axis='index')
И это вернет наш ответ: