对大数据进行分析的时候,⼀项基本工作就是数据累计(summarization),通常包括:
- sum: 求和
- mean:平均数
- median:中位数
- min:最小值
- max:最大值
- count:计数
- first/last: 第⼀项和最后⼀项
- std: 标准差
- var:方差
- mad:均值绝对方差
- prod:所有项乘积
# 准备数据
# 我们这次准备数据时候⽤seaborn提供的⾏星数据,包括天⽂学家观测到的围绕恒星运⾏的⾏星数据
# 下载地址:
# https://github.com/mwaskom/seaborn-data
import seaborn as sns
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
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
GroupBy
GroupBy借用的是SQL的命令, 其核心思想是:
- 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
GroupBy对象
GroupBy返回的结果是⼀个抽象类型,可以看组是⼀个DataFrame的集合。
按列取值返回的结果
# 按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
按组迭代
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)
调用方法
借助于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
累计,过滤和转换
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
累计
相比较于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
过滤
通过过滤功能,可以保留我们需要的值,把不需要的去掉。
# 过滤函数
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
转换
累计操作把数据集合进行了裁剪和选择,而转换操作是把全量数据进行加工,得到的数据格式与输入数据⼀致,常见的操作是对数据减去均值,实现数据标准化。
# 对数据进⾏标准化
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
应用
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
设置分割的键
对DataFrame的分割可以根据列来,也还有其他的方法。
将列表,数组,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
用字典或者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
使用任意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
多个有效的键组成的列表
有效的键值可以组合起来,从而返回⼀个多级索引的分组结果。
# 利⽤上⾯定义的字典,我们可以组合成多级索引
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