groupby 技术

分组键可以有很多形式,且类型不必相同:
1、列表或数组,其长度与待分组的轴一样
2、表示DataFrame某个列名的值
3、字典或Series,给出待分组轴上的值与分组名之间的对应关系
4、函数,用于处理轴索引或索引中的各个标签
 
1、分组键为Series
 1 df=DataFrame({'key1':['a','a','b','b','a'],
 2 'key2':['one','two','one','two','one'],
 3 'data1':np.random.randn(5),
 4 'data2':np.random.randn(5)})
 5 
 6 df
 7 Out[6]: 
 8       data1     data2 key1 key2
 9 0 -0.814074  1.244593    a  one
10 1 -1.203203 -0.199076    a  two
11 2  0.846649  1.136826    b  one
12 3 -1.700835  1.822935    b  two
13 4  1.190682 -2.001369    a  one

按照key1进行分组,并计算data1列的平均值,这里使用:访问data1,并根据key1调用groupby:

1 grouped=df['data1'].groupby(df['key1'])
2 grouped
3 Out[6]: <pandas.core.groupby.SeriesGroupBy object at 0x000000000ADEEC18>

变量grouped是一个GroupBy对象。实际上还没有进行任何计算,只是含有一些有关分组键df['key1']的中间数据。换句话说,该对象已经有了接下来对个分组执行运算所需的一切信息。

1 #调用GroupBy的mean方法计算分组平均值
2 grouped.mean()
3 Out[8]: 
4 key1
5 a   -0.275532
6 b   -0.427093
7 Name: data1, dtype: float64
可以看出,数据(Series)根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值。之所以结果中索引的名称为key1,是因为原始DataFrame的列df['key1']就叫这个名字。
一次传入多个数组
 1 #一次传入多个数组,使用列表方式[]
 2 means=df['data1'].groupby([df['key1'],df['key2']]).mean()
 3 
 4 means
 5 Out[11]: 
 6 key1  key2
 7 a     one     0.188304
 8       two    -1.203203
 9 b     one     0.846649
10       two    -1.700835
11 Name: data1, dtype: float64
12 
13 #分组后得到的Series具有一个层次化索引
14 means.unstack()
15 Out[12]: 
16 key2       one       two
17 key1                    
18 a     0.188304 -1.203203
19 b     0.846649 -1.700835
 
2、分组键是数组
上面的示例中,分组键均为Series,实际上,分组键可以是任何长度适当的数组
 1 states=np.array(['Ohio','California','California','Ohio','Ohio'])
 2 
 3 years=np.array([2005,2005,2006,2005,2006])
 4 
 5 df['data1'].groupby([states,years]).mean()
 6 Out[15]: 
 7 California  2005   -1.203203
 8             2006    0.846649
 9 Ohio        2005   -1.257454
10             2006    1.190682
11 Name: data1, dtype: float64
 
3、列名做分组键
此外还可以将列名(可以是字符串、数字或其他Python对象)作为分组键:
 1 df.groupby('key1').mean()
 2 Out[16]: 
 3          data1     data2
 4 key1                    
 5 a    -0.275532 -0.318617
 6 b    -0.427093  1.479880
 7 
 8 df.groupby(['key1','key2']).mean()
 9 Out[17]: 
10               data1     data2
11 key1 key2                    
12 a    one   0.188304 -0.378388
13      two  -1.203203 -0.199076
14 b    one   0.846649  1.136826
15      two  -1.700835  1.822935
可以注意到,在执行df.groupby('key1').mean()时,结果中没有key2列。这是因为df['key2']不是数值数据(俗称“麻烦列”),所以从结果中排除了。默认情况下,所有数值列都会被聚合
GroupBy的size方法,它可以返回一个含有分组大小的Series:
1 df.groupby(['key1','key2']).size()
2 Out[18]: 
3 key1  key2
4 a     one     2
5       two     1
6 b     one     1
7       two     1
8 dtype: int64
对分组进行迭代
GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)。
 1 for name,group in df.groupby('key1'):
 2     print(name)
 3     print(group)
 4     
 5 a
 6       data1     data2 key1 key2
 7 0 -0.814074  1.244593    a  one
 8 1 -1.203203 -0.199076    a  two
 9 4  1.190682 -2.001369    a  one
