美文网首页
Pandas数据分析-分组和聚合Aggregation&Grou

Pandas数据分析-分组和聚合Aggregation&Grou

作者: Mc杰夫 | 来源:发表于2022-05-11 17:09 被阅读0次
    • 注:本文演示基于Python 3.9.7和Pandas 1.3.4

    (2022.05.12 Thur)
    Python的Pandas工具包提供了类似于SQL中group的操作指令groupby,但功能更为强大。本文介绍基于groupby的数据分组和聚合操作。此外,还介绍了pandas.transform的使用。

    GroupBy机制

    GroupBy机制可以简单的描述为split-appy-combine过程。pandas对象中的数据,e.g., Series/DataFrame or others,根据用户提供的一个或多个key,被切分(split)成多个group。切分操作按照特定轴进行,即行(axis=0)或列(axis=1)。之后应用(apply)某个函数在切分的group上,生成新值诸如此类。最后将处理后的数据组合(combine)成新的数据对象。

    用于分组的键(key)可以有多种形式:

    • 与分组的轴有相同数据长度的list/array
    • DataFrame中的列名
    • 提供了被分组的值和分组名的字典/Series
    • a function to be invoked on the axis index or the individual labels in the index

    案例如下

    import pandas as pd
    import numpy as np
    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

    >> df
    key1    key2    data1   data2
    0   a   one 0.162224    -0.497908
    1   a   two 0.497138    0.195447
    2   b   one 1.158331    -0.580097
    3   b   two 1.639932    -0.997953
    4   a   one -0.303192   0.525740
    

    下面根据key1列的值对data1列做分组,注意groupby接收的参数是df['key1'],而非'key1',事实上如果被分组的对象是一个Series,则groupby的参数是一个Series,如果被分组对象是一个DataFrame,则groupby的参数可以是一个Series,也可以是该Series的名字,参考下面示例。返回结果grouped的类型是SeriesGroupBy对象。

    >> grouped = df['data1'].groupby(df['key1']) 
    >> grouped
    <pandas.core.groupby.generic.SeriesGroupBy object at 0x117a01370>
    

    该分组结果计算sum

    >> grouped.sum()
    key1
    a    0.356170
    b    2.798262
    Name: data1, dtype: float64
    

    同时也可以对整个DataFrame df做分组,再计算其中列data1的聚合值

    >> df.groupby('key1')['data1'].sum() # or df.groupby('key1')[['data1']].sum()
    key1
    a    0.356170
    b    2.798262
    Name: data1, dtype: float64
    

    可以根据多个列做分组,比如下面用key1key2对DataFrame做分组。

    >> g2 = df.groupby(['key1', 'key2']) 
    >> g2.sum()
                  data1     data2
    key1 key2       
    a    one     -0.140968  0.027832
         two     0.497138   0.195447
    b    one     1.158331   -0.580097
         two     1.639932   -0.997953
    

    上面的g2结果也可以通过unstack方法表示为包含独一无二键值对的分级索引(hierarchical index)

    >> g2.sum.unstack()
                 data1               data2
    key2    one       two        one         two
    key1                
    a   -0.140968   0.497138    0.027832    0.195447
    b   1.158331    1.639932    -0.580097   -0.997953
    

    分组的依据也可以是相同长度的其他数组,比如

    >> states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
    >> years = np.array([2005, 2005, 2006, 2005, 2006])
    >> df['data1'].groupby([states, years]).sum()
    California  2005    0.497138
                2006    1.158331
    Ohio        2005    1.802156
                2006   -0.303192
    Name: data1, dtype: float64
    

    我们再来看有哪些列参与了分组。下面的这个例子,用key1列对DataFrame做分组,结果中只显示了data1data2而没有key2。因为key2列是非数值列,被排除在分组聚合的结果中

    >> df.groupby('key1').sum()
        data1   data2
    key1        
    a   0.356170    0.223279
    b   2.798262    -1.578050
    

    size方法返回分组的数字

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

    (2022.05.13 Fri)
    迭代groupby对象

    >> for k,v in df.groupby('key1'):
      ...   print(k,'\n')
      ...   print(v)
    a 
    
      key1 key2     data1     data2
    0    a  one  0.162224 -0.497908
    1    a  two  0.497138  0.195447
    4    a  one -0.303192  0.525740
    b 
    
      key1 key2     data1     data2
    2    b  one  1.158331 -0.580097
    3    b  two  1.639932 -0.997953
    

    也可以转化成list对象迭代

    >> list(df.groupby('key1'))
    [('a',
        key1 key2     data1     data2
      0    a  one  0.162224 -0.497908
      1    a  two  0.497138  0.195447
      4    a  one -0.303192  0.525740),
     ('b',
        key1 key2     data1     data2
      2    b  one  1.158331 -0.580097
      3    b  two  1.639932 -0.997953)]
    

    注意,当使用多个key做分组时,key的不同元素的组合保存为tuple

    >> for k, g in df.groupby(['key1', 'key2']):
      ...   print(k, '\n')
      ...   print(g)
    ('a', 'one') 
    
      key1 key2     data1     data2
    0    a  one  0.162224 -0.497908
    4    a  one -0.303192  0.525740
    ('a', 'two') 
    
      key1 key2     data1     data2
    1    a  two  0.497138  0.195447
    ('b', 'one') 
    
      key1 key2     data1     data2
    2    b  one  1.158331 -0.580097
    ('b', 'two') 
    
      key1 key2     data1     data2
    3    b  two  1.639932 -0.997953
    

    按字典和Series分组
    除了数组之外,DataFrame还可按字典和Series分组。
    考虑如下DataFrame

    people = pd.DataFrame(np.random.randn(5, 5),
    columns=['a', 'b', 'c', 'd', 'e'],
    index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
    people.iloc[2:3, [1, 2]] = np.nan
    

    打印

    >> people
             a           b          c           d           e
    Joe 1.317179    -0.209542   0.943849    -2.226055   -0.430271
    Steve   -1.757563   -1.531038   -0.519030   -0.533200   0.447961
    Wes 0.407967    NaN NaN 0.658021    0.440425
    Jim -0.320255   -1.165006   0.275818    1.276247    -1.346920
    Travis  -1.678433   -1.272230   1.391566    -0.006185   -0.109339
    

    定义一个字典

    >> mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
                  'd': 'blue', 'e': 'red', 'f' : 'orange'}
    

    可以字典为key,对people进行分组

    >> by_column = people.groupby(mapping, axis=1)
    >> by_column.sum()
            blue     red
    Joe -1.282207   0.677367
    Steve   -1.052230   -2.840639
    Wes 0.658021    0.848391
    Jim 1.552065    -2.832181
    Travis  1.385381    -3.060002
    

    也可以将字典转化为Series并以此为依据对数据做分组

    >> ms = pd.Series(mapping)
    >> ms
    a       red
    b       red
    c      blue
    d      blue
    e       red
    f    orange
    dtype: object
    
    >> people.groupby(ms, axis=1).sum()
            blue       red
    Joe -1.282207   0.677367
    Steve   -1.052230   -2.840639
    Wes 0.658021    0.848391
    Jim 1.552065    -2.832181
    Travis  1.385381    -3.060002
    

    根据函数分组
    函数也可作为group key。一旦函数做为group key,将index值作为函数的输入,而返回值作为组名。比如上面的people数组,调用len作为group key,则返回的index值的长度将作为组名。

    >> people.groupby(len).sum()
            a           b           c           d         e
    3   1.404891    -1.374547   1.219667    -0.291787   -1.336766
    5   -1.757563   -1.531038   -0.519030   -0.533200   0.447961
    6   -1.678433   -1.272230   1.391566    -0.006185   -0.109339
    

    另一个例子,一个函数检测string的结尾字母,如果是e或s则分别返回,其他情况返回others。将这个函数作为group key,查看结果。

    def endswith(k):
        if k.endswith('e'):
            return 'e'
        elif k.endswith('s'):
            return 's'
        else:
            return 'others'
    
    >> people.groupby(endswith).sum()
              a         b           c           d           e
    e   -0.440383   -1.740579   0.424819    -2.759255   0.017690
    others  -0.320255   -1.165006   0.275818    1.276247    -1.346920
    s   -1.270466   -1.272230   1.391566    0.651836    0.331085
    

    亦可混合使用

    >> key_list = ['one', 'one', 'one', 'two', 'two']
    >> people.groupby([len, key_list]).min()
                a          b            c           d           e
    3   one 0.407967    -0.209542   0.943849    -2.226055   -0.430271
        two -0.320255   -1.165006   0.275818    1.276247    -1.346920
    5   one -1.757563   -1.531038   -0.519030   -0.533200   0.447961
    6   two -1.678433   -1.272230   1.391566    -0.006185   -0.109339
    

    根据分级索引(hierarchical index)分组

    >> 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
    cty   US                         JP
    tenor 1        3         5        1        3
    0 0.560145 -1.265934 0.119827 -1.063512 0.332883
    1 -2.359419 -0.199543 -1.541996 -0.970736 -1.307030
    2 0.286350 0.377984 -0.753887 0.331286 1.349742
    3 0.069877 0.246674 -0.011862 1.004812 1.327195
    
    >> hier_df.groupby(level='cty', axis=1).count()
    cty JP  US
    0   2   3
    1   2   3
    2   2   3
    3   2   3
    

    Data Aggregation数据聚合

    数据聚合指的是从数组生成标量(scalar)值的数据变换过程。常见的数据聚合包括前面出现的几种,比如meansumcountmin/maxmedianstd, varprodfirstlast等。还包括quantile用于计算分位点。比如前面的根据key1分组的data1数据,计算其0.9分位点

    >> grouped.quantile(0.9)
    key1
    a    0.430156
    b    1.591772
    Name: data1, dtype: float64
    

    也可以自定义聚合函数,并用agg方法引入。比如定义一个80分位点与20分位点的差值函数q82,并用agg方法调用该聚合函数。

    def q82(arr):
        return np.percentile(arr, 80) - np.percentile(arr, 20)
    
    >> grouped.agg(q82)
    key1
    a    0.480198
    b    0.288961
    Name: data1, dtype: float64
    

    注意,如果是自定义的函数,在被agg方法调用时直接写函数名即可,如果是内置聚合函数如上面提到的,则需要在名字上加引号,且与不用agg方法的直接调用返回相同的结果。

    >> grouped.agg('mean')
    key1
    a    0.118723
    b    1.399131
    Name: data1, dtype: float64
    >> grouped.mean()
    key1
    a    0.118723
    b    1.399131
    Name: data1, dtype: float64
    

    可通过describe方法验证上面的结果

    >> grouped.describe()
       count    mean      std          min          25%         50%         75%  
    max
    key1                                
    a   3.0   0.118723  0.401935    -0.303192   -0.070484   0.162224    0.329681    0.497138
    b   2.0   1.399131  0.340543    1.158331    1.278731    1.399131    1.519531    1.639932
    

    当然也可以用实现其他功能的函数,比如对分组结果排序

    def sf(arr):
        arr = list(map(lambda x: round(x, 2), arr))
        return sorted(arr)
    
    >> grouped.agg(sf)
    key1
    a    [-0.3, 0.16, 0.5]
    b         [1.16, 1.64]
    Name: data1, dtype: object
    

    列级(colume-wise)应用和多函数应用

    对单独一个列做多个函数操作,参考如下案例

    >> tips = pd.read_csv('/Users/.../pydata-book/examples/tips.csv')
    >> tips.shape
    (244, 6)
    >> tips.columns
    Index(['total_bill', 'tip', 'smoker', 'day', 'time', 'size'], dtype='object')
    >> tips.head(5)
        total_bill tip smoker day time size
    0   16.99   1.01    No  Sun Dinner  2
    1   10.34   1.66    No  Sun Dinner  3
    2   21.01   3.50    No  Sun Dinner  3
    3   23.68   3.31    No  Sun Dinner  2
    4   24.59   3.61    No  Sun Dinner  4
    >> tips['tip_pct'] = tips['tip']/tips['total_bill'] # 生成小费比例字段
    >> grouped = tips.groupby(['day', 'smoker']) #根据day和smoker两个字段对数据分组
    >> grouped_pct = grouped['tip_pct'] # 取得tip_pct字段
    

    计算平均

    >> grouped_pct.agg('mean')
    day   smoker
    Fri   No        0.151650
          Yes       0.174783
    Sat   No        0.158048
          Yes       0.147906
    Sun   No        0.160113
          Yes       0.187250
    Thur  No        0.160298
          Yes       0.163863
    Name: tip_pct, dtype: float64
    

    计算一列的多个统计量/聚合值,用agg方法,统计量用list保存

    >> grouped_pct.agg(['mean', 'max', q82])
              mean          max       q82
    day smoker          
    Fri No  0.151650    0.187735    0.034600
        Yes 0.174783    0.263480    0.097666
    Sat No  0.158048    0.291990    0.061659
        Yes 0.147906    0.325733    0.112116
    Sun No  0.160113    0.252672    0.072194
        Yes 0.187250    0.710345    0.144385
    Thur No 0.160298    0.266312    0.060163
        Yes 0.163863    0.241255    0.061858
    

    此外,你可以为作用于列的统计量/聚合函数命名,并以新命名显示,以tuple in list为表示。

    >> rn = [('func_mean', 'mean'), ('func_q82', q82)]
    >> grouped_pct.agg(rn)
           func_mean    func_q82
    day smoker      
    Fri No  0.151650    0.034600
        Yes 0.174783    0.097666
    Sat No  0.158048    0.061659
        Yes 0.147906    0.112116
    Sun No  0.160113    0.072194
        Yes 0.187250    0.144385
    Thur No 0.160298    0.060163
        Yes 0.163863    0.061858
    

    此方法也可用于多个列使用相同的聚合方法,注意多列的名字表示成一个list,作为grouped的index。注意到对多个列使用不同的聚合方法,DataFrame将返回分级列(hierarchical column)。

    >> tmp = grouped[['tip_pct', 'total_bill']].agg(['mean', 'max', q82])
    >> tmp
                         tip_pct                         total_bill
              mean         max         q82         mean      max    q82
    day smoker                      
    Fri No  0.151650    0.187735    0.034600    18.420000   22.75   8.022
        Yes 0.174783    0.263480    0.097666    16.813333   40.17   11.166
    Sat No  0.158048    0.291990    0.061659    19.661778   48.33   7.724
        Yes 0.147906    0.325733    0.112116    21.276667   50.81   15.168
    Sun No  0.160113    0.252672    0.072194    20.506667   48.17   11.844
        Yes 0.187250    0.710345    0.144385    24.120000   45.35   17.224
    Thur No 0.160298    0.266312    0.060163    17.113111   41.19   11.706
        Yes 0.163863    0.241255    0.061858    19.190588   43.11   7.284
    

    上面结果可按列读取

    >> tmp['tip_pct']
               mean      max        q82
    day smoker          
    Fri No  0.151650    0.187735    0.034600
        Yes 0.174783    0.263480    0.097666
    Sat No  0.158048    0.291990    0.061659
        Yes 0.147906    0.325733    0.112116
    Sun No  0.160113    0.252672    0.072194
        Yes 0.187250    0.710345    0.144385
    Thur No 0.160298    0.266312    0.060163
        Yes 0.163863    0.241255    0.061858
    

    而对不同的列采用不同的聚合函数,需要使用字典。比如对tip列要计算最大值,对size列计算求和,可用列名做key,聚合函数名或聚合函数形成的列做value

    >> ac = {'tip': ['max', 'mean'], 'size': 'sum'}
    >> grouped.agg(ac)
                tip            size
            max    mean        sum
    day smoker          
    Fri No  3.50    2.812500    9
        Yes 4.73    2.714000    31
    Sat No  9.00    3.102889    115
        Yes 10.00   2.875476    104
    Sun No  6.00    3.167895    167
        Yes 6.50    3.516842    49
    Thur No 6.70    2.673778    112
        Yes 5.00    3.030000    40
    

    聚合结果取消index

    对分组后的聚合结果取消index,只需要在做groupby操作时加入as_index = False选项

    >> tips.groupby(['day', 'smoker'], as_index=False).mean()
        day smoker total_bill  tip       size       tip_pct
    0   Fri No  18.420000   2.812500    2.250000    0.151650
    1   Fri Yes 16.813333   2.714000    2.066667    0.174783
    2   Sat No  19.661778   3.102889    2.555556    0.158048
    3   Sat Yes 21.276667   2.875476    2.476190    0.147906
    4   Sun No  20.506667   3.167895    2.929825    0.160113
    5   Sun Yes 24.120000   3.516842    2.578947    0.187250
    6   Thur No 17.113111   2.673778    2.488889    0.160298
    7   Thur Yes 19.190588  3.030000    2.352941    0.163863
    

    使用聚合函数的agg方法与apply的差别

    agg方法应用于列、Series
    apply方法应用于这个DataFrame
    (待验证)

    Apply-一般性的Split-Apply-Combine

    Groupby方法里面最常用的部分是Apply,即对数据分组之后,再对每个分组piece进行的操作。Apply操作之后即是对数据做concatenate。注意Apply作用的对象是group piece,也是一个DataFrame。

    仍然考虑上面的数据框tips,找出每天的中晚饭小费收入占比最高的前五位。首先写个函数返回一个数据框按某一列排序最高的前5位所有数据。

    def top(df, n=5, column='tip_pct'):
        return df.sort_value(name=column)[-n:]
    

    下面按daytime分组,查看每组有多少笔小费,并对分组执行top函数。可以看出周六周日两天人们普遍支付更多比例的小费。

    >> tips.groupby(['day', 'time']).size()
    day   time  
    Fri   Dinner    12
          Lunch      7
    Sat   Dinner    87
    Sun   Dinner    76
    Thur  Dinner     1
          Lunch     61
    dtype: int64
    >> tips.groupby(['day', 'time']).apply(top)
               total_bill  tip  smoker  day  time  size  tip_pct
    day time                                
    Fri Dinner  91  22.49   3.50    No  Fri Dinner  2   0.155625
                92  5.75    1.00    Yes Fri Dinner  2   0.173913
                101 15.38   3.00    Yes Fri Dinner  2   0.195059
                100 11.35   2.50    Yes Fri Dinner  2   0.220264
                93  16.32   4.30    Yes Fri Dinner  2   0.263480
         Lunch  220 12.16   2.20    Yes Fri Lunch   2   0.180921
                223 15.98   3.00    No  Fri Lunch   3   0.187735
                226 10.09   2.00    Yes Fri Lunch   2   0.198216
                222 8.58    1.92    Yes Fri Lunch   1   0.223776
                221 13.42   3.48    Yes Fri Lunch   2   0.259314
    Sat Dinner  20  17.92   4.08    No  Sat Dinner  2   0.227679
                214 28.17   6.50    Yes Sat Dinner  3   0.230742
                109 14.31   4.00    Yes Sat Dinner  2   0.279525
                232 11.61   3.39    No  Sat Dinner  2   0.291990
                67  3.07    1.00    Yes Sat Dinner  1   0.325733
    Sun Dinner  181 23.33   5.65    Yes Sun Dinner  2   0.242177
                51  10.29   2.60    No  Sun Dinner  2   0.252672
                183 23.17   6.50    Yes Sun Dinner  4   0.280535
                178 9.60    4.00    Yes Sun Dinner  2   0.416667
                172 7.25    5.15    Yes Sun Dinner  2   0.710345
    Thur Dinner 243 18.78   3.00    No  Thur    Dinner  2   0.159744
         Lunch  200 18.71   4.00    Yes Thur    Lunch   3   0.213789
                87  18.28   4.00    No  Thur    Lunch   2   0.218818
                88  24.71   5.85    No  Thur    Lunch   2   0.236746
                194 16.58   4.00    Yes Thur    Lunch   2   0.241255
                149 7.51    2.00    No  Thur    Lunch   2   0.266312
    ...
    

    apply中调用的函数可以通过如下方式传递参数。比如查看每天每个时段最高价的bill,可指定n=1columns=total_bill

    >> tips.groupby(['day', 'time']).apply(top, n=1, columns='total_bill')
                total_bill  tip   smoker day  time size  tip_pct
    day time                                
    Fri Dinner  95  40.17   4.73    Yes Fri Dinner  4   0.117750
        Lunch   225 16.27   2.50    Yes Fri Lunch   2   0.153657
    Sat Dinner  170 50.81   10.00   Yes Sat Dinner  3   0.196812
    Sun Dinner  156 48.17   5.00    No  Sun Dinner  6   0.103799
    Thur Dinner 243 18.78   3.00    No  Thur    Dinner  2   0.159744
        Lunch   197 43.11   5.00    Yes Thur    Lunch   4   0.115982
    

    再比如,查看分组后某个数据的describe信息,除了对分组结果调用内置的describe方法,也可将describe写成lambda函数通过apply调用

    >> tips.groupby('smoker')['tip_pct'].describe()
         count    mean        std          min         25%        50%        75%        max
    smoker                              
    No  151.0   0.159328    0.039910    0.056797    0.136906    0.155625    0.185014    0.291990
    Yes 93.0    0.163196    0.085119    0.035638    0.106771    0.153846    0.195059    0.710345
    >> f = lambda x: x.describe()
    >> tips.groupby('smoker')['tip_pct'].apply(f)
    smoker       
    No      count    151.000000
            mean       0.159328
            std        0.039910
            min        0.056797
            25%        0.136906
            50%        0.155625
            75%        0.185014
            max        0.291990
    Yes     count     93.000000
            mean       0.163196
            std        0.085119
            min        0.035638
            25%        0.106771
            50%        0.153846
            75%        0.195059
            max        0.710345
    Name: tip_pct, dtype: float64
    

    (2022.05.16 Mon)

    不显示分组键Suppressing the group key:

    上面的例子中结果数据在初始对象的索引之上保存了分级索引hierachical index,可使用group_keys=False指令取消分级索引:

    >> tips.groupby(['day', 'time'], group_keys=False).apply(top, n=1, columns='total_bill')
        total_bill  tip smoker day time size tip_pct
    95  40.17   4.73    Yes Fri Dinner  4   0.117750
    225 16.27   2.50    Yes Fri Lunch   2   0.153657
    170 50.81   10.00   Yes Sat Dinner  3   0.196812
    156 48.17   5.00    No  Sun Dinner  6   0.103799
    243 18.78   3.00    No  Thur    Dinner  2   0.159744
    197 43.11   5.00    Yes Thur    Lunch   4   0.115982
    

    分位数和桶分析Quantile and Bucket Analysis

    可使用pandas.cut指令对一个Series做分位分析,并结合groupby指令对数组做分析。

    >> frame = pd.DataFrame({'data1': np.random.randn(1000),
                          'data2': np.random.randn(1000)})
    >> frame.head()
           data1   data2
    0   -0.754650   -0.085965
    1   -0.922789   0.523089
    2   -1.001486   0.306347
    3   1.169146    -1.424664
    4   2.311694    0.280592
    >> quartiles = pd.cut(frame.data1, 4)
    >> quartiles.head()
    0    (-1.563, 0.154]
    1    (-1.563, 0.154]
    2    (-1.563, 0.154]
    3     (0.154, 1.871]
    4     (1.871, 3.588]
    Name: data1, dtype: category
    Categories (4, interval[float64, right]): [(-3.287, -1.563] < (-1.563, 0.154] < (0.154, 1.871] < (1.871, 3.588]]
    

    cut方法返回的Categorial对象可传递给groupby方法。我们可以以此为依据对data2字段做分组。

    def get_stats(df):
        return {'mean': df.mean(), 'min': df.min(), 'max': df.max(), 'count': df.count()}
    
    >> q = frame.data2.groupby(quartiles).apply(get_stats).unstack()
            mean      min      max      count
    data1               
    (-3.287, -1.563]    0.069223    -2.676886   2.390891    57.0
    (-1.563, 0.154] -0.089421   -3.099386   2.872714    514.0
    (0.154, 1.871]  -0.001327   -3.319630   3.122984    394.0
    (1.871, 3.588]  0.093478    -2.111966   2.091880    35.0
    

    注意这里的cut得到的分组是范围的长度尺度上相同/相似的结果。如果要每个分组内值的数量相同/相似,则需要使用qcut指令。

    GroupBy应用

    Case 1:按组填充NA值

    按组填充该组中的空值。有下面数据,保存美国若干州的某项数据。这些州分属东西两部分。我们设置其中一些值为空。

    >> states = ['Ohio', 'New York', 'Vermont', 'Florida',\
              'Oregon', 'Nevada', 'California', 'Idaho']
    >> group_key = ['East'] * 4 + ['West'] * 4
    >> data = pd.Series(np.random.randn(8), index=states)
    >> data
    Ohio          0.869617
    New York     -1.113901
    Vermont      -0.225091
    Florida      -0.010972
    Oregon       -0.312229
    Nevada       -0.122721
    California   -1.171598
    Idaho         0.361301
    dtype: float64
    >> data[::2] = np.nan
    >> data
    Ohio               NaN
    New York     -1.113901
    Vermont            NaN
    Florida      -0.010972
    Oregon             NaN
    Nevada       -0.122721
    California         NaN
    Idaho         0.361301
    dtype: float64
    

    如果此时分组,计算得到的是非空值的统计信息,不包含空值部分

    >> data.groupby(group_key).mean()
    East   -0.562437
    West    0.119290
    dtype: float64
    

    下面按组填充数据,比如对每组中的空值填充该组其他值的mean

    >> f_mean = lambda x: x.fillna(x.mean())
    >> data.groupby(group_key).apply(f_mean)
    Ohio         -0.562437
    New York     -1.113901
    Vermont      -0.562437
    Florida      -0.010972
    Oregon        0.119290
    Nevada       -0.122721
    California    0.119290
    Idaho         0.361301
    dtype: float64
    

    也可针对某个组填充特定值

    >> fill_values = {'East': 5, 'West': 10}
    >> fill_func = lambda g: g.fillna(fill_values[g.name])
    >> data.groupby(group_key).apply(fill_func)
    Ohio           5.000000
    New York      -1.113901
    Vermont        5.000000
    Florida       -0.010972
    Oregon        10.000000
    Nevada        -0.122721
    California    10.000000
    Idaho          0.361301
    dtype: float64
    

    Case 2:分组计算均值

    df = pd.DataFrame({'category': ['a', 'a', 'a', 'a',\
                                    'b', 'b', 'b', 'b'],\
                                     'data': np.random.randn(8),\
                                     'weights': np.random.rand(8)})
    >> df
    category data weights
    0 a 1.561587 0.957515
    1 a 1.219984 0.347267
    2 a -0.482239 0.581362
    3 a 0.315667 0.217091
    4 b -0.047852 0.894406
    5 b -0.454145 0.918564
    6 b -0.556774 0.277825
    7 b 0.253321 0.955905
    

    加权平均的函数get_wa

    get_wa = lambda x: np.average(x.data, weights=x.weights)
    

    df这个dataframe按分组执行加权平均操作

    >> df.groupby('category').apply(get_wa)
    category
    a    0.111150
    b   -1.091834
    dtype: float64
    

    基于pandas.groupbypandas.transform指令

    前面已经提到,用pandas.groupby指令对DataFrame分组之后,可针对各组做操作。比如对各组求其mean操作

    >> dd = pd.DataFrame({'keys': ['a', 'b', 'c', 'c','c','a','b','b','a'], 'values': np.arange(9,)})
    >> dd.groupby('keys').values.mean()
    keys
    a    4.333333
    b    4.666667
    c    3.000000
    Name: values, dtype: float64
    

    假定我们想要得到一个Series和原序列长度相同,但是新Series中的值用原序列分组后的mean值代替。这种情况下pandas提供了transform方法执行这种操作。下面计算对根据key keys做分组的values的值做归一化操作的过程,归一化的结果写入原DataFrame中。

    >> f = lambda x: (x - x.mean())/x.std()
    >> dd['mean'] = dd.groupby('keys').values.transform('mean')
    >> dd['std'] = dd.groupby('keys').values.transform('std')
    >> dd['norm'] = dd.groupby('keys').values.transform(f)
    >> dd
      keys values  mean       std        norm
    0   a   0   4.333333    4.041452    -1.072222
    1   b   1   4.666667    3.214550    -1.140647
    2   c   2   3.000000    1.000000    -1.000000
    3   c   3   3.000000    1.000000    0.000000
    4   c   4   3.000000    1.000000    1.000000
    5   a   5   4.333333    4.041452    0.164957
    6   b   6   4.666667    3.214550    0.414781
    7   b   7   4.666667    3.214550    0.725866
    8   a   8   4.333333    4.041452    0.907265
    

    Reference

    1 Python for Data Analysis, Wes McKinney

    相关文章

      网友评论

          本文标题:Pandas数据分析-分组和聚合Aggregation&Grou

          本文链接:https://www.haomeiwen.com/subject/ugosurtx.html