第九章、数据聚合与分组计算
9.1 groupby技术(groupby and agg)
“拆分split——应用apply——合并conbine”的过程。
分组键的形式:
- 列表或数组,其长度和待分组的轴一样
- 表示DataFrame某个列名的值
- 字典或Series,给出待分组的轴上的值与分组名之间的对应关系
-
函数,用于处理轴索引或索引中的各个标签
df = DataFrame({'key1':['a','a','b','b','a'],'key2':['one','two','one','two','one'],'data1':np.random.randn(5),'data2':np.random.randn(5)}) df out: data1 data2 key1 key2 0 -0.2 1.3 a one 1 0.4 0.09 a two 2 -0.5 0.28 b one 3 -0.5 0.76 b two 4 1.96 1.24 a one # 按key1分组,对data1计算平均值 grouped = df['data1'].groupby(data['key1']) # 上述代码也可以携程 grouped = df.groupby(data['key1'])['data1'] # grouped是一个SeriesGroupBy对象 grouped.mean() out: key1 a 0.74 b -0.53 # 根据key1、key2分组,并给她unstack一下 df['data1'].groupby([df['key1'],df['key2']]).mean().unstack() out: key2 one two key1 a -0.88 0.47 b -0.51 -0.55 # 分组键只写列名 df['data1'].groupby([states,years]).mean() # 对df分组,不包括麻烦列(不是数值型的) df.groupby(df['key1'])
对分组进行迭代
groupby对象支持迭代,可以产生一个二元组(由分组名和数据块组成)
for name,group in df.groupby('key1')
print name
print group
a
data1 data2 key1 key2
0 x x a one
1 x x a two
4 x x a one
b
data1 data2 key1 key2
2 x x b one
3 x x b two
如果是多重键,第一个元素是多个键组成的元组,代码略
可以将数据片段赋值给字典
# 将数值片段赋值给字典
pieces = dict(list(df.groupby('key1'))
pieces['b']
out:
key1 为 b 的片段
通过字典或Series进行分组
groupby是仅根据一列、或多列进行聚合,输出的结果是一次聚合的结果。也可以先对列(axis=1)进行分组,然后再进行聚合运算。
people
# 数据是正态分布随机数
out:
a b c d e
Joe
Steve
Wes
Jim
Travis
# 定义字典映射或这Series
mapping = {'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':orange}
# 将这个映射传递给groupby函数
by_columns = people.groupby(mapping,axis=1)
by_columns.sum()
out:
blue red
Joe 0.5 1.06
Steve 1.29 1.55
Wes -1.02 -1.11
Jim 0.52 1.77
Travis -4.23 -2.4
# map_series = Series(mapping)
people.groupby(map_series,axis=1).count()
通过函数进行分组
如果对上一节的人名长度(index长度)进行分组,那么可以传入一个函数即可
people.groupby(len).sum()
??列表、字典、Series混合使用也不是问题,因为任何东西最终都会被转换为数组。
根据索引级别分组
# 定义层次化索引极其名称
columns = pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],[1,3,5,1,3]],names=['cty','tenor'])
hier_df = DataFrame(np.random.randn(4,5),columns=columns)
# 通过level关键字传入级别编号或名称即可
hier_df.groupby(level='cty',axis=1).count()
9.2 数据聚合
聚合函数
给定的聚合函数:
- sum()
- count()
- mean()
- median():中位数
- std()
- var()
- min()
- max()
- first():第一个
- last():最后一个
- quantile(?):分位数
自定义的聚合函数:agg()
# 最大值-最小值
def peak_to_peak(arr):
return arr.max()-arr.min()
grouped.agg(peak_to_peak)
# 单纯用人家的聚合函数,则传入函数名字的时候要加''
grouped.agg('mean')
ps. agg和apply的区别在于,apply更加的一般化(能排序等),而agg更加适合以为运算。总结一下,map针对某类,agg针对聚合运算,apply一般化,可以任意轴的任意运算。
面向列的多函数应用
grouped = tips.groupby(['sex','smoker'])
grouped_pct = grouped['tip_pct']
# ① 一个列来说(Series)
# 传入多个函数
grouped_pct.agg(['mean','std',peak_to_peak])
# 出来的数据columns名字是函数的名字
# 传入多个函数,自己定义名字,用一个元组列表
grouped_pct.agg([('foo,'mean'),('bar',np.std)])
# ② 面向多个列的,列名是层次化索引的最高层
functions = ['count','mean','max']
result = grouped['tip_pct','total_bill'].agg(functions)
result
out:
tip_pct total_bill
sex smoker count mean max count mean max
Famale False
True
Male False
True
以无索引的形式返回聚合数据
所有的groupby函数,如果不需要让索引无意义起来,用as_index=Flase就能禁止。
9.3 分组级运算和转换(transform and apply)
transform
transfrom会将一个函数应用到各个分组,然后将结果放置到适当的位置。
key = ['one','two','one','two','two']
people.groupby(key).transform(np.mean)
apply
在各个分组上调用,并用concat组装到了一起,分组名称也进行了标记。
# 定义一个top函数
def top(df,n=5,column='tip_pct'):
return df.sort_index(by=columns)[-n:]
# 对每一个分组都执行这个函数
tips.groupby('smoker').apply(top)
# 禁止层次化索引
tips.groupby('smoker',group_keys=False).apply(top)
分位数和桶分析
# 结合cut和groupby
frame = DataFrame({'data1':np.random.rand(100),'data2':np.random.randn(1000)})
factor = pd.cut(frame.data1,4)
factor[:10]
out:
Categorical:
array([(],(],(]...])
Levels(4):Index((],(],(]...)
# 定义一个函数,能返回分组(面元)统计信息的
def get_stats(group):
return {'min':group.min(),'max':group.max(),'count':group.count(),'mean':group.mean()}
grouped = frame.data2.groupby(factor)
grouped.apply(get_stats).unstack()
9.4 透视表和交叉表
透视表:pivot table
可以通过groupby技术实现透视表,但是pandas还有一个pivot_table函数,另可添加分项小计margins
# 对tips表的tip_pct和size进行统计,列为smoker,行为sex和day,要分项统计
tip.pivot_table(['tip_pct','size'],rows=['sex','day'],cols='smoker',margin=True,aggfunc=?)
交叉表:crosstab
用于计算分组频率的特殊透视表。
# pd.crosstab(data.Gender,data.Handedness,margin=true)