python - Pandas:自定义 WMAPE 函数聚合函数到没有 for 循环的多列?

标签 python pandas pandas-groupby forecasting pandas-apply

目标:在多个预测列和一个实际数据列上使用自定义 WMAPE(加权平均绝对百分比误差)函数对 pandas 数据框进行分组,无需 for 循环。我知道输出数据帧的 for 循环和合并可以解决问题。我想高效地做到这一点。

有: WMAPE 函数,在数据帧的一个预测列上成功使用 WMAPE 函数。一列实际数据,可变数量的预测列。

输入数据:具有多个分类列(City、Person、DT、HOUR)、一个实际数据列(Actual)和四个预测列(Forecast_1 ... Forecast_4)的 Pandas DataFrame。请参阅 csv 链接: https://www.dropbox.com/s/tidf9lj80a1dtd8/data_small_2.csv?dl=1

需要:WMAPE 函数在 groupby 期间应用于多列,并将预测列列表馈入 groupby 行。

所需输出:具有分类组列和 WMAPE 的所有列的输出数据框。标签是首选,但不是必需的(下面的输出图像)。

到目前为止成功的代码: 两个 WMAPE 函数:一个将两个系列输入并输出一个浮点值 (wmape),一个结构化用于 groupby (wmape_gr):

def wmape(actual, forecast):
    # we take two series and calculate an output a wmape from it

    # make a series called mape
    se_mape = abs(actual-forecast)/actual

    # get a float of the sum of the actual
    ft_actual_sum = actual.sum()

    # get a series of the multiple of the actual & the mape
    se_actual_prod_mape = actual * se_mape

    # summate the prod of the actual and the mape
    ft_actual_prod_mape_sum = se_actual_prod_mape.sum()

    # float: wmape of forecast
    ft_wmape_forecast = ft_actual_prod_mape_sum / ft_actual_sum

    # return a float
    return ft_wmape_forecast

def wmape_gr(df_in, st_actual, st_forecast):
    # we take two series and calculate an output a wmape from it

    # make a series called mape
    se_mape = abs(df_in[st_actual] - df_in[st_forecast]) / df_in[st_actual]

    # get a float of the sum of the actual
    ft_actual_sum = df_in[st_actual].sum()

    # get a series of the multiple of the actual & the mape
    se_actual_prod_mape = df_in[st_actual] * se_mape

    # summate the prod of the actual and the mape
    ft_actual_prod_mape_sum = se_actual_prod_mape.sum()

    # float: wmape of forecast
    ft_wmape_forecast = ft_actual_prod_mape_sum / ft_actual_sum

    # return a float
    return ft_wmape_forecast

# read in data directly from Dropbox
df = pd.read_csv('https://www.dropbox.com/s/tidf9lj80a1dtd8/data_small_2.csv?dl=1',sep=",",header=0)

# grouping with 3 columns. wmape_gr uses the Actual column, and Forecast_1 as inputs
df_gr = df.groupby(['City','Person','DT']).apply(wmape_gr,'Actual','Forecast_1')

输出看起来像(前两行):

enter image description here

所需的输出将一次性包含所有预测(Forecast_2 ... Forecast_4 的虚拟数据)。我可以已经使用 for 循环来做到这一点。我只想在 groupby 中进行。我想调用一个 wmape 函数四次。如有任何帮助,我将不胜感激。

最佳答案

这是一个非常好的问题,可以展示如何在 pandas 中优化 groupby.apply。我使用两个原则来帮助解决这些问题。

  1. 任何独立于组的计算都不应该在groupby中进行
  2. 如果有内置的groupby方法,在使用之前先使用它 申请

让我们逐行查看您的 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()

此行依赖于组,因此我们必须在此处使用 groupby,但没有必要将其放在 apply 函数中。稍后将在下面进行计算。

让我们转到下一行:

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 个“forecast_wampe”列中的每一列。我们只需要对每组中的每一个求和。 “实际”列也是如此。

我们可以运行两个单独的 groupby 操作来对这些列中的每一列求和,如下所示:

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()

我们得到以下系列和 DataFrame 返回

enter image description here

enter image description here

然后我们只需要将 DataFrame 中的每一列除以 Series。我们需要使用 div 方法来改变分区的方向,以便索引对齐

forecast1_wampe_sum.div(actual_sum, axis='index')

这返回了我们的答案:

enter image description here

关于python - Pandas:自定义 WMAPE 函数聚合函数到没有 for 循环的多列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54831335/

相关文章:

python - 按列名称操作 Pandas 数据框值

python - 过滤 Pandas 系列数组

python-3.x - Pandas 按两列分组,并获取按降序排序的其中一列的每个值的前 n 行

python - 如何为 C 代码创建 python 接口(interface)?

Python - 可选列表值 - 更Pythonic的方式?

Python正则表达式从文件中匹配字符串

python - 重置列的 MultiIndex 级别

python - 以可以在 Python 中排序的格式存储输出

python - Pandas Dataframe 中两个日期之间的营业时间(包括节假日)

python - 同一数据帧上的多个总和