%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
数据汇总和组操作
1、GroupBy Mechanics(分组机制)
Hadley Wickham,是很多R语言有名库的作者,他描述group operation(组操作)为split-apply-combine(分割-应用-结合)。第一个阶段,存储于series或DataFrame中的数据,根据不同的keys会被split(分割)为多个组。而且分割的操作是在一个特定的axis(轴)上。例如,DataFrame能按行(axis=0)或列(axis=1)来分组。之后,我们可以把函数apply(应用)在每一个组上,产生一个新的值。最后,所以函数产生的结果被combine(结合)为一个结果对象(result object)。下面是一个图示:
每一个用于分组的key能有很多形式,而且keys也不必都是一种类型
df = pd.DataFrame({
'key1' : ['a' , 'a' , 'b' , 'b' , 'a' ],
'key2' : ['one' , 'two' , 'one' , 'two' , 'one' ],
'data1' : np.random.randn(5 ),
'data2' : np.random.randn(5 )})
df
data1
data2
key1
key2
0
0.120113
0.412418
a
one
1
1.541971
-0.459096
a
two
2
-0.480680
0.703068
b
one
3
0.438569
-0.891204
b
two
4
0.389028
-0.565778
a
one
grouped = df['data1' ].groupby(df['key1' ])
grouped
<pandas.core.groupby.SeriesGroupBy object at 0x0000000017E5CCC0>
这个grouped变量是一个GroupBy object(分组对象)。实际上现在还没有进行任何计算,除了调用group key(分组键)df[‘key1’]时产生的一些中间数据。整个方法是这样的,这个GroupBy object(分组对象)已经有了我们想要的信息,现在需要的是对于每一个group(组)进行一些操作。例如,通过调用GroupBy的mean方法,我们可以计算每个组的平均值
grouped.mean()
key1
a 0.683704
b -0.021055
Name: data1, dtype: float64
grouped.mean().index
Index(['a', 'b'], dtype='object', name='key1')
means = df['data1' ].groupby([df['key1' ], df['key2' ]]).mean()
means
key1 key2
a one 0.254570
two 1.541971
b one -0.480680
two 0.438569
Name: data1, dtype: float64
means.unstack()
key2
one
two
key1
a
0.25457
1.541971
b
-0.48068
0.438569
df.groupby('key1' ).mean()
data1
data2
key1
a
0.683704
-0.204152
b
-0.021055
-0.094068
df.groupby(['key1' , 'key2' ]).mean()
data1
data2
key1
key2
a
one
0.254570
-0.076680
two
1.541971
-0.459096
b
one
-0.480680
0.703068
two
0.438569
-0.891204
上面第一例,结果里并没有key2这一列。因为df[‘key2’]这一列不是数值型数据,我们称这种列为nuisance column(有碍列),这种列不会出现在结果中。 默认情况下,所有的数值型列都会被汇总计算,但是出现有碍列的情况的话,就会过滤掉这种列。 另外一点需要注意的是,如果作为group key的列中有缺失值的话,也不会出现在结果中。
df.groupby(['key1' , 'key2' ]).size()
key1 key2
a one 2
two 1
b one 1
two 1
dtype: int64
2、对组进行迭代
GroupBy对象支持迭代,能产生一个2-tuple(二元元组),包含组名和对应的数据块。
pieces = dict(list(df.groupby('key1' )))
pieces
{'a': data1 data2 key1 key2
0 0.120113 0.412418 a one
1 1.541971 -0.459096 a two
4 0.389028 -0.565778 a one, 'b': data1 data2 key1 key2
2 -0.480680 0.703068 b one
3 0.438569 -0.891204 b two}
pieces['b' ]
data1
data2
key1
key2
2
-0.480680
0.703068
b
one
3
0.438569
-0.891204
b
two
3、选中一列,或列的子集
如果一个GroupBy对象是由DataFrame创建来的,那么通过列名或一个包含列名的数组来对GroupBy对象进行索引的话,就相当于对列取子集做聚合(column subsetting for aggregation)。这句话的意思是:
df.groupby('key1' )['data1' ]
df.groupby('key1' )[['data2']]
上面的代码其实就是下面的语法糖(Syntactic sugar):
df['data1' ].groupby(df['key1' ])
df[['data2']] .groupby(df['key1' ])
df
data1
data2
key1
key2
0
0.120113
0.412418
a
one
1
1.541971
-0.459096
a
two
2
-0.480680
0.703068
b
one
3
0.438569
-0.891204
b
two
4
0.389028
-0.565778
a
one
df.groupby(['key1' , 'key2' ])['data1' ].mean()
key1 key2
a one 0.254570
two 1.541971
b one -0.480680
two 0.438569
Name: data1, dtype: float64
df.groupby(['key1' , 'key2' ])[['data1' ]].mean()
data1
key1
key2
a
one
0.254570
two
1.541971
b
one
-0.480680
two
0.438569
4、用Dicts与Series进行分组
people = pd.DataFrame(np.random.randn(5 , 5 ),
columns=['a' , 'b' , 'c' , 'd' , 'e' ],
index=['Joe' , 'Steve' , 'Wes' , 'Jim' , 'Travis' ])
people.iloc[2 :3 , [1 , 2 ]] = np.nan
people
a
b
c
d
e
Joe
-0.189645
-0.154784
-0.651044
-0.086191
0.962789
Steve
-1.508240
0.568177
-0.745980
0.559590
0.090825
Wes
-0.090708
NaN
NaN
-0.473691
-0.089319
Jim
0.557464
0.796324
0.393388
-0.372522
0.927045
Travis
1.367567
0.507116
-0.745044
0.620176
-1.468771
mapping = {
'a' : 'red' , 'b' : 'red' , 'c' : 'blue' ,
'd' : 'blue' , 'e' : 'red' , 'f' : 'orange' }
现在,我们可以通过这个dict构建一个数组,然后传递给groupby,但其实我们可以直接传入dict(可以注意到key里有一个’f’,这说明即使有,没有被用到的group key,也是ok的)
by_column = people.groupby(mapping, axis=1 )
by_column.sum()
blue
red
Joe
-0.737235
0.618361
Steve
-0.186390
-0.849238
Wes
-0.473691
-0.180027
Jim
0.020865
2.280834
Travis
-0.124868
0.405912
这种用法同样适用于series,这种情况可以看作是固定大小的映射(fixed-size mapping)
map_series = pd.Series(mapping)
map_series
a red
b red
c blue
d blue
e red
f orange
dtype: object
people.groupby(map_series, axis=1 ).count()
blue
red
Joe
2
3
Steve
2
3
Wes
1
2
Jim
2
3
Travis
2
3
5、用函数进行分组
比起用dict或series定义映射关系,使用python的函数是更通用的方法。任何一个作为group ke