In [2]:
import pandas as pd
import numpy as np

GroupBy: split-apply-combine

"group by"操作通常涉及下面几个步骤:

  • Splitting 分组,将数据按照某种指标分为多组
  • Applying 对每一组数据都应用一个函数,进行转换
  • Combining 将各个组的转换结果合并

其中applying通常是下面三个类型的函数:

  • 集成函数:计算每组数据的统计指标,比如sum、means、size、count
  • 转换函数:对每组数据进行标准化,填充NAN值
  • 过滤函数:

groupby和SQL中的GROUP BY很像:


In [ ]:
SELECT Column1, Column2, mean(Column3), sum(Column4)
FROM SomeTable
GROUP BY Column1, Column2

Split:对数据分组


In [ ]:
# 默认按照axis=0分组
grouped = obj.groupby(key)
grouped = obj.groupby(key, axis=1)
grouped = obj.groupby([key1, key2])

In [4]:
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'foo'],
                   'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
                   'C': np.random.randn(8),
                   'D': np.random.randn(8)})

In [5]:
df


Out[5]:
A B C D
0 foo one -0.925271 0.961582
1 bar one -0.365797 -0.636498
2 foo two -1.919952 0.501423
3 bar three -0.462709 0.864412
4 foo two -0.558642 0.964123
5 bar two -1.277091 -0.130611
6 foo one -1.040990 0.181257
7 foo three -0.616584 -0.132354

默认按行分割


In [7]:
grouped = df.groupby('A')
grouped = df.groupby(['A', 'B'])

按列分割

把DataFrame按照列分组,比如第一列为一组,第二、三列为另一组。相比于按行分组,按列分组用处不大。


In [22]:
def get_letter_type(letter):
    if letter.lower() in 'aeiou':
        return 'vowel'
    else:
        return 'consonant'
grouped = df.groupby(get_letter_type, axis=1)

In [9]:
grouped


Out[9]:
<pandas.core.groupby.DataFrameGroupBy object at 0x1139deb10>

GroupBy 排序

groupby的结果默认会对group key排序,可以通过参数sort=False来组织排序,提高性能:


In [10]:
df2 = pd.DataFrame({'X': ['B', 'B', 'A', 'A'], 'Y': [1, 2, 3, 4]})

In [11]:
df2.groupby('X').sum()


Out[11]:
Y
X
A 7
B 3

In [13]:
df2.groupby('X', sort=False).sum()


Out[13]:
Y
X
B 3
A 7

分组后,各个组内数据的顺序和原来DataFrame内数据顺序一致。


In [14]:
df3 = pd.DataFrame({'X': ['A', 'B', 'A', 'B'], 'Y': [1, 4, 3, 2]})

In [15]:
df3.groupby('X').get_group('A')


Out[15]:
X Y
0 A 1
2 A 3

In [17]:
df3.groupby('X').get_group('B')


Out[17]:
X Y
1 B 4
3 B 2

GroupBy 对象属性

groups属性是一个字典,每个key就是group key


In [20]:
df.groupby('A').groups


Out[20]:
{'bar': Int64Index([1, 3, 5], dtype='int64'),
 'foo': Int64Index([0, 2, 4, 6, 7], dtype='int64')}

In [21]:
df.groupby(get_letter_type, axis=1).groups


Out[21]:
{'consonant': Index([u'B', u'C', u'D'], dtype='object'),
 'vowel': Index([u'A'], dtype='object')}

可以对grouped调用len()函数,会得到gruops这个字典的大小


In [23]:
grouped = df.groupby(['A', 'B'])

In [24]:
grouped.groups


Out[24]:
{('bar', 'one'): Int64Index([1], dtype='int64'),
 ('bar', 'three'): Int64Index([3], dtype='int64'),
 ('bar', 'two'): Int64Index([5], dtype='int64'),
 ('foo', 'one'): Int64Index([0, 6], dtype='int64'),
 ('foo', 'three'): Int64Index([7], dtype='int64'),
 ('foo', 'two'): Int64Index([2, 4], dtype='int64')}

In [25]:
len(grouped)


Out[25]:
6

想查看groupby的属性,按TAB


In [ ]:
gb.<TAB>

迭代访问各组数据


In [26]:
grouped = df.groupby('A')

In [28]:
for name, group in grouped:
    print(name)
    print(group)


bar
     A      B         C         D
1  bar    one -0.365797 -0.636498
3  bar  three -0.462709  0.864412
5  bar    two -1.277091 -0.130611
foo
     A      B         C         D
0  foo    one -0.925271  0.961582
2  foo    two -1.919952  0.501423
4  foo    two -0.558642  0.964123
6  foo    one -1.040990  0.181257
7  foo  three -0.616584 -0.132354

如果根据多个key进行groupby,则name类型是tuple:


