09 数据聚合与分组计算

时间:2022-11-11 21:50:19

第九章、数据聚合与分组计算

9.1 groupby技术(groupby and agg)

“拆分split——应用apply——合并conbine”的过程。

09 数据聚合与分组计算

图9.1 分组聚合演示

分组键的形式:

  • 列表或数组,其长度和待分组的轴一样
  • 表示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)

09 数据聚合与分组计算

图9.2 计算后,one的两行是都是一至的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=?)

09 数据聚合与分组计算

图9.3 上述处理结果

交叉表:crosstab

用于计算分组频率的特殊透视表。

# pd.crosstab(data.Gender,data.Handedness,margin=true)

09 数据聚合与分组计算

图9.4 上述处理结果

9.5 实例:2012联邦选举委员会…