二、聚合函数

1. 内置聚合函数

在介绍agg之前,首先要了解一些直接定义在groupby对象的聚合函数,因为它的速度基本都会经过内部的优化,使用功能时应当优先考虑。根据返回标量值的原则,包括如下函数: max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod

In [20]: gb = df.groupby('Gender')['Height']
In [21]: gb.idxmin()
Out[21]: 
Gender
Female    143
Male      199
Name: Height, dtype: int64
In [22]: gb.quantile(0.95)
Out[22]: 
Gender
Female    166.8
Male      185.9
Name: Height, dtype: float64

练一练

请查阅文档,明确 all/any/mad/skew/sem/prod 函数的含义。

这些聚合函数当传入的数据来源包含多个列时,将按照列进行迭代计算:

In [23]: gb = df.groupby('Gender')[['Height', 'Weight']]
In [24]: gb.max()
Out[24]: 
        Height  Weight
Gender        
Female   170.2    63.0
Male     193.9    89.0

2. agg方法

虽然在 groupby 对象上定义了许多方便的函数,但仍然有以下不便之处:

  • 无法同时使用多个函数
  • 无法对特定的列使用特定的聚合函数
  • 无法使用自定义的聚合函数
  • 无法直接对结果的列名在聚合前进行自定义命名

下面说明如何通过 agg 函数解决这四类问题:

【a】使用多个函数

当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的。

In [25]: gb.agg(['sum', 'idxmax', 'skew'])
Out[25]: 
         Height                   Weight         
            sum idxmax      skew     sum idxmax      skew
Gender                                           
Female  21014.0     28 -0.219253  6469.0     28 -0.268482
Male     8854.9    193  0.437535  3929.0      2 -0.332393

从结果看,此时的列索引为多级索引,第一层为数据源,第二层为使用的聚合方法,分别逐一对列使用聚合,因此结果为6列。

【b】对特定的列使用特定的聚合函数

对于方法和列的特殊对应,可以通过构造字典传入 agg 中实现,其中字典以列名为键,以聚合字符串或字符串列表为值。

In [26]: gb.agg({'Height':['mean','max'], 'Weight':'count'})
Out[26]: 
           Height        Weight
             mean    max  count
Gender               
Female  159.19697  170.2    135
Male    173.62549  193.9     54

练一练

请使用【b】中的传入字典的方法完成【a】中等价的聚合任务。

【c】使用自定义函数

agg 中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算 。下面分组计算身高和体重的极差:

In [27]: gb.agg(lambda x: x.mean()-x.min())
Out[27]: 
          Height     Weight
Gender           
Female  13.79697  13.918519
Male    17.92549  21.759259

练一练

groupby 对象中可以使用 describe 方法进行统计信息汇总,请同时使用多个聚合函数,完成与该方法相同的功能。

由于传入的是序列,因此序列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可。下面的例子是指,如果组的指标均值,超过该指标的总体均值,返回High,否则返回Low。

In [28]: def my_func(s):
   ....:     res = 'High'
   ....:     if s.mean() <= df[s.name].mean():
   ....:         res = 'Low'
   ....:     return res
   ....: 
In [29]: gb.agg(my_func)
Out[29]: 
       Height Weight
Gender      
Female    Low    Low
Male     High   High

【d】聚合结果重命名

如果想要对聚合结果的列名进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数,现举若干例子说明:

In [30]: gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])
Out[30]: 
       Height          Weight  
        range   my_sum  range  my_sum
Gender                       
Female   24.8  21014.0   29.0  6469.0
Male     38.2   8854.9   38.0  3929.0
In [31]: gb.agg({'Height': [('my_func', my_func), 'sum'],
   ....:         'Weight': lambda x:x.max()})
   ....: 
Out[31]: 
        Height            Weight
       my_func      sum <lambda>
Gender                  
Female     Low  21014.0     63.0
Male      High   8854.9     89.0

另外需要注意,使用对一个或者多个列使用单个聚合的时候,重命名需要加方括号,否则就不知道是新的名字还是手误输错的内置函数字符串:

In [32]: gb.agg([('my_sum', 'sum')])
Out[32]: 
         Height  Weight
         my_sum  my_sum
Gender         
Female  21014.0  6469.0
Male     8854.9  3929.0
In [33]: gb.agg({'Height': [('my_func', my_func), 'sum'],
   ....:         'Weight': [('range', lambda x:x.max())]})
   ....: 
Out[33]: 
        Height          Weight
       my_func      sum  range
Gender            
Female     Low  21014.0   63.0
Male      High   8854.9   89.0