Skip to content

Python | Pandas | Aggregation & Grouping

Posted on:February 18, 2019

注意,虽然累计也有单独使用的场景(前几节介绍的内容),但更重要的用法是配合分组进行各组内部的累计——可以说aggregation和grouping是一对孪生兄弟。

使用Seaborn库中的一份行星数据:

# 导入seaborn库,获取该数据集,打印形状
import seaborn as sns
planets = sns.load_dataset('planets')
planets.shape
(1035, 6)
# 查看前5行数据
planets.head()
method number orbital_period mass distance year
0 Radial Velocity 1 269.300 7.10 77.40 2006
1 Radial Velocity 1 874.774 2.21 56.95 2008
2 Radial Velocity 1 763.000 2.60 19.84 2011
3 Radial Velocity 1 326.030 19.40 110.62 2007
4 Radial Velocity 1 516.220 10.50 119.47 2009

Table of contents

1. 回顾:pandas的简单Aggregate功能

1.1 Series累计

ser = pd.Series(np.arange(10,16))
ser
0    10
1    11
2    12
3    13
4    14
5    15
dtype: int64
# 求和
ser.sum()
75
# 求均值
ser.mean()
12.5

1.2 Dataframe累计

Dataframe默认对每列进行累计,即默认axis=0;可以修改axis=1使累计在每行进行。

df = pd.DataFrame({'A':np.arange(5,10),
                  'B':np.arange(10,15)})
df
A B
0 5 10
1 6 11
2 7 12
3 8 13
4 9 14
# 默认对列
df.sum()
A    35
B    60
dtype: int64
# 修改为行
df.sum(axis=1)
0    15
1    17
2    19
3    21
4    23
dtype: int64

1.3 describe常用统计值

describe() 函数可以计算每一列的若干常用统计值。

planets.describe()
number orbital_period mass distance year
count 1035.000000 992.000000 513.000000 808.000000 1035.000000
mean 1.785507 2002.917596 2.638161 264.069282 2009.070531
std 1.240976 26014.728304 3.818617 733.116493 3.972567
min 1.000000 0.090706 0.003600 1.350000 1989.000000
25% 1.000000 5.442540 0.229000 32.560000 2007.000000
50% 1.000000 39.979500 1.260000 55.250000 2010.000000
75% 2.000000 526.005000 3.040000 178.500000 2012.000000
max 7.000000 730000.000000 25.000000 8500.000000 2014.000000

2. Groupby:分割, 应用, 组合

有时我们需要对某些标签或索引的局部进行累计分析。groupby函数可以实现lazy分割,得到groupby对象;再对groupby对象进行函数操作时,就等价于将函数操作分别作用于分割出的每一个组,并输出合并的结果。此过程称为split-apply-combine

2.1 Split-Apply-Combine

df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'data': range(6)}, columns=['key', 'data'])
df
key data
0 A 0
1 B 1
2 C 2
3 A 3
4 B 4
5 C 5
df.groupby('key')
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fae291ef400>

groupby 返回一个DataFrameGroupBy对象,其中“隐藏”着若干组数据,但在没有应用累计函数之前是不会计算的——延迟计算(lazy evalution):

df.groupby('key').sum()
data
key
A 3
B 5
C 7

2.2 Groupby对象的基本操作方法

Groupby 对象可以被看做是分割得到的Dataframe的集合。

1)按列取值

依据method列进行分组,只输出orbital_period的中位数:

planets.groupby('method')['orbital_period'].median()
method
Astrometry                         631.180000
Eclipse Timing Variations         4343.500000
Imaging                          27500.000000
Microlensing                      3300.000000
Orbital Brightness Modulation        0.342887
Pulsar Timing                       66.541900
Pulsation Timing Variations       1170.000000
Radial Velocity                    360.200000
Transit                              5.714932
Transit Timing Variations           57.011000
Name: orbital_period, dtype: float64

2)按组迭代

Groupby 对象支持直接按组进行迭代

for (method, group) in planets.groupby('method'):
    print("{0:30s} shape={1}".format(method, group.shape))
Astrometry                     shape=(2, 6)
Eclipse Timing Variations      shape=(9, 6)
Imaging                        shape=(38, 6)
Microlensing                   shape=(23, 6)
Orbital Brightness Modulation  shape=(3, 6)
Pulsar Timing                  shape=(5, 6)
Pulsation Timing Variations    shape=(1, 6)
Radial Velocity                shape=(553, 6)
Transit                        shape=(397, 6)
Transit Timing Variations      shape=(4, 6)

3)调用方法