In [29]:
for name, group in df.groupby(['A', 'B']):
    print(name)
    print(group)


('bar', 'one')
     A    B         C         D
1  bar  one -0.365797 -0.636498
('bar', 'three')
     A      B         C         D
3  bar  three -0.462709  0.864412
('bar', 'two')
     A    B         C         D
5  bar  two -1.277091 -0.130611
('foo', 'one')
     A    B         C         D
0  foo  one -0.925271  0.961582
6  foo  one -1.040990  0.181257
('foo', 'three')
     A      B         C         D
7  foo  three -0.616584 -0.132354
('foo', 'two')
     A    B         C         D
2  foo  two -1.919952  0.501423
4  foo  two -0.558642  0.964123

选择某个组的数据

使用get_group()函数:


In [31]:
grouped.get_group('bar')


Out[31]:
A B C D
1 bar one -0.365797 -0.636498
3 bar three -0.462709 0.864412
5 bar two -1.277091 -0.130611

In [32]:
df.groupby(['A', 'B']).get_group(('bar', 'one'))


Out[32]:
A B C D
1 bar one -0.365797 -0.636498

数据集成

一旦数据分组完成,就可以对各个组的数据应用某个函数了,典型的例子是调用aggregate/agg:


In [33]:
grouped = df.groupby('A')

In [34]:
grouped.aggregate(np.sum)


Out[34]:
C D
A
bar -2.105597 0.097302
foo -5.061439 2.476032

In [37]:
for name, group in grouped:
    print(name)
    print(group)


bar
     A      B         C         D
1  bar    one -0.365797 -0.636498
3  bar  three -0.462709  0.864412
5  bar    two -1.277091 -0.130611
foo
     A      B         C         D
0  foo    one -0.925271  0.961582
2  foo    two -1.919952  0.501423
4  foo    two -0.558642  0.964123
6  foo    one -1.040990  0.181257
7  foo  three -0.616584 -0.132354

可以看到,集成后的数据包含组名,并且组名作为行索引。如果groupby分组时有多个group key,集成后数据默认含有层级索引,可以通过as_index=False参数来去掉索引:


In [46]:
grouped = df.groupby(['A', 'B'], as_index=False)

In [47]:
grouped.aggregate(np.sum)


Out[47]:
A B C D
0 bar one -0.365797 -0.636498
1 bar three -0.462709 0.864412
2 bar two -1.277091 -0.130611
3 foo one -1.966261 1.142840
4 foo three -0.616584 -0.132354
5 foo two -2.478594 1.465546

In [48]:
df.groupby('A', as_index=False).sum()


Out[48]:
A C D
0 bar -2.105597 0.097302
1 foo -5.061439 2.476032

除了使用as_index参数,也可以使用reset_index()去掉组名构成的多级索引:


In [50]:
df.groupby(['A', 'B']).sum().reset_index()


Out[50]:
A B C D
0 bar one -0.365797 -0.636498
1 bar three -0.462709 0.864412
2 bar two -1.277091 -0.130611
3 foo one -1.966261 1.142840
4 foo three -0.616584 -0.132354
5 foo two -2.478594 1.465546

In [52]:
grouped.size() # 返回各个组的size


Out[52]:
A    B    
bar  one      1
     three    1
     two      1
foo  one      2
     three    1
     two      2
dtype: int64

一次应用多个函数


In [54]:
grouped = df.groupby('A')

In [55]:
grouped['C'].agg([np.sum, np.mean, np.std])


Out[55]:
sum mean std
A
bar -2.105597 -0.701866 0.500511
foo -5.061439 -1.012288 0.546495

In [56]:
grouped.agg([np.sum, np.mean, np.std])


Out[56]:
C D
sum mean std sum mean std
A
bar -2.105597 -0.701866 0.500511 0.097302 0.032434 0.763623
foo -5.061439 -1.012288 0.546495 2.476032 0.495206 0.482137

对DataFrame不同列应用不同的函数


In [57]:
grouped.agg({'C': np.sum,
             'D': lambda x: np.std(x, ddof=1)})


Out[57]:
C D
A
bar -2.105597 0.763623
foo -5.061439 0.482137

Cython优化过的数据集成函数

一些常用的数据集成函数,比如sum、mean、std、sem,都有优化过的Cython实现:


In [58]:
df.groupby('A').sum()


Out[58]:
C D
A
bar -2.105597 0.097302
foo -5.061439 2.476032

In [59]:
df.groupby(['A', 'B']).mean()


Out[59]:
C D
A B
bar one -0.365797 -0.636498
three -0.462709 0.864412
two -1.277091 -0.130611
foo one -0.983130 0.571420
three -0.616584 -0.132354
two -1.239297 0.732773

In [ ]:


In [ ]:

数据转换函数


In [ ]: