在对比较大的数据进行分析时,一项基本的工作就是有效的数据累计(summarization):计算累计(aggragation)指标,如sum()、mean()、median()、min()和max(),其中每一项指标都呈现出大数据集的特征。
简单的累计计算是对DataFrame或者Series对象使用累计函数:sum()、mean()、median()、min()和max()等。设数据集为df,那么简单的累计计算语法为df.agg_func()
,pandas为Dataframe提供了describe()方法可以一次性计算出每一列上的若干常用统计值。
#数据:Seaborn数据库中的行星数据
import pandas as pd
import seaborn as sns
import numpy as np
planets = sns.load_dataset('planets')
planets.describe()
Out[79]:
number orbital_period ... distance year
count 1035.000000 992.000000 ... 808.000000 1035.000000
mean 1.785507 2002.917596 ... 264.069282 2009.070531
std 1.240976 26014.728304 ... 733.116493 3.972567
min 1.000000 0.090706 ... 1.350000 1989.000000
25% 1.000000 5.442540 ... 32.560000 2007.000000
50% 1.000000 39.979500 ... 55.250000 2010.000000
75% 2.000000 526.005000 ... 178.500000 2012.000000
max 7.000000 730000.000000 ... 8500.000000 2014.000000
[8 rows x 5 columns]
这些简单的累计方法默认都是对列进行统计,如果想要统计行的数据的特性,可以加上参数axis=1
#在列的方向上聚合
df.agg_func(axis=1)
GroupBy简单来看就是Pandas为DataFrame提供的一个分组的工具,通过df.groupby('key')
可以得到一个DataFrameGroupBy对象,你可以将它看作是一种特殊形式的DataFrame,里面隐藏着若干组数据,但是在没有应用聚合操作之前不会计算。这种“延迟计算”的方法使得大多数常见的累计计算操作可以通过一种对用户而言几乎透明的方式非常高效的实现。
GroupBy的过程如下:
- 分割:将DataFrame按照指定的键分割成若干组
- 应用:对每个组应用聚合函数,通常是累计、转换或过滤函数
- 组合:将每个组的结果合并成一个输出数组
对分组后指定列的数据使用简单累计函数
#对数据集在method列上进行分组,然后求各组中行星轨道周期的中位数
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
按组迭代
#获取分组后各组的信息
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)
调用方法
借用python类的魔力(@classmethod),可以让GroupBy对象不直接实现的方法应用到每一组,比如你可以对每组数据都调用DataFrame对象才能使用的describe()方法。
planets.groupby('method')['year'].describe()
#输出结果:
count mean ... 75% max
method ...
Astrometry 2.0 2011.500000 ... 2012.25 2013.0
Eclipse Timing Variations 9.0 2010.000000 ... 2011.00 2012.0
Imaging 38.0 2009.131579 ... 2011.00 2013.0
Microlensing 23.0 2009.782609 ... 2012.00 2013.0
Orbital Brightness Modulation 3.0 2011.666667 ... 2012.00 2013.0
Pulsar Timing 5.0 1998.400000 ... 2003.00 2011.0
Pulsation Timing Variations 1.0 2007.000000 ... 2007.00 2007.0
Radial Velocity 553.0 2007.518987 ... 2011.00 2014.0
Transit 397.0 2011.236776 ... 2013.00 2014.0
Transit Timing Variations 4.0 2012.500000 ... 2013.25 2014.0
[10 rows x 8 columns]
累计(aggregate)
GroupBy对象的aggregate()方法可以支持复杂的累计操作,比如字符串、函数或者函数列表,并且能一次性计算出所有累计值。
#数据
df = pd.DataFrame({'key':['A','B','C','A','B','C'],
'data1':range(6),
'data2':np.random.randint(0,10,6)},
columns = ['key', 'data1', 'data2'])
#aggregate()函数的参数可以支持字符串、函数或者函数列表
df.groupby('key').aggregate(['min', np.median, max])
#输出结果:
data1 data2
min median max min median max
key
A 0 1.5 3 3 5.5 8
B 1 2.5 4 7 7.5 8
C 2 3.5 5 0 2.0 4
过滤(filter)
过滤操作可以让你按照分组的属性丢弃若干数据,例如,我们可能只需要保留标准差超过某个阀值的组:
#定义过滤函数
def filter_func(x):
return x['data2'].std()>2
#假如某组的经过过滤函数后的返回值为False的话就将其过滤
df.groupby('key').filter(filter_func)
#输出结果
key data1 data2
0 A 0 8
2 C 2 0
3 A 3 3
5 C 5 4
转换
累计操作返回的是对组内数据缩减过得结果,而转换操作会返回一个新的全量数据。常见的操作是将每一组的样本数据减去各组的均值,实现数据标准化
df.groupby('key').transform(lambda x:x-x.mean())
#输出结果:
Out[101]:
data1 data2
0 -1.5 2.5
1 -1.5 0.5
2 -1.5 -2.0
3 1.5 -2.5
4 1.5 -0.5
5 1.5 2.0
apply()方法
apply()方法让你可以在每个组上那个应用任意方法。这个函数输入一个DataFrame,返回一个Pandas对象(DataFrame或者Series)或一个标量(Scalar,单个数值)。组合操作会适应返回结果。
#将第一列数据以第二列的和为基数进行标准化
def norm_by_data2(x):
#x是一个分组数据的DataFrame
x['data1'] /= x['data2'].sum()
return x
df.groupby('key').apply(norm_by_data2)
#输出结果:
Out[102]:
key data1 data2
0 A 0.000000 8
1 B 0.066667 8
2 C 0.500000 0
3 A 0.272727 3
4 B 0.266667 7
5 C 1.250000 4
分组的键除了DataFrame的列之外,还有需要其他的方法。
将列表、数组、Series或索引作为分组键
#将列表作为分组键,下面列表中,所有0、1、2对应的索引所在行将被分到一组
L = [0, 1, 0, 1, 2, 0]
df.groupby(L).sum()
#输出结果:
Out[103]:
data1 data2
0 7 12
1 4 11
2 4 7
#对列的内容处理后作为分组键,下面代码目的是获取不同方法和不同年份发现的行星数量
decade = 10 * (planets['year'] // 10)
decade = decade.astype(str) + 's'
decade.name = 'decade'
planets.groupby(['method', decade])['number'].sum().unstack().fillna(0)
#输出结果:
decade 1980s 1990s 2000s 2010s
method
Astrometry 0.0 0.0 0.0 2.0
Eclipse Timing Variations 0.0 0.0 5.0 10.0
Imaging 0.0 0.0 29.0 21.0
Microlensing 0.0 0.0 12.0 15.0
Orbital Brightness Modulation 0.0 0.0 0.0 5.0
Pulsar Timing 0.0 9.0 1.0 1.0
Pulsation Timing Variations 0.0 0.0 1.0 0.0
Radial Velocity 1.0 52.0 475.0 424.0
Transit 0.0 0.0 64.0 712.0
Transit Timing Variations 0.0 0.0 0.0 9.0
用字典或Series将索引映射到分组名称
#将索引映射到分组键
df2 = df.set_index('key')
mapping = {'A':'vowel', 'B':'consonant', 'C':'consonant'}
df2.groupby(mapping).sum()
#输出结果:
data1 data2
consonant 12 19
vowel 3 11
任意的python函数
#与前面的字典映射类似,你可以将任意python函数传入groupby,函数映射到索引,然后新的分组输出
df2.groupby(str.lower).mean()
#输出结果:
data1 data2
a 1.5 5.5
b 2.5 7.5
c 3.5 2.0
多个有效键构成的列表
#任意之前的有效的键可以组合起来进行分组,从而返回一个多级索引的分组结果
df2.groupby([str.lower, mapping]).mean()
#输出结果:
data1 data2
a vowel 1.5 5.5
b consonant 2.5 7.5
c consonant 3.5 2.0