10 b
11       data1     data2 key1 key2
12 2  0.846649  1.136826    b  one
13 3 -1.700835  1.822935    b  two

多重键,元组的第一个元素将会是由键值组成的元组

 1 for (k1,k2),group in df.groupby(['key1','key2']):
 2     print(k1,k2)
 3     print(group)
 4     
 5 a one
 6       data1     data2 key1 key2
 7 0 -0.814074  1.244593    a  one
 8 4  1.190682 -2.001369    a  one
 9 a two
10       data1     data2 key1 key2
11 1 -1.203203 -0.199076    a  two
12 b one
13       data1     data2 key1 key2
14 2  0.846649  1.136826    b  one
15 b two
16       data1     data2 key1 key2
17 3 -1.700835  1.822935    b  two

可以对这些数据片段做任何操作,例如:将这些数据片段做成一个字典。

 1 pieces=dict(list(df.groupby('key1')))
 2 
 3 pieces
 4 Out[24]: 
 5 {'a':       data1     data2 key1 key2
 6  0 -0.814074  1.244593    a  one
 7  1 -1.203203 -0.199076    a  two
 8  4  1.190682 -2.001369    a  one, 'b':       data1     data2 key1 key2
 9  2  0.846649  1.136826    b  one
10  3 -1.700835  1.822935    b  two}
11 
12 pieces['b']
13 Out[25]: 
14       data1     data2 key1 key2
15 2  0.846649  1.136826    b  one
16 3 -1.700835  1.822935    b  two

groupby默认是在axis=0上进行分组的,通过设置也可以在其他任何轴上进行分组。

 1 df.dtypes
 2 Out[26]: 
 3 data1    float64
 4 data2    float64
 5 key1      object
 6 key2      object
 7 dtype: object
 8 
 9 #在axis=1分组
