分组键可以有多种形式,且类型不必相同
a.列表或数组,且长度与待分组的轴一样
b.表示DataFrame某个列的值
c.字典或Series,给出待分组轴上的值与分组名之间的对应关系
d.函数,用于处理轴索引或索引中的各个标签
df=DataFrame({'key1':['a','a','b','b','a'],
'key2':['one','two','one','two','one'],
'data1':np.random.randn(5),
'data2':np.random.randn(5)})
print df
结果为:
data1 data2 key1 key2假设想按key1进行分组,并计算data1列的平均值。
grouped=df['data1'].groupby(df['key1'])print grouped.mean()
结果为:
key1
a 0.009357
b 0.150788
Name: data1, dtype: float64
数据(Series)根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值。
如果我们一次传入多个数组,就会得到不同的结果
means=df['data1'].groupby([df['key1'],df['key2']]).mean()
print means
结果为:
key1 key2
a one 0.210008
two -0.235437
b one 1.218374
two -1.650944
Name: data1, dtype: float64
print means.unstack()
结果为:
key2 one two
key1
a -0.999244 -0.196333
b -1.373276 -0.193712
在上面的示例中,分组键均为Series。实际上,分组键可以是任何长度适当的数组
states=np.array(['Ohio','California','California','Ohio','Ohio'])
years=np.array([2005,2005,2006,2005,2006])
print df['data1'].groupby([states,years]).mean()
结果为:
California 2005 -1.483611
2006 1.493843
Ohio 2005 0.098908
2006 0.197631
Name: data1, dtype: float64
此外,还可以将列名用作分组键
print df.groupby('key1').mean()
print df.groupby([df['key1'],df['key2']]).mean()
结果为:
data1 data2
key1
a -0.193688 -0.548340
b -0.877449 -0.038743
data1 data2
key1 key2
a one 0.180915 -0.584049
two -0.942894 -0.476924
b one -0.431921 -0.396192
two -1.322977 0.318706
无论你准备拿groupby做什么,都有可能会用到GroupBy的size方法,它可以返回一个包含分组大小的Series
print df.groupby([df['key1'],df['key2']]).size()
结果为:
key1 key2
a one 2
two 1
b one 1
two 1
dtype: int64
1.对分组进行迭代
GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)
for name,group in df.groupby('key1'):
print name
print group
结果为:
a
data1 data2 key1 key2
0 -2.009305 1.592601 a one
1 0.352132 0.312646 a two
4 -0.385399 2.710117 a one
b
data1 data2 key1 key2
2 1.414748 -0.282476 b one
3 -0.717728 -0.941639 b two
对于多重键的情况,元组的第一个元素将会是由键组成的元组
for (k1,k2),group in df.groupby(['key1','key2']):
print k1,k2
print group
结果为:
a one
data1 data2 key1 key2
0 0.063003 -0.339903 a one
4 -0.684557 0.660166 a one
a two
data1 data2 key1 key2
1 -0.241703 2.085999 a two
b one
data1 data2 key1 key2
2 1.544291 0.663751 b one
b two
data1 data2 key1 key2
3 -2.1877 -0.447066 b two
将数据片段做成一个字典
pieces=dict(list(df.groupby('key1')))
print pieces
结果为:
{'a': data1 data2 key1 key2
0 -0.777734 0.040249 a one
1 -0.915293 0.702558 a two
4 1.150464 -0.705429 a one, 'b': data1 data2 key1 key2
2 0.13729 -1.814537 b one
3 -0.98634 -0.247277 b two}
groupby默认是在axis=0上进行分组的,通过设置页可以在其它任何轴上进行分组。上面的例子,可以根据dtype对列进行分组
print df.dtypes
grouped=df.groupby(df.dtypes,axis=1)
print dict(list(grouped))
结果为:
{dtype('O'): key1 key2
0 a one
1 a two
2 b one
3 b two
4 a one, dtype('float64'): data1 data2
0 -1.039294 0.755238
1 -0.066335 0.545684
2 -1.599256 1.784150
3 0.301808 1.120469
4 0.272417 -0.489573}
2.选取一个或一组列
对于由DataFrame产生的GroupBy对象,如果用一个或一组列名对其进行索引,就能实现选取部分列进行聚合的目的
df.groupby('key1')['data1']
df.groupby('key1')['data2']
以上代码是下方的语法糖
df['data1'].groupby(df['key1'])
df['data2'].groupby(df['key1'])
print df.groupby(['key1','key2'])[['data2']].mean()
结果为:
data2
key1 key2
a one -0.100339
two -1.060136
b one -0.820279
two 0.469765
这种索引操作所返回的对象是一个已分组的DataFrame或已分组的Series
s_grouped=df.groupby(['key1','key2'])['data2']
print s_grouped.mean()
结果为:
key1 key2
a one 0.788137
two 0.146028
b one -1.451863
two -1.079122
Name: data2, dtype: float64
3.通过字典或Series进行分组
people=DataFrame(np.random.randn(5,5),
columns=['a','b','c','d','e'],
index=['Joe','Steve','Wes','Jim','Travis'])
people.ix[2:3,['b','c']]=np.nan
print people
结果为:
a b c d e
Joe 1.634277 1.349782 0.128156 1.278728 1.777325
Steve -0.523126 0.864387 -1.826873 0.526364 -0.606603
Wes 0.654637 NaN NaN 1.705333 -1.563021
Jim 0.631228 -2.043448 0.803147 -0.270859 -.489161
Travis -0.845370 1.290415 -1.523796 1.722646 0.783629
假设已知列的分组关系,并希望根据分组计算列的总计
mapping={'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':'orange'}
by_column=people.groupby(mapping,axis=1)
print by_column.sum()
结果为:
blue red
Joe 1.468728 -0.479912
Steve 0.342441 -0.745494
Wes 1.699735 1.366498
Jim 1.309097 1.457425
Travis -0.509003 -1.766705
Series也有同样的功能,它可以被看做一个固定大小的映射。对于上面的例子,如果用Series作为分组键,则pandas会检查Series以确保其索引跟分组轴是对齐的
map_series=Series(mapping)
print map_series
print people.groupby(map_series,axis=1).count()
结果为:
a red
b red
c blue
d blue
e red
f orange
dtype: object
blue red
Joe 2 3
Steve 2 3
Wes 1 2
Jim 2 3
4.通过函数进行分组
任何被当做分组键的函数都会在各个索引值上被调用一次,其返回值就会被用做分组名称。
假设你希望根据人名的长度进行分组,虽然可以求取一个字符串长度数组,但其实仅仅传入len函数就可以了
print people.groupby(len).sum()
结果为:
a b c d e
3 -2.941612 0.287511 -0.563681 -2.299495 -2.397725
5 0.869115 -0.778281 0.499254 -1.638035 -0.364295
6 0.410260 0.051555 -2.052192 0.002615 -0.603595
将函数数组、列表、字典、Series混合使用也不是问题,因为任何东西最终都会被转为数组
5.根据索引级别分组
层次化索引数据集最方便的地方就在于它能够根据索引级别进行聚合。要实现该目的,通过level关键字传入级别编号或名称即可
columns=pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],[1,3,5,1,3]],names=['cty','tenor'])
hier_df=DataFrame(np.random.randn(4,5),columns=columns)
print hier_df
print hier_df.groupby(level='cty',axis=1).count()
结果为:
cty US JP
tenor 1 3 5 1 3
0 -0.435451 0.565291 0.166041 -1.024469 -0.186554
1 0.765508 0.028762 -0.260855 0.841159 0.672992
2 -0.668505 1.504732 -1.722919 -0.256070 -2.454356
3 -0.996366 2.231010 -2.008115 -0.178930 -0.282828
cty JP US
0 2 3
1 2 3
2 2 3
3 2 3
6.数据聚合
对于聚合,我指的是任何能够从数组产生标量值的数据转换过程。你可以使用自己发明的聚合运算,还可以调用分组对象上已经定义好的任何方法。例如,quantile可以计算Series或DataFrame列的样本分位数
print df
grouped=df.groupby('key1')
print grouped['data1'].quantile(0.9)
结果为:
data1 data2 key1 key2
0 -1.392568 -0.961439 a one
1 0.587472 -0.620796 a two
2 0.134594 1.188383 b one
3 -1.270534 0.256603 b two
4 0.731437 0.885424 a one
key1
a 0.702644
b -0.005919
Name: data1, dtype: float64
如果要使用你自己的聚合函数,只需将其传入aggregate或agg方法即可
def peak_to_peak(arr):
return arr.max()-arr.min()
print grouped.agg(peak_to_peak)
结果为:
data1 data2
key1
a 2.603744 0.806062
b 0.938295 3.332358
有些方法(如describe)也是可以用在这里的,即使严格来讲,它们并非聚合运算
print grouped.describe()
结果为:
data1 data2
key1
a count 3.000000 3.000000
mean 0.714186 0.008609
std 0.303997 0.528171
min 0.374777 -0.516977
25% 0.590555 -0.256751
50% 0.806332 0.003476
75% 0.883891 0.271402
max 0.961449 0.539328
b count 2.000000 2.000000
mean -0.952964 -0.031301
std 0.164985 0.859381
min -1.069626 -0.638975
25% -1.011295 -0.335138
50% -0.952964 -0.031301
75% -0.894633 0.272536
max -0.836302 0.576373
为了说明一些更高级的聚合功能,我将使用一个有关餐馆小费的数据集。
tips=pd.read_csv('data/tips.csv')
#添加“小费占总额百分比”的列
tips['tip_pct']=tips['tip']/tips['total_bill']
print tips[:6]
结果为:
total_bill tip sex smoker day time size tip_pct
0 16.99 1.01 Female No Sun Dinner 2 0.059447
1 10.34 1.66 Male No Sun Dinner 3 0.160542
2 21.01 3.50 Male No Sun Dinner 3 0.166587
3 23.68 3.31 Male No Sun Dinner 2 0.139780
4 24.59 3.61 Female No Sun Dinner 4 0.146808
5 25.29 4.71 Male No Sun Dinner 4 0.186240
7.面向列的多函数应用
对Series或DataFrame列的聚合运算其实就是使用aggregate(使用自定义函数)或调用诸如mean、std之类的方法。你可能希望对不同的列使用不同的聚合函数,或一次应用多个函数。
首先,根据sex和smoker对tips进行分组
grouped=tips.groupby(['sex','smoker'])
grouped_pct=grouped['tip_pct']
print grouped_pct.agg('mean')
结果为:
sex smoker
Female No 0.156921
Yes 0.182150
Male No 0.160669
Yes 0.152771
Name: tip_pct, dtype: float64
如果传入一组函数或函数名,得到的DataFrame的列就会以相应的函数命名
print grouped_pct.agg(['mean','std',peak_to_peak])
结果为:
mean std peak_to_peak
sex smoker
Female No 0.156921 0.036421 0.195876
Yes 0.182150 0.071595 0.360233
Male No 0.160669 0.041849 0.220186
Yes 0.152771 0.090588 0.674707
并非一定要接受GroupBy自动给出的那些列名,特别是lamba函数,它们的名称是‘
print grouped_pct.agg([('foo','mean'),('bar',np.std)])
结果为:
foo bar
sex smoker
Female No 0.156921 0.036421
Yes 0.182150 0.071595
Male No 0.160669 0.041849
Yes 0.152771 0.090588
对于DataFrame,你还可以定义一组应用于全部列的函数,或不同的列应用不同的函数。假设我们想要对tip_pct和total_bill列计算三个统计信息
functions=['count','mean','max']
result=grouped['tip_pct','total_bill'].agg(functions)
print result
结果为:
tip_pct total_bill
count mean max count mean max
sex smoker
Female No 54 0.156921 0.252672 54 18.105185 35.83
Yes 33 0.182150 0.416667 33 17.977879 44.30
Male No 97 0.160669 0.291990 97 19.791237 48.33
Yes 60 0.152771 0.710345 60 22.284500 50.81
如你所见,结果DataFrame拥有层次化的列,这相当于分别对各列进行聚合,然后用concat将结果组装到一起(列名用做keys参数)
print result['tip_pct']
结果为:
count mean max
sex smoker
Female No 54 0.156921 0.252672
Yes 33 0.182150 0.416667
Male No 97 0.160669 0.291990
Yes 60 0.152771 0.710345
跟前面一样,这里也可以传入带有自定义名称的元组列表
ftuples=[('Durchschnitt','mean'),('Abweichung',np.var)]
print grouped['tip_pct','total_bill'].agg(ftuples)
结果为:
tip_pct total_bill
Durchschnitt Abweichung Durchschnitt Abweichung
sex smoker
Female No 0.156921 0.001327 18.105185 53.092422
Yes 0.182150 0.005126 17.977879 84.451517
Male No 0.160669 0.001751 19.791237 76.152961
Yes 0.152771 0.008206 22.284500 98.244673
现在,假设你想要对不同的列应用不同的函数。具体的办法是想agg传入一个从列名映射到函数的字典
print grouped.agg({'tip':np.max,'size':'sum'})
结果为:
tip size
sex smoker
Female No 5.2 140
Yes 6.5 74
Male No 9.0 263
Yes 10.0 150
print grouped.agg({'tip_pct':['min','max','mean','std'],'size':'sum'})
结果为:
tip_pct size
min max mean std sum
sex smoker
Female No 0.056797 0.252672 0.156921 0.036421 140
Yes 0.056433 0.416667 0.182150 0.071595 74
Male No 0.071804 0.291990 0.160669 0.041849 263
Yes 0.035638 0.710345 0.152771 0.090588 150
只有将多个函数应用到至少一列时,DataFrame才会拥有层次化的列
8.以“无索引”的形式返回聚合函数
目前所有的示例中的聚合函数都有由唯一的分组键组成的索引。由于并不是总需要如此,可以向groupby传入as_index=False以禁用该功能
print tips.groupby(['sex','smoker'],as_index=False).mean()
结果为:
sex smoker total_bill tip size tip_pct
0 Female No 18.105185 2.773519 2.592593 0.156921
1 Female Yes 17.977879 2.931515 2.242424 0.182150
2 Male No 19.791237 3.113402 2.711340 0.160669
3 Male Yes 22.284500 3.051167 2.500000 0.152771
对结果调用reset_index也能得到这种形式的结果