文档展示了如何在一个groupby对象上同时应用多个函数,使用输出列名作为键的dict:

In [563]: grouped['D'].agg({'result1' : np.sum,
   .....:                   'result2' : np.mean})
   .....:
Out[563]: 
      result2   result1
A                      
bar -0.579846 -1.739537
foo -0.280588 -1.402938

但是,这只适用于Series groupby对象。当dict类似地通过DataFrame传递给一个组时,它期望键是函数将应用到的列名。

What I want to do is apply multiple functions to several columns (but certain columns will be operated on multiple times). Also, some functions will depend on other columns in the groupby object (like sumif functions). My current solution is to go column by column, and doing something like the code above, using lambdas for functions that depend on other rows. But this is taking a long time, (I think it takes a long time to iterate through a groupby object). I'll have to change it so that I iterate through the whole groupby object in a single run, but I'm wondering if there's a built in way in pandas to do this somewhat cleanly.

例如,我曾经尝试过

grouped.agg({'C_sum' : lambda x: x['C'].sum(),
             'C_std': lambda x: x['C'].std(),
             'D_sum' : lambda x: x['D'].sum()},
             'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)

但正如预期的那样,我得到一个KeyError(因为键必须是一列,如果agg从一个DataFrame调用)。

是否有任何内置的方式来做我想做的事情,或者这种功能可能会被添加,或者我只需要手动遍历组?


当前回答

Pandas >= 0.25.0,命名为聚合

从pandas 0.25.0或更高版本开始,我们将不再使用基于字典的聚合和重命名,而是使用接受元组的命名聚合。现在我们可以同时聚合+重命名为一个更有信息的列名:

例子:

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]

          a         b         c         d  group
0  0.521279  0.914988  0.054057  0.125668      0
1  0.426058  0.828890  0.784093  0.446211      0
2  0.363136  0.843751  0.184967  0.467351      1
3  0.241012  0.470053  0.358018  0.525032      1

应用GroupBy。具有命名聚合的Agg:

df.groupby('group').agg(
             a_sum=('a', 'sum'),
             a_mean=('a', 'mean'),
             b_mean=('b', 'mean'),
             c_sum=('c', 'sum'),
             d_range=('d', lambda x: x.max() - x.min())
)

          a_sum    a_mean    b_mean     c_sum   d_range
group                                                  
0      0.947337  0.473668  0.871939  0.838150  0.320543
1      0.604149  0.302074  0.656902  0.542985  0.057681

其他回答

0.25.0新版功能。

为了支持特定于列的聚合并控制输出列名,pandas接受GroupBy.agg()中的特殊语法,即“命名聚合”,其中

关键字是输出列名 这些值是元组,其第一个元素是要选择的列,第二个元素是要应用到该列的聚合。熊猫提供熊猫。NamedAgg用字段['column', 'aggfunc']命名元组,使其更清楚参数是什么。通常,聚合可以是可调用的或字符串别名。

>>> animals = pd.DataFrame({
...     'kind': ['cat', 'dog', 'cat', 'dog'],
...     'height': [9.1, 6.0, 9.5, 34.0],
...     'weight': [7.9, 7.5, 9.9, 198.0]
... })

>>> print(animals)
  kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0

>>> print(
...     animals
...     .groupby('kind')
...     .agg(
...         min_height=pd.NamedAgg(column='height', aggfunc='min'),
...         max_height=pd.NamedAgg(column='height', aggfunc='max'),
...         average_weight=pd.NamedAgg(column='weight', aggfunc=np.mean),
...     )
... )
      min_height  max_height  average_weight
kind                                        
cat          9.1         9.5            8.90
dog          6.0        34.0          102.75

熊猫。NamedAgg只是一个namedtuple。也允许使用普通元组。

>>> print(
...     animals
...     .groupby('kind')
...     .agg(
...         min_height=('height', 'min'),
...         max_height=('height', 'max'),
...         average_weight=('weight', np.mean),
...     )
... )
      min_height  max_height  average_weight
kind                                        
cat          9.1         9.5            8.90
dog          6.0        34.0          102.75

其他关键字参数不会传递给聚合函数。只有对(column, aggfunc)应该作为**kwarg传递。如果您的聚合函数需要额外的参数,请使用functools.partial()部分应用它们。

命名聚合也适用于系列groupby聚合。在这种情况下,没有列选择,所以值只是函数。

>>> print(
...     animals
...     .groupby('kind')
...     .height
...     .agg(
...         min_height='min',
...         max_height='max',
...     )
... )
      min_height  max_height
kind                        
cat          9.1         9.5
dog          6.0        34.0

泰德的回答很惊人。我最后用了一个更小的版本,以防有人感兴趣。当您正在寻找一个依赖于多个列的值的聚合时非常有用:

创建一个数据框架

df = pd.DataFrame({
    'a': [1, 2, 3, 4, 5, 6], 
    'b': [1, 1, 0, 1, 1, 0], 
    'c': ['x', 'x', 'y', 'y', 'z', 'z']
})

print(df)
   a  b  c
0  1  1  x
1  2  1  x
2  3  0  y
3  4  1  y
4  5  1  z
5  6  0  z

使用apply进行分组和聚合(使用多个列)

print(
    df
    .groupby('c')
    .apply(lambda x: x['a'][(x['a'] > 1) & (x['b'] == 1)]
    .mean()
)
c
x    2.0
y    4.0
z    5.0

使用聚合进行分组和聚合(使用多个列)

我喜欢这种方法,因为我仍然可以使用聚合。也许人们会告诉我,在对组进行聚合时,为什么需要apply来获取多个列。

现在看起来很明显,但只要不直接在groupby后面选择感兴趣的列,就可以从聚合函数中访问数据框架的所有列。

只能访问所选列

df.groupby('c')['a'].aggregate(lambda x: x[x > 1].mean())

访问所有的列,因为选择是神奇的

df.groupby('c').aggregate(lambda x: x[(x['a'] > 1) & (x['b'] == 1)].mean())['a']

或类似的

df.groupby('c').aggregate(lambda x: x['a'][(x['a'] > 1) & (x['b'] == 1)].mean())

我希望这能有所帮助。

作为Ted Petrou的答案的替代(主要是在美学方面),我发现我更喜欢一个更紧凑的列表。请不要考虑接受它,它只是一个更详细的评论Ted的答案,加上代码/数据。Python/熊猫不是我的第一个/最好的,但我发现这个读起来很好:

df.groupby('group') \
  .apply(lambda x: pd.Series({
      'a_sum'       : x['a'].sum(),
      'a_max'       : x['a'].max(),
      'b_mean'      : x['b'].mean(),
      'c_d_prodsum' : (x['c'] * x['d']).sum()
  })
)

          a_sum     a_max    b_mean  c_d_prodsum
group                                           
0      0.530559  0.374540  0.553354     0.488525
1      1.433558  0.832443  0.460206     0.053313

我发现它更容易让人想起dplyr管道和数据。表链接命令。不是说他们更好,只是对我来说更熟悉。(对于许多人来说,我当然认识到对这些类型的操作使用更形式化的def函数的力量和偏好。这只是一种选择,不一定更好。)


我用和泰德一样的方式生成数据,我将添加一个种子以提高再现性。

import numpy as np
np.random.seed(42)
df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.374540  0.950714  0.731994  0.598658      0
1  0.156019  0.155995  0.058084  0.866176      0
2  0.601115  0.708073  0.020584  0.969910      1
3  0.832443  0.212339  0.181825  0.183405      1

Pandas >= 0.25.0,命名为聚合

从pandas 0.25.0或更高版本开始,我们将不再使用基于字典的聚合和重命名,而是使用接受元组的命名聚合。现在我们可以同时聚合+重命名为一个更有信息的列名:

例子:

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]

          a         b         c         d  group
0  0.521279  0.914988  0.054057  0.125668      0
1  0.426058  0.828890  0.784093  0.446211      0
2  0.363136  0.843751  0.184967  0.467351      1
3  0.241012  0.470053  0.358018  0.525032      1

应用GroupBy。具有命名聚合的Agg:

df.groupby('group').agg(
             a_sum=('a', 'sum'),
             a_mean=('a', 'mean'),
             b_mean=('b', 'mean'),
             c_sum=('c', 'sum'),
             d_range=('d', lambda x: x.max() - x.min())
)

          a_sum    a_mean    b_mean     c_sum   d_range
group                                                  
0      0.947337  0.473668  0.871939  0.838150  0.320543
1      0.604149  0.302074  0.656902  0.542985  0.057681

这是对使用命名聚合的“exans”答案的扭曲。它是一样的,但是有参数解包,它允许你仍然将一个字典传递给agg函数。

命名的aggs是一个很好的特性,但是乍一看可能很难用编程方式编写,因为它们使用关键字,但实际上通过参数/关键字解包很简单。

animals = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
                         'height': [9.1, 6.0, 9.5, 34.0],
                         'weight': [7.9, 7.5, 9.9, 198.0]})
 
agg_dict = {
    "min_height": pd.NamedAgg(column='height', aggfunc='min'),
    "max_height": pd.NamedAgg(column='height', aggfunc='max'),
    "average_weight": pd.NamedAgg(column='weight', aggfunc=np.mean)
}

animals.groupby("kind").agg(**agg_dict)

结果

      min_height  max_height  average_weight
kind                                        
cat          9.1         9.5            8.90
dog          6.0        34.0          102.75