planets.groupby('method')['year'].describe()
count mean std min 25% 50% 75% max
method
Astrometry 2.0 2011.500000 2.121320 2010.0 2010.75 2011.5 2012.25 2013.0
Eclipse Timing Variations 9.0 2010.000000 1.414214 2008.0 2009.00 2010.0 2011.00 2012.0
Imaging 38.0 2009.131579 2.781901 2004.0 2008.00 2009.0 2011.00 2013.0
Microlensing 23.0 2009.782609 2.859697 2004.0 2008.00 2010.0 2012.00 2013.0
Orbital Brightness Modulation 3.0 2011.666667 1.154701 2011.0 2011.00 2011.0 2012.00 2013.0
Pulsar Timing 5.0 1998.400000 8.384510 1992.0 1992.00 1994.0 2003.00 2011.0
Pulsation Timing Variations 1.0 2007.000000 NaN 2007.0 2007.00 2007.0 2007.00 2007.0
Radial Velocity 553.0 2007.518987 4.249052 1989.0 2005.00 2009.0 2011.00 2014.0
Transit 397.0 2011.236776 2.077867 2002.0 2010.00 2012.0 2013.00 2014.0
Transit Timing Variations 4.0 2012.500000 1.290994 2011.0 2011.75 2012.5 2013.25 2014.0

2.3 累计(aggregate), 过滤(filter), 转换(transform), 应用(apply)

Groupby 对象的一些重要方法:

# 数据准备
rng = np.random.RandomState(0)
df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'data1': range(6),
                   'data2': rng.randint(0, 10, 6)},
                   columns = ['key', 'data1', 'data2'])
df
key data1 data2
0 A 0 5
1 B 1 0
2 C 2 3
3 A 3 3
4 B 4 7
5 C 5 9

1)累计

aggregate()支持更复杂的操作,如字符串、函数等,并且能一次性计算所有累计值;

df.groupby('key').aggregate(['min',max,np.median])
data1 data2
min max median min max median
key
A 0 3 1.5 3 5 4.0
B 1 4 2.5 0 7 3.5
C 2 5 3.5 3 9 6.0
df.groupby('key').aggregate({'data1':['min', np.median],
                            'data2':'max'})
data1 data2
min median max
key
A 0 1.5 5
B 1 2.5 7
C 2 3.5 9

2)过滤

# 判断函数
# 参数x是一个组,即dataframe
def filter_func(x):
    return x['data2'].std() > 4
df.groupby('key').std()
data1 data2
key
A 2.12132 1.414214
B 2.12132 4.949747
C 2.12132 4.242641
# 将过滤函数应用于每个lazy组中
# 不符合条件(False)的lazy组被筛掉。
df.groupby('key').filter(filter_func)
key data1 data2
1 B 1 0
2 C 2 3
4 B 4 7
5 C 5 9

3)转换

转换,形状与原来的输入是一样的。

df.groupby('key').transform(lambda x:x-x.mean())
data1 data2
0 -1.5 1.0
1 -1.5 -3.5
2 -1.5 -3.0
3 1.5 -1.0
4 1.5 3.5
5 1.5 3.0

4)apply方法

apply()方法让你可以在每个组上应用任意方法:

# 定义函数
# 参数x是一个组,即dataframe
def norm_by_data2(x):
    # x是一个分组数据的Dataframe
    x['data1'] /= x['data2'].sum()
    return x

df.groupby('key').apply(norm_by_data2)
key data1 data2
0 A 0.000000 5
1 B 0.142857 0
2 C 0.166667 3
3 A 0.375000 3
4 B 0.571429 7
5 C 0.416667 9

2.4 设置分割的键(不太会用得到)

df
key data1 data2
0 A 0 5
1 B 1 0
2 C 2 3
3 A 3 3
4 B 4 7
5 C 5 9

1)将列表、数组、Series或索引作为分组键

分组键的长度需要与df匹配。

# L作为新的一列,成为key列
L = [0,1,0,1,2,0]
df.groupby(L).sum()
data1 data2
0 7 17
1 4 3
2 4 7

2)用字典或Series将索引映射到分组名称

df2 = df.set_index('key')
df2
data1 data2
key
A 0 5
B 1 0
C 2 3
A 3 3
B 4 7
C 5 9
mapping = {'A':'vowel', 'B':'consonant', 'C':'consonant'}
df2.groupby(mapping).sum()
data1 data2
key
consonant 12 19
vowel 3 8

3)任意Python函数

df2.groupby(str.lower).mean()
data1 data2
key
a 1.5 4.0
b 2.5 3.5
c 3.5 6.0

4)多个有效键组成的列表

df2.groupby([str.lower, mapping]).mean()
data1 data2
key key
a vowel 1.5 4.0
b consonant 2.5 3.5
c consonant 3.5 6.0
df2.groupby([mapping, str.lower]).mean()
data1 data2
key key
consonant b 2.5 3.5
c 3.5 6.0
vowel a 1.5 4.0