7. 累计和分组¶
对大数据进行分析的时候,一项基本工作就是数据累计(summarization),通常包括:
- sum: 求和
- mean:平均数
- median:中位数
- min:最小值
- max:最大值
- count:计数
- first/last: 第一项和最后一项
- std: 标准差
- var:方差
- mad:均值绝对方差
- prod:所有项乘积
# 准备数据
# 我们这次准备数据时候用seaborn提供的行星数据,包括天文学家观测到的围绕恒星运行的行星数据
# 下载地址:
# https://github.com/mwaskom/seaborn-data
# Github的原因,下载很慢,可能会出现传输链接错误,需要多试几遍才行
import seaborn as sns
# Planets是行星观测数据,包括观测方法,数量,轨道周期等待
planets = sns.load_dataset('planets')
print(planets.shape)
# 显示数据头部
print(planets.head())
(1035, 6)
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
7.1. describe函数¶
describe函数计算每一列的常用统计值,给出一个比较概括性的数值。
通过下面describe计算的数据,我们可以对数据总体做出一个大概的判断,比如各个列的均值,最大值,均值等等。
在统计之前,我们需要删除掉缺失值以免影响最后结果。
d = planets.dropna().describe()
print(d)
number orbital_period mass distance year
count 498.00000 498.000000 498.000000 498.000000 498.000000
mean 1.73494 835.778671 2.509320 52.068213 2007.377510
std 1.17572 1469.128259 3.636274 46.596041 4.167284
min 1.00000 1.328300 0.003600 1.350000 1989.000000
25% 1.00000 38.272250 0.212500 24.497500 2005.000000
50% 1.00000 357.000000 1.245000 39.940000 2009.000000
75% 2.00000 999.600000 2.867500 59.332500 2011.000000
max 6.00000 17337.500000 25.000000 354.000000 2014.000000
7.2. GroupBy¶
GroupBy借用的是SQL的命令,通过GroupBy可以对数据进行灵活的几乎各种操作, 其核心思想是:
- split:分割
- applay:应用
- combine:组合
通过运用GroupBy命令和不同的累计函数进行组合使用,对某些标签或索引进行累计分析。
# 小案例
import pandas as pd
df = pd.DataFrame({"name":list("ABCABC"), "data":range(100,106)}, columns=["name", "data"])
print("df = \n", df)
# 按name列进行分组,然后求魅族的平均数
a = df.groupby("name").mean()
print("\n a = \n", a)
df =
name data
0 A 100
1 B 101
2 C 102
3 A 103
4 B 104
5 C 105
a =
data
name
A 101.5
B 102.5
C 103.5
7.3. GroupBy对象¶
GroupBy返回的结果是一个抽象类型,可以看组是一个DataFrame的集合。
7.3.1. 按列取值返回的结果¶
# 按method列进行组合
gb = planets.groupby("method")
# gb是一个抽象数据类型
print(gb)
# 对GroupBy结果还可以再次抽取数
print(gb["orbital_period"])
<pandas.core.groupby.groupby.DataFrameGroupBy object at 0x7f755623f5f8>
<pandas.core.groupby.groupby.SeriesGroupBy object at 0x7f755623fa90>
# 下面句子相当于按找method进行组合,然后选取orbital_period列,选取后求每个组的中位数
a = planets.groupby("method")['orbital_period'].median()
print("组合后统计结果: \n", a)
组合后统计结果:
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
7.3.2. 按组迭代¶
GroupBy对象支持进行迭代,返回每一组都是Series或者DataFrame数据。
# 迭代回来的数据大概是每组以method作为名称,多行六列的一个DataFrame
for (method, group) in planets.groupby("method"):
print(method, group.shape)
Astrometry (2, 6)
Eclipse Timing Variations (9, 6)
Imaging (38, 6)
Microlensing (23, 6)
Orbital Brightness Modulation (3, 6)
Pulsar Timing (5, 6)
Pulsation Timing Variations (1, 6)
Radial Velocity (553, 6)
Transit (397, 6)
Transit Timing Variations (4, 6)
7.3.3. 调用方法¶
借助于Python强大的类方法(@classmethod), 可以直接对GroupBy的每一组对象添加功能,无论是DataFrame或者Series都可以使用。
a = planets.groupby("method")['year'].describe().unstack()
print(a)
method
count Astrometry 2.000000
Eclipse Timing Variations 9.000000
Imaging 38.000000
Microlensing 23.000000
Orbital Brightness Modulation 3.000000
Pulsar Timing 5.000000
Pulsation Timing Variations 1.000000
Radial Velocity 553.000000
Transit 397.000000
Transit Timing Variations 4.000000
mean Astrometry 2011.500000
Eclipse Timing Variations 2010.000000
Imaging 2009.131579
Microlensing 2009.782609
Orbital Brightness Modulation 2011.666667
Pulsar Timing 1998.400000
Pulsation Timing Variations 2007.000000
Radial Velocity 2007.518987
Transit 2011.236776
Transit Timing Variations 2012.500000
std Astrometry 2.121320
Eclipse Timing Variations 1.414214
Imaging 2.781901
Microlensing 2.859697
Orbital Brightness Modulation 1.154701
Pulsar Timing 8.384510
Pulsation Timing Variations NaN
Radial Velocity 4.249052
Transit 2.077867
Transit Timing Variations 1.290994
...
50% Astrometry 2011.500000
Eclipse Timing Variations 2010.000000
Imaging 2009.000000
Microlensing 2010.000000
Orbital Brightness Modulation 2011.000000
Pulsar Timing 1994.000000
Pulsation Timing Variations 2007.000000
Radial Velocity 2009.000000
Transit 2012.000000
Transit Timing Variations 2012.500000
75% Astrometry 2012.250000
Eclipse Timing Variations 2011.000000
Imaging 2011.000000
Microlensing 2012.000000
Orbital Brightness Modulation 2012.000000
Pulsar Timing 2003.000000
Pulsation Timing Variations 2007.000000
Radial Velocity 2011.000000
Transit 2013.000000
Transit Timing Variations 2013.250000
max Astrometry 2013.000000
Eclipse Timing Variations 2012.000000
Imaging 2013.000000
Microlensing 2013.000000
Orbital Brightness Modulation 2013.000000
Pulsar Timing 2011.000000
Pulsation Timing Variations 2007.000000
Radial Velocity 2014.000000
Transit 2014.000000
Transit Timing Variations 2014.000000
Length: 80, dtype: float64
7.4. 累计,过滤和转换¶
GroupBy基本功能是分组,但分组之后相应的操作,为数据分析提供了很多高效的方法。 此类方法大概分为:
- aggregate:累计
- filter:过滤
- transform:变换
- apply:应用
## 数据准备
import numpy as np
import pandas as pd
rng = np.random.RandomState(0)
df = pd.DataFrame({'key':list("ABCABC"),
'data_1':range(100,106),
'data_2': rng.randint(0,10, 6)},
columns=['key', 'data_1', 'data_2'])
print("df = \n", df)
df =
key data_1 data_2
0 A 100 5
1 B 101 0
2 C 102 3
3 A 103 3
4 B 104 7
5 C 105 9
7.4.1. 累计¶
相比较于sum和median之类的功能,累计(aggregate)能实现比较复杂的操作,代表操作的参数可以是字符串,函数或者函数列表,并且能一次性计算所有累计值。
# 统计min,median,max
# 作为代表功能的参数,都放入一个列表中,可以是字符串,函数名等
rst = df.groupby('key').aggregate(['min', np.median, np.max])
print(rst)
data_1 data_2
min median amax min median amax
key
A 100 101.5 103 3 4.0 5
B 101 102.5 104 0 3.5 7
C 102 103.5 105 3 6.0 9
# 或者还有其他的方法,对每列数据用不同的统计函数
rst = df.groupby('key').aggregate({'data_1':'min',
'data_2':'max'})
print(rst)
data_1 data_2
key
A 100 5
B 101 7
C 102 9
7.4.2. 过滤¶
通过过了功能,可以保留我们需要的值,把不需要的去掉。
# 过滤函数
def my_filter(x):
return x['data_2'].std() > 1.5
rst = df.groupby('key').std()
print('df.std = \n', rst)
# 使用过滤函数,把不符合要求的过滤掉
rst = df.groupby('key').filter(my_filter)
print("\n rst.filter_func = \n", rst)
df.std =
data_1 data_2
key
A 2.12132 1.414214
B 2.12132 4.949747
C 2.12132 4.242641
rst.filter_func =
key data_1 data_2
1 B 101 0
2 C 102 3
4 B 104 7
5 C 105 9
7.4.3. 转换¶
累计操作把数据集合进行了裁剪和选择,而转换操作是把全量数据进行加工,得到的数据格式与输入数据一致,常见的操作是对数据减去均值,实现数据标准化。
# 对数据进行标准化
rst = df.groupby('key').transform(lambda x: x - x.mean())
print(rst)
data_1 data_2
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
7.4.4. 应用¶
apply可以让人在每个数据上应用任意方法,这个函数让输入一个DataFrane,返回的结果可以是pandas对象或者标量。
# 求每一项的百分比
def norm_data_1(x):
# 求百分比
x['data_1'] = x['data_1'] / x['data_1'].sum()
return x
rst = df.groupby('key').apply(norm_data_1)
print(rst)
key data_1 data_2
0 A 0.492611 5
1 B 0.492683 0
2 C 0.492754 3
3 A 0.507389 3
4 B 0.507317 7
5 C 0.507246 9
7.5. 设置分割的键¶
对DataFrame的分割可以根据列来,还可以有其他的方法,本节主要介绍对DataFrame的各种分割方法。
7.5.1. 将列表,数组,Series或者索引作为分组键¶
此时分组键可以是与DaytaFrame匹配的任意Series或者列表。
# 普通数组作为分组键
L = [0,1,1,0,2,1]
a = df.groupby(L).sum()
print(a)
data_1 data_2
0 203 8
1 308 12
2 104 7
# 直接用键值也可以,比较啰嗦
a = df.groupby(df['key']).sum()
print(a)
data_1 data_2
key
A 203 8
B 205 7
C 207 12
7.5.2. 用字典或者Series将索引映射到分组的名称¶
提供字典,按照字典的键值进行分组,最后结果使用字典键值映射的值。
要求索引必须跟字典的键值匹配。
# 利用字典分组
D_mapping = {'A':"One", 'B':"Two", 'C':"Three"}
df = df.set_index('key')
a = df.groupby(D_mapping).sum()
print(a)
data_1 data_2
One 203 8
Three 207 12
Two 205 7
7.5.3. 使用任意Python函数¶
我们还可以把Python函数传入groupby,与前面的内容类似,然后得到新的分组。
# 传入任意分组
a = df.groupby(str.lower).mean()
print(a)
data_1 data_2
a 101.5 4.0
b 102.5 3.5
c 103.5 6.0
7.5.4. 多个有效的键组成的列表¶
有效的键值可以组合起来,从而返回一个多级索引的分组结果。
# 利用上面定义的字典,我们可以组合成多级索引
a = df.groupby([str.lower, D_mapping]).mean()
print(a)
data_1 data_2
a One 101.5 4.0
b Two 102.5 3.5
c Three 103.5 6.0