10 grouped=df.groupby(df.dtypes,axis=1)
11 dict(list(grouped))
12 Out[29]: 
13 {dtype('float64'):       data1     data2
14  0 -0.814074  1.244593
15  1 -1.203203 -0.199076
16  2  0.846649  1.136826
17  3 -1.700835  1.822935
18  4  1.190682 -2.001369, dtype('O'):   key1 key2
19  0    a  one
20  1    a  two
21  2    b  one
22  3    b  two
23  4    a  one}
选取一个或一组列
对于由DataFrame产生的GroupBy对象,如果用一个(单个字符串)或一组(字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的。
1 df.groupby('key1')['data1']
2 df.groupby('key1')[[data2']]
3 #上面的代码是下面代码的语法糖
4 df['data1'].groupby(df['key1'])
5 df[['data2']].groupby(df['key1'])                    
1 #对部分列进行聚合
2 df.groupby(['key1','key2'])[['data2']].mean()
3 Out[32]: 
4               data2
5 key1 key2          
6 a    one  -0.378388
7      two  -0.199076
8 b    one   1.136826
9      two   1.822935

这样操作返回的对象是一个已分组的DataFrame(传入的是列表或数组)或已分组的Series(传入的是标量形式的单个列名):

1 s_grouped=df.groupby(['key1','key2'])['data2']
2 s_grouped.mean()
3 Out[36]: 
4 key1  key2
5 a     one    -0.378388
6       two    -0.199076
7 b     one     1.136826
8       two     1.822935
9 Name: data2, dtype: float64

 

4、通过字典或Series进行分组

 1 people=DataFrame(np.random.randn(5,5),
 2 columns=['a','b','c','d','e'],
 3 index=['Joe','Steve','Wes','Jim','Travis'])
 4 
 5 #将行索引为2,列索引名为'b','c'的数据赋值为NaN
 6 people.ix[2:3,['b','c']]=np.nan
 7 __main__:1: DeprecationWarning: 
 8 .ix is deprecated. Please use
 9 .loc for label based indexing or
10 .iloc for positional indexing
11 
12 See the documentation here:
13 http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
14 
15 people
16 Out[42]: 
17                a         b         c         d         e
18 Joe     0.125621 -0.059778  0.437543 -1.583435  0.472849
19 Steve   0.855371  0.461129 -0.126290  0.146014  0.373913
20 Wes    -2.106125       NaN       NaN  0.895130 -1.547358
21 Jim     0.155206  0.202384  0.932044 -1.171872 -1.035313
22 Travis  0.875559 -0.161025  0.482190  1.593750  0.637874

假设已知列的分组关系,并希望根据分组计算列的总计:

 1 mapping={'a':'red','b':'red','c':'blue',
 2 'd':'blue','e':'red','f':'orange'}
 3 
 4 #将mapping这个字典传给groupby
 5 by_column=people.groupby(mapping,axis=1)
 6 
 7 by_column.sum()
 8 Out[45]: 
 9             blue       red
10 Joe    -1.145892  0.538692
11 Steve   0.019724  1.690413
12 Wes     0.895130 -3.653483
13 Jim    -0.239828 -0.677722
14 Travis  2.075939  1.352408
 
5、用Series做分组键
Series也有同样的功能,被看做一个固定大小的映射。用Series做分组键,pandas会检查Series以确保其索引跟分组轴是对齐的:
 1 map_series=Series(mapping)
 2 
 3 map_series
 4 Out[48]: 
 5 a       red
 6 b       red
 7 c      blue
 8 d      blue
 9 e       red
10 f    orange
11 dtype: object
12 
13 people.groupby(map_series,axis=1).count()
14 Out[49]: 
15         blue  red
16 Joe        2    3
17 Steve      2    3
18 Wes        1    2
19 Jim        2    3
20 Travis     2    3

 

6、通过函数进行分组
任何被当做分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称。
1 people.groupby(len).sum()
2 Out[50]: 
3           a         b         c         d         e
4 3 -1.825298  0.142606  1.369587 -1.860177 -2.109822
5 5  0.855371  0.461129 -0.126290  0.146014  0.373913
6 6  0.875559 -0.161025  0.482190  1.593750  0.637874

将函数和数组、列表、字典、Series混合使用也必是问题,因为任何东西最终都会被转换为数组:

1 key_list=['one','one','one','two','two']
2 people.groupby([len,key_list]).min()
3 Out[53]: 
4               a         b         c         d         e
5 3 one -2.106125 -0.059778  0.437543 -1.583435 -1.547358
6   two  0.155206  0.202384  0.932044 -1.171872 -1.035313
7 5 one  0.855371  0.461129 -0.126290  0.146014  0.373913
8 6 two  0.875559 -0.161025  0.482190  1.593750  0.637874

 

7、根据索引级别分组
层次化索引数据集最方便的地方在于能够根据索引级别进行聚合。
 1 columns=pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],
 2 [1,3,5,1,3]],names=['cty','tenor'])
 3 
 4 hier_df=DataFrame(np.random.randn(4,5),columns=columns)
 5 
 6 hier_df
 7 Out[58]: 
 8 cty          US                            JP          
 9 tenor         1         3         5         1         3
10 0      1.641749  2.434674 -0.546666  0.797418  0.530019
11 1      0.084086  0.309776 -0.322581  1.996448 -0.093791
12 2      1.387329 -0.200419 -0.182946 -0.811081  1.081501
13 3     -0.237261  0.288679 -0.057882  0.267184  0.907478
14 
15 
16 #通过level关键字传入级别编号或名称
17 hier_df.groupby(level='cty',axis=1).count()
18 Out[59]: 
19 cty  JP  US
20 0     2   3
21 1     2   3
22 2     2   3
23 3     2   3

 

posted @ 2018-07-24 15:19  平淡才是真~~  阅读(2320)  评论(0编辑  收藏  举报