首页 > 解决方案 > 如何在熊猫中进行这种聚合?

问题描述

我有一个包含分类列和数值列的数据框,我希望根据分类列的值对数值列(最大值、最小值、总和...)上的值进行一些聚合(所以我必须为每个分类列可以采用的每个值)。

为了让它更不稳定,最好放一个玩具例子。

假设我有这个数据框:

 import pandas as pd
 df = pd.DataFrame({
     'ref' : [1, 1, 1, 2, 2, 3],
     'value_type' : ['A', 'B', 'A', 'C', 'C', 'A'],
     'amount' : [100, 50, 20, 300, 150, 70]
}).set_index(['ref'])
    value_type  amount
ref     
1      A      100
1      B      50
1      A      20
2      C      300
2      C      150
3      A      70

我想对 value_type 的值进行分组,也为每个参考分组。这种情况下的结果(假设只需要总和)将是这个:

df_result = pd.DataFrame({
    'ref' : [1, 2, 3],
    'sum_amount_A' : [120, 0, 70],
    'sum_amount_B' : [50, 0, 0],
    'sum_amount_C' : [0, 450, 0]
}).set_index('ref')
    sum_amount_A    sum_amount_B    sum_amount_C
ref         
1        120         50                   0
2        0           0                    450
3        70          0                    0

我尝试了一些可行的方法,但效率极低。大约需要几分钟来处理 30.000 行。

我所做的是:(我有一个数据框,每个索引引用只有一行,称为 df_final)

df_grouped = df.groupby(['ref'])

for ref in df_grouped.groups:
    df_aux = df.loc[[ref]]
    column = 'A' # I have more columns, but for illustration one is enough
    for value in df_aux[column].unique():
        df_aux_column_value = df_aux.loc[df_aux[column] == value]
        df_final.at[ref,'sum_' + column + '_' + str(value)] = np.sum(df_aux_columna_valor[column])

我敢肯定应该有更好的方法来进行这种聚合......在此先感谢!

编辑:

当只有一列可供分组时,给出的答案是正确的。在真实的数据框中,我有几列我想计算一些 agg 函数,但分别计算每列上的值。我的意思是我不希望列值的每个组合都有一个聚合值,而只需要列本身。

让我们举个例子。

import pandas as pd
df = pd.DataFrame({
    'ref' : [1, 1, 1, 2, 2, 3],
    'sexo' : ['Hombre', 'Hombre', 'Hombre', 'Mujer', 'Mujer', 'Hombre'],
    'lugar_trabajo' : ['Campo', 'Ciudad', 'Campo', 'Ciudad', 'Ciudad', 'Campo'],
    'dificultad' : ['Alta', 'Media', 'Alta', 'Media', 'Baja', 'Alta'],
    'amount' : [100, 50, 20, 300, 150, 70]
}).set_index(['ref'])

这个数据框看起来像这样:

   sexo lugar_trabajo   dificultad  amount
ref             
1   Hombre  Campo       Alta         100
1   Hombre  Ciudad      Media        50
1   Hombre  Campo       Alta         20
2   Mujer   Ciudad      Media        300
2   Mujer   Ciudad      Baja         150
3   Hombre  Campo       Alta         70

如果我按几列分组,或者制作一个数据透视表(据我所知,这在某种程度上是等效的),请执行以下操作:

df.pivot_table(index='ref',columns=['sexo','lugar_trabajo','dificultad'],values='amount',aggfunc=[np.sum,np.min,np.max,len], dropna=False)

我将得到一个包含 48 列的数据框(因为我有 3 * 2 * 2 个不同的值和 4 个 agg 函数)。

实现我想要的结果的一种方法是:

df_agregado = pd.DataFrame(df.index).set_index('ref')

for col in ['sexo','lugar_trabajo','dificultad']:
    df_agregado = pd.concat([df_agregado, df.pivot_table(index='ref',columns=[col],values='amount',aggfunc=[np.sum,np.min,np.max,len])],axis=1)

我一个人做每一组,然后把它们全部连接起来。这样我得到 28 列(2 * 4 + 3 * 4 + 2 * 4)。它可以工作并且速度很快,但它不是很优雅。有没有其他方法可以得到这个结果??

标签: pythonpandasdataframeaggregatedata-wrangling

解决方案


更有效的方法是使用 Pandas 内置函数而不是for循环。您应该采取两个主要步骤。首先,您不仅需要按索引分组,还需要按索引和列分组:

res = df.groupby(['ref','value_type']).sum()
print(res)

这一步的输出是这样的:

                amount
ref value_type        
1   A              120
    B               50
2   C              450
3   A               70

其次,需要对multi index进行unstack,如下:

df2 = res.unstack(level='value_type',fill_value=0)

输出将是您想要的输出:

    amount
value_type  A   B   C
ref         
1   120 50  0
2   0   0   450
3   70  0   0

作为可选步骤,您可以使用droplevel它来展平它:

df2.columns = df2.columns.droplevel()
value_type  A   B   C
ref         
1   120 50  0
2   0   0   450
3   70  0   0

推荐阅读