• 【学习】数据聚合和分组运算【groupby】


    分组键可以有多种方式,且类型不必相同

    • 列表或数组, 某长度与待分组的轴一样
    • 表示DataFrame某个列名的值
    • 字典或Series,给出待分组轴上的值与分组名之间的对应关系
    • 函数用于处理轴索引或索引中的各个标签

    看一下示例:

    import numpy as np  
    import pandas as pd  
    df = pd.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[3]:
          data1     data2 key1 key2
    0  1.236062 -0.576333    a  one
    1 -1.925623 -0.874375    a  two
    2  1.472156 -0.005463    b  one
    3 -0.155766  2.318643    b  two
    4 -1.961012 -0.947843    a  one

    按key1分组,并计算data1列的平均值。访问data1,并根据key1调用groupby

    #按key1分组,访问data1的平均值
    df['data1'].groupby(df['key1']).mean()
    
    Out[4]: 
    key1
    a   -0.883525
    b    0.658195
    Name: data1, dtype: float64

    数据根据分组键进行了聚合,产生一个新的Series,其索引为key1列中的唯一值

    如果一次传入多个数组,就会得到不同的结果

    df['data1'].groupby([df['key1'], df['key2']]).mean()
    Out[5]: 
    key1  key2
    a     one    -0.362475
          two    -1.925623
    b     one     1.472156
          two    -0.155766
    Name: data1, dtype: float64
    #这里,通过两个键进行了分组,得到的Series具有一个层次化索引(由唯一的键对组成)
    means.unstack()
    Out[7]:
    key2       one       two
    key1                    
    a    -0.362475 -1.925623
    b     1.472156 -0.155766

    GroupBy的size方法,可以返回一个含有分组大小的Series

    df.groupby(['key1', 'key2']).size()
    Out[13]: 
    key1  key2
    a     one     2
          two     1
    b     one     1
          two     1
    dtype: int64

    注意:分组键中的任何缺失 值都会被排除在结果之外

    1、对分组进行迭代

    GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)

    for name, group in df.groupby('key1'):
        print(name)
        print(group)
    
    
    a
          data1     data2 key1 key2
    0  1.236062 -0.576333    a  one
    1 -1.925623 -0.874375    a  two
    4 -1.961012 -0.947843    a  one
    b
          data1     data2 key1 key2
    2  1.472156 -0.005463    b  one
    3 -0.155766  2.318643    b  two

    对于多重键的情况,元组的第一个元素将会是由键值组成的元组:

    for (k1, k2), group in df.groupby(['key1', 'key2']):
        print(k1, k2)
        print(group)
    
    
    a one
          data1     data2 key1 key2
    0  1.236062 -0.576333    a  one
    4 -1.961012 -0.947843    a  one
    a two
          data1     data2 key1 key2
    1 -1.925623 -0.874375    a  two
    b one
          data1     data2 key1 key2
    2  1.472156 -0.005463    b  one
    b two
          data1     data2 key1 key2
    3 -0.155766  2.318643    b  two

    你可以对这些数据片段做任何操作,将这些数据片段做成一个字典:

    pieces = dict(list(df.groupby('key1')))
    
    pieces
    Out[18]: 
    {'a':       data1     data2 key1 key2
     0  1.236062 -0.576333    a  one
     1 -1.925623 -0.874375    a  two
     4 -1.961012 -0.947843    a  one, 'b':       data1     data2 key1 key2
     2  1.472156 -0.005463    b  one
     3 -0.155766  2.318643    b  two}

    pieces['b']
    Out[19]:
          data1     data2 key1 key2
    2  1.472156 -0.005463    b  one
    3 -0.155766  2.318643    b  two

    groupby默认在axis = 0 上进行分组的,通过设置也可以在其他任何轴上进行分组

    df.dtypes
    Out[20]: 
    data1    float64
    data2    float64
    key1      object
    key2      object
    dtype: object
    grouped = df.groupby(df.dtypes, axis = 1)
    
    dict(list(grouped))
    Out[22]: 
    {dtype('float64'):       data1     data2
     0  1.236062 -0.576333
     1 -1.925623 -0.874375
     2  1.472156 -0.005463
     3 -0.155766  2.318643
     4 -1.961012 -0.947843, dtype('O'):   key1 key2
     0    a  one
     1    a  two
     2    b  one
     3    b  two
     4    a  one}

    2、选取一个或一组列

    对于由DataFrame产生的GroupBy对象,如果用一个(单个字符串)或一组(字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的。也就是说

    df.groupby('key1')['data1']
    df.groupby('key1')['data2']
    
    df['data1'].groupby(df['key1'])
    df['data2'].groupby(df['key2'])

    尤其对于大数据集,很可能只需要对部分列进行聚合

    df.groupby(['key1', 'key2'])[['data2']].mean()
    Out[25]: 
                  data2
    key1 key2          
    a    one  -0.762088
         two  -0.874375
    b    one  -0.005463
         two   2.318643

    这种索引操作所返回的对象是一个已分组的DataFrame或已分组的Series(如果传入的是标量形式的单个列名)

    3、通过字典或Series进行分组

    people.ix[2:3, ['b', 'c']] = np.nan #添加几个NA值
    
    people
    Out[29]: 
                   a         b         c         d         e
    joe    -0.237225 -0.583231  2.506186  0.764346  2.628579
    Steve  -0.125559 -0.185558 -0.226190 -1.624512 -0.988784
    Wes     0.337773       NaN       NaN -2.489380 -0.064929
    Jim     1.052081 -0.888873  1.227694  0.809077  1.031245
    Travis  0.042581  0.793400  0.316565 -1.098852  0.129532

    假设已知列的分组关系,并根据根据分组计算列的总计

    #ix再回顾温习一下
    mapping = {'a': 'red', 'b': 'red', 'c': 'blue', 'd': 'blue', 'e': 'red', 'f': 'orange'}
    #现在只需将这个字典传给groupby即可
    by_column = people.groupby(mapping, axis = 1)
    by_column.sum()

    Out[32]:
                blue       red
    joe     3.270531  1.808124
    Steve  -1.850702 -1.299900
    Wes    -2.489380  0.272844
    Jim     2.036771  1.194453
    Travis -0.782287  0.965513

    Series也有同样的功能,被看做一个固定大小的映射,如果是Series作为分组键,则pandas会检查Series以确保其索引跟分组轴是对齐的

    map_series = pd.Series(mapping)
    
    map_series
    Out[34]: 
    a       red
    b       red
    c      blue
    d      blue
    e       red
    f    orange
    dtype: object
    people.groupby(map_series, axis = 1).count()
    Out[35]: 
            blue  red
    joe        2    3
    Steve      2    3
    Wes        1    2
    Jim        2    3
    Travis     2    3

    4、通过函数进行分组

    根据人名的长度进行分组,虽然可以求取一个字符串长度数组,但其实仅仅传入len函数就可以

    people.groupby(len).sum()
    Out[36]: 
              a         b         c         d         e
    3  1.152630 -1.472104  3.733879 -0.915957  3.594895
    5 -0.125559 -0.185558 -0.226190 -1.624512 -0.988784
    6  0.042581  0.793400  0.316565 -1.098852  0.129532

    将函数跟数组、列表、字典、Series混合使用,因为任何东西最终都会被转换为数组

    people.groupby([len, key_list]).min()  #这个没有理解
    Out[38]: 
                  a         b         c         d         e
    3 one -0.237225 -0.583231  2.506186 -2.489380 -0.064929
      two  1.052081 -0.888873  1.227694  0.809077  1.031245
    5 one -0.125559 -0.185558 -0.226190 -1.624512 -0.988784
    6 two  0.042581  0.793400  0.316565 -1.098852  0.129532

    5、根据索引级别分组

    层次化索引数据集最方便的地方就在于它能够根据索引级别进行聚合,要实现该目的,通过level关键字传入级别编号或名称即可

    columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'], [1, 3, 5, 1, 3]], names = ['cty', 'tenor'])
    
    hier_df = pd.DataFrame(np.random.randn(4, 5), columns = columns)
    
    hier_df
    Out[41]: 
    cty          US                            JP          
    tenor         1         3         5         1         3
    0     -0.248738  0.627953  2.071784  0.245686 -1.590562
    1      1.723024  0.111605 -0.520688 -0.370661 -0.287072
    2      0.061884 -0.502882  0.281061  0.083908  2.224092
    3     -0.341529  0.945064 -0.785416 -0.443815  0.186289
    hier_df.groupby(level = 'cty', axis =1).count()
    Out[42]: 
    cty  JP  US
    0     2   3
    1     2   3
    2     2   3
    3     2   3

    二、数据聚合

    任何能够从数组产生标量值的数据转换过程。比如mean、 count 、min 以及 sum等。许多常见的聚合运算都有就地计算数据集统计信息的优化实现

    quantile可以计算Series或DataFrame列的样本分位数
    df
    Out[43]:
          data1     data2 key1 key2
    0  1.236062 -0.576333    a  one
    1 -1.925623 -0.874375    a  two
    2  1.472156 -0.005463    b  one
    3 -0.155766  2.318643    b  two
    4 -1.961012 -0.947843    a  one
    grouped
    = df.groupby('key1') grouped grouped['data1'].quantile(0.9)
    Out[47]:
    key1
    a    0.603725
    b    1.309364
    Name: data1, dtype: float64
    #注意:如果传入的百分位上没有值,则quantile会进行线性插值

    实际上,GroupBy会高效地对Series进行切片,然后对各片调用piece, quantile(0.9), 最后将这些结果组成成最终结果

    使用自己的聚合函数,只需将其传入aggregate或agg方法即可

    def peak_to_peak(arr):
        return arr.max() - arr.min()
    
    
    
    grouped.agg(peak_to_peak)
    Out[49]: 
             data1     data2
    key1                    
    a     3.197074  0.371510
    b     1.627922  2.324107
    #describe也可以这样用
    grouped.describe() Out[
    50]: data1 data2 key1 a count 3.000000 3.000000 mean -0.883525 -0.799517 std 1.835701 0.196743 min -1.961012 -0.947843 25% -1.943318 -0.911109 50% -1.925623 -0.874375 75% -0.344781 -0.725354 max 1.236062 -0.576333 b count 2.000000 2.000000 mean 0.658195 1.156590 std 1.151114 1.643392 min -0.155766 -0.005463 25% 0.251215 0.575563 50% 0.658195 1.156590 75% 1.065176 1.737617 max 1.472156 2.318643

    经过优化的groupby方法

     1、面向列的多函数应用

    #备注,tips没有下载下来,这里仅作为展示实例
    grouped = tips.groupby(['sex', 'smoker'])
    grouped_pct = grouped(['tip_pct'])
    grouped_pct.agg('mean')
    grouped_pct.agg('mean', 'std', peak_to_peak)
    #以无索引的形式返回聚合数据
    tips.groupby(['sex', 'smoker'], as_index = False).mean()

    三、分组级运算和转换

    transform和apply方法,它能够执行更多其他的分组运算

    为一个DataFrame添加 一个用于存储各索引分组平均值的列,一个办法是先聚合再合并

    df
    Out[51]: 
          data1     data2 key1 key2
    0  1.236062 -0.576333    a  one
    1 -1.925623 -0.874375    a  two
    2  1.472156 -0.005463    b  one
    3 -0.155766  2.318643    b  two
    4 -1.961012 -0.947843    a  one
    
    k1_means = df.groupby('key1').mean().add_prefix('mean_')
    
    k1_means
    Out[53]: 
          mean_data1  mean_data2
    key1                        
    a      -0.883525   -0.799517
    b       0.658195    1.156590
    pd.merge(df, k1_means, left_on = 'key1', right_index = True)
    Out[54]: 
          data1     data2 key1 key2  mean_data1  mean_data2
    0  1.236062 -0.576333    a  one   -0.883525   -0.799517
    1 -1.925623 -0.874375    a  two   -0.883525   -0.799517
    4 -1.961012 -0.947843    a  one   -0.883525   -0.799517
    2  1.472156 -0.005463    b  one    0.658195    1.156590
    3 -0.155766  2.318643    b  two    0.658195    1.156590
    key = ['one', 'two', 'one', 'two',  'one']
    
    people.groupby(key).mean()
    Out[57]: 
                a         b         c         d         e
    one  0.047710  0.105085  1.411375 -0.941295  0.897728
    two  0.463261 -0.537216  0.500752 -0.407717  0.021231
    
    people.groupby(key).transform(np.mean)
    Out[58]: 
                   a         b         c         d         e
    joe     0.047710  0.105085  1.411375 -0.941295  0.897728
    Steve   0.463261 -0.537216  0.500752 -0.407717  0.021231
    Wes     0.047710  0.105085  1.411375 -0.941295  0.897728
    Jim     0.463261 -0.537216  0.500752 -0.407717  0.021231
    Travis  0.047710  0.105085  1.411375 -0.941295  0.897728

    不难看出,transform会将一个函数应用到各个分组,然后将结果放置到适当的位置上

    假设想从各分组中减去平均值,先创建一个距平化函数,然后将其传给transform

    def demean(arr):
        return arr - arr.mean()
    
    demeaned = people.groupby(key).transform(demean)
    
    demeaned
    Out[60]: 
                   a         b         c         d         e
    joe    -0.284934 -0.688315  1.094810  1.705641  1.730852
    Steve  -0.588820  0.351658 -0.726942 -1.216794 -1.010014
    Wes     0.290063       NaN       NaN -1.548084 -0.962657
    Jim     0.588820 -0.351658  0.726942  1.216794  1.010014
    Travis -0.005129  0.688315 -1.094810 -0.157557 -0.768195
  • 相关阅读:
    机器学习
    机器学习
    JavaWeb之tomcat安装、配置与使用(一)
    Tomcat安装、配置和部署笔记
    Java配置----JDK开发环境搭建及环境变量配置
    安装SQL2012
    SQLServer 数据库变成单个用户后无法访问问题的解决方法
    临时记录
    SQL Server 动态生成数据库所有表Insert语句
    SQL2000查看表的大小
  • 原文地址:https://www.cnblogs.com/yizhenfeng/p/7518111.html
Copyright © 2020-2023  润新知