数据聚合
对于聚合,这里指的是任何能够从数组产生标量值的数据转换过程。之前的例子中已经用过一些,比如mean、count、min以及sum等。我们可能想知道在GroupBy对象上调用mean()时究竟发生了什么。许多常见的聚合运算(如下表所示)都有就地计算数据集统计信息的优化实现。然而,并不是只能使用这些方法。我们可以使用自己发明的聚合运算,还可以调用分组对象上已经定义好的任何方法。例如,quantile可以计算Series或DataFrame列的样本分位数:
In [1]: import pandas as pd
In [2]: from pandas import Series,DataFrame
In [3]: import numpy as np
In [4]: df=DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one','t
...: wo','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)})
In [5]: df
Out[5]:
key1 key2 data1 data2
0 a one 0.280118 0.195369
1 a two 1.166245 0.728360
2 b one 0.701840 1.373510
3 b two 0.485219 -1.124589
4 a one -0.555356 0.899907
In [6]: grouped=df.groupby('key1')
In [7]: grouped
Out[7]:
D8>
In [8]: grouped['data1'].quantile(0.9)
Out[8]:
key1
a 0.989020
b 0.680178
Name: data1, dtype: float64
虽然quantile并没有明确地实现于GroupBy,但它是一个Series方法,所以这里是能用的。实际上,GroupBy会高效地对Series进行切片,然后对各片调用piece,quantile(0.9),最后将这些结果组装成最终结果。
如果要使用我们自己的聚合函数,只需将其传入aggregate或agg方法即可:
In [10]: def peak_to_peak(arr):
...: return arr.max()-arr.min()
...:
In [11]: grouped.agg(peak_to_peak)
Out[11]:
data1 data2
key1
a 1.721601 0.704539
b 0.216621 2.498099
注意,有些方法(如descirbe)也是可以用在这里的,即使严格来讲,它们并非聚合运算:
In [12]: grouped.describe()
Out[12]:
data1 ... data2
count mean std ... 50% 75% max
key1 ...
a 3.0 0.297002 0.860925 ... 0.72836 0.814134 0.899907
b 2.0 0.593530 0.153174 ... 0.12446 0.748985 1.373510
[2 rows x 16 columns]
在后面关于分组级运算和转换的那一节中,我们将详细说明这到底是怎么回事。
表:经过优化的GroupBy的方法
函数名 说明
count 分组中非NA值的数量
sum 非NA值的和
mean 非NA值的平均值
median 非NA值的算术中位数
std、var 无偏(分母为n-1)标准差和方差
min、max 非NA值的最小值和最大值
prod 非NA值的积
first、last 第一个和最后一个非NA值
1.面向列的多函数应用
我们已经看到,对Series或DataFrame列的聚合运算其实就是使用aggregate(使用自定义函数)或调用诸如mean、std之类的方法。然而,我们可能希望对不同的列使用不同的聚合函数,或一次应用多个函数。其实这事也好办,我们将通过一些示例来进行说明。
In [37]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'scor
...: e':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'
...: smoker':['True','False','False','False','False'],'age':[30,36,20,24,8]
...: ,'ss_pct':['0.8','0.9','0.75','0.7','0.6']})
In [38]: grouped=tips.groupby(['sex','smoker'])
注意,对于上表中的那些描述统计,可以将函数名以字符串的形式传入:
In [39]: grouped.agg('mean')
Out[39]:
score age
sex smoker
Female False -0.039857 16
Male False -0.895152 28
True -0.264493 30
如果传入一组函数或函数名,得到的DataFrame的列就会以相应的函数命名:
In [40]: grouped.agg(['mean','std',peak_to_peak])
Out[40]:
score ... age
mean std ... std peak_to_peak
sex smoker ...
Female False -0.039857 0.547300 ... 11.313708 16
Male False -0.895152 0.271034 ... 11.313708 16
True -0.264493 NaN ... NaN 0
[3 rows x 6 columns]
我们并非一定要接受GroupBy自动给出的那些列名,特别是lambda函数,它们的名称是‘’,这样的辨识度就很低了(通过函数的name属性看看就知道了)。如果传入的是一个由(name,function)元组组成的列表,则各元组的第一个元素就会被用作DataFrame的列名(可以将这种二元元组列表看做一个有序映射):
In [41]: grouped.agg([('foo','mean'),('bar',np.std)])
Out[41]:
score age
foo bar foo bar
sex smoker
Female False -0.039857 0.547300 16 11.313708
Male False -0.895152 0.271034 28 11.313708
True -0.264493 NaN 30 NaN
对于DataFrame,我们还可以定义一组应用于全部列的函数,或不同的列应用不同的函数。假设我们想要对score和age列计算三个统计信息:
In [1]: import numpy as np
In [2]: from pandas import Series,DataFrame
In [3]: tips=DataFrame({'name':['calvin','kobe','michale','kate','mary'],'score
...: ':np.random.randn(5),'sex':['Male','Male','Male','Female','Female'],'sm
...: oker':['True','False','False','False','False'],'age':[30,36,20,24,8],'s
...: s_pct':['0.8','0.9','0.75','0.7','0.6']})
In [4]: functions=['count','mean','max']
In [5]: grouped=tips.groupby(['sex','smoker'])
In [9]: result=grouped['score','age'].agg(functions)
In [10]: result