• pandas教程:Data Aggregation 数据聚合


    10.2 Data Aggregation(数据聚合)

    聚合(Aggregation)指的是一些数据转化data transformation),这些数据转化能从数组中产生标量(scalar values)。下面的例子就是一些聚合方法,包括mean, count, min and sum。我们可能会好奇,在一个GroupBy对象上调用mean()的时候,究竟发生了什么。一些常见的聚合,比如下表,实现方法上都已经被优化过了。当然,我们可以使用的聚合方法不止这些:

    我们可以使用自己设计的聚合方法,而且可以调用分组后对象上的任意方法。例如,我们可以调用quantile来计算SeriesDataFrame中列的样本的百分数。

    尽管quantile并不是专门为GroupBy对象设计的方法,这是一个Series方法,但仍可以被GroupBy对象使用。GroupBy会对Series进行切片(slice up),并对于切片后的每一部分调用piece.quantile(0.9),然后把每部分的结果整合到一起

    import numpy as np
    import pandas as pd
    
    • 1
    • 2
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    data1data2key1key2
    01.7077380.186729aone
    11.0698311.305796atwo
    2-2.291339-1.609071bone
    31.348090-0.294999btwo
    40.3411760.429461aone
    grouped = df.groupby('key1')
    for key, group in grouped:
        print(key)
        print(group)
    
    • 1
    • 2
    • 3
    • 4
    a
          data1     data2 key1 key2
    0  1.707738  0.186729    a  one
    1  1.069831  1.305796    a  two
    4  0.341176  0.429461    a  one
    b
          data1     data2 key1 key2
    2 -2.291339 -1.609071    b  one
    3  1.348090 -0.294999    b  two
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    grouped['data1'].quantile(0.9)
    
    • 1
    key1
    a    1.580157
    b    0.984147
    Name: data1, dtype: float64
    
    • 1
    • 2
    • 3
    • 4

    如果想用自己设计的聚合函数,把用于聚合数组的函数传入到aggregateagg方法即可:

    def peak_to_peak(arr):
        return arr.max() - arr.min()
    
    • 1
    • 2
    grouped.agg(peak_to_peak)
    
    • 1
    data1data2
    key1
    a1.3665631.119067
    b3.6394301.314072

    我们发现很多方法,比如describe,也能正常使用,尽管严格的来说,这并不是聚合:

    grouped.describe()
    
    • 1
    data1data2
    key1
    acount3.0000003.000000
    mean1.0395820.640662
    std0.6837830.588670
    min0.3411760.186729
    25%0.7055030.308095
    50%1.0698310.429461
    75%1.3887850.867629
    max1.7077381.305796
    bcount2.0000002.000000
    mean-0.471624-0.952035
    std2.5734650.929189
    min-2.291339-1.609071
    25%-1.381482-1.280553
    50%-0.471624-0.952035
    75%0.438233-0.623517
    max1.348090-0.294999

    细节的部分在10.3会进行更多解释。

    注意:自定义的函数会比上面表中的函数慢一些,上面的函数时优化过的,而自定义的函数会有一些额外的计算,所以慢一些。

    1 Column-Wise and Multiple Function Application(列对列和多函数应用)

    让我们回到tipping数据集。加载数据及后,我们添加一列用于描述小费的百分比:

    tips = pd.read_csv('../examples/tips.csv')
    
    • 1
    # Add tip percentage of total bill
    tips['tip_pct'] = tips['tip'] / tips['total_bill']
    
    • 1
    • 2
    tips[:6]
    
    • 1
    total_billtipsmokerdaytimesizetip_pct
    016.991.01NoSunDinner20.059447
    110.341.66NoSunDinner30.160542
    221.013.50NoSunDinner30.166587
    323.683.31NoSunDinner20.139780
    424.593.61NoSunDinner40.146808
    525.294.71NoSunDinner40.186240

    我们可以看到,对seriesDataFrame进行聚合,其实就是通过aggregate使用合适的函数,或者调用一些像meanstd这样的方法。然而,我们可能想要在列上使用不同的函数进行聚合,又或者想要一次执行多个函数。幸运的是,这是可能的,下面将通过一些例子来说明。首先,对于tips数据集,先用daysmoker进行分组:

    grouped = tips.groupby(['day', 'smoker'])
    
    • 1

    对于像是上面表格10-1中的一些描述性统计,我们可以直接传入函数的名字,即字符串:

    grouped_pct = grouped['tip_pct']
    
    • 1
    for name, group in grouped_pct:
        print(name)
        print(group[:2], '\n')
    
    • 1
    • 2
    • 3
    ('Fri', 'No')
    91    0.155625
    94    0.142857
    Name: tip_pct, dtype: float64 
    
    ('Fri', 'Yes')
    90    0.103555
    92    0.173913
    Name: tip_pct, dtype: float64 
    
    ('Sat', 'No')
    19    0.162228
    20    0.227679
    Name: tip_pct, dtype: float64 
    
    ('Sat', 'Yes')
    56    0.078927
    58    0.156584
    Name: tip_pct, dtype: float64 
    
    ('Sun', 'No')
    0    0.059447
    1    0.160542
    Name: tip_pct, dtype: float64 
    
    ('Sun', 'Yes')
    164    0.171331
    172    0.710345
    Name: tip_pct, dtype: float64 
    
    ('Thur', 'No')
    77    0.147059
    78    0.131810
    Name: tip_pct, dtype: float64 
    
    ('Thur', 'Yes')
    80    0.154321
    83    0.152999
    Name: tip_pct, dtype: float64 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    grouped_pct.agg('mean')
    
    • 1
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果我们把函数或函数的名字作为一个list传入,我们会得到一个DataFrame,每列的名字就是函数的名字:

    # def peak_to_peak(arr):
    #     return arr.max() - arr.min()
    grouped_pct.agg(['mean', 'std', peak_to_peak])
    
    • 1
    • 2
    • 3
    meanstdpeak_to_peak
    daysmoker
    FriNo0.1516500.0281230.067349
    Yes0.1747830.0512930.159925
    SatNo0.1580480.0397670.235193
    Yes0.1479060.0613750.290095
    SunNo0.1601130.0423470.193226
    Yes0.1872500.1541340.644685
    ThurNo0.1602980.0387740.193350
    Yes0.1638630.0393890.151240

    上面我们把多个聚合函数作为一个list传入给agg,这些函数会独立对每一个组进行计算。

    上面结果的列名是自动给出的,当然,我们也可以更改这些列名。这种情况下,传入一个由tuple组成的list,每个tuple的格式是(name, function),每个元组的第一个元素会被用于作为DataFrame的列名(我们可以认为这个二元元组list是一个有序的映射):

    grouped_pct.agg([('foo', 'mean'), ('bar', np.std)])
    
    • 1
    foobar
    daysmoker
    FriNo0.1516500.028123
    Yes0.1747830.051293
    SatNo0.1580480.039767
    Yes0.1479060.061375
    SunNo0.1601130.042347
    Yes0.1872500.154134
    ThurNo0.1602980.038774
    Yes0.1638630.039389

    如果是处理一个DataFrame,我们有更多的选择,我们可以用一个含有多个函数的list应用到所有的列上,也可以在不同的列上应用不同的函数。演示一下,假设我们想要在tip_pcttotal_bill这两列上,计算三个相同的统计指标:

    functions = ['count', 'mean', 'max']
    
    • 1
    result = grouped['tip_pct', 'total_bill'].agg(functions)
    result
    
    • 1
    • 2
    tip_pcttotal_bill
    countmeanmaxcountmeanmax
    daysmoker
    FriNo40.1516500.187735418.42000022.75
    Yes150.1747830.2634801516.81333340.17
    SatNo450.1580480.2919904519.66177848.33
    Yes420.1479060.3257334221.27666750.81
    SunNo570.1601130.2526725720.50666748.17
    Yes190.1872500.7103451924.12000045.35
    ThurNo450.1602980.2663124517.11311141.19
    Yes170.1638630.2412551719.19058843.11

    我们可以看到,结果中的DataFrame有多层级的列(hierarchical columns)。另外一种做法有相同的效果,即我们对于每一列单独进行聚合(aggregating each column separately),然后使用concat把结果都结合在一起,然后用列名作为keys参数:

    result['tip_pct']
    
    • 1
    countmeanmax
    daysmoker
    FriNo40.1516500.187735
    Yes150.1747830.263480
    SatNo450.1580480.291990
    Yes420.1479060.325733
    SunNo570.1601130.252672
    Yes190.1872500.710345
    ThurNo450.1602980.266312
    Yes170.1638630.241255

    我们之前提到过,可以用元组组成的list来自己定义列名:

    ftuples = [('Durchschnitt', 'mean'), ('Abweichung', np.var)]
    
    • 1
    grouped['tip_pct', 'total_bill'].agg(ftuples)
    
    • 1
    tip_pcttotal_bill
    DurchschnittAbweichungDurchschnittAbweichung
    daysmoker
    FriNo0.1516500.00079118.42000025.596333
    Yes0.1747830.00263116.81333382.562438
    SatNo0.1580480.00158119.66177879.908965
    Yes0.1479060.00376721.276667101.387535
    SunNo0.1601130.00179320.50666766.099980
    Yes0.1872500.02375724.120000109.046044
    ThurNo0.1602980.00150317.11311159.625081
    Yes0.1638630.00155119.19058869.808518

    现在,假设我们想要把不同的函数用到一列或多列上。要做到这一点,给agg传递一个dict,这个dict需要包含映射关系,用来表示列名和函数之间的对应关系:

    grouped.agg({'tip': np.max, 'size': 'sum'})
    
    • 1
    tipsize
    daysmoker
    FriNo3.509
    Yes4.7331
    SatNo9.00115
    Yes10.00104
    SunNo6.00167
    Yes6.5049
    ThurNo6.70112
    Yes5.0040
    grouped.agg({'tip_pct': ['min', 'max', 'mean', 'std'],
                 'size': 'sum'})
    
    • 1
    • 2
    tip_pctsize
    minmaxmeanstdsum
    daysmoker
    FriNo0.1203850.1877350.1516500.0281239
    Yes0.1035550.2634800.1747830.05129331
    SatNo0.0567970.2919900.1580480.039767115
    Yes0.0356380.3257330.1479060.061375104
    SunNo0.0594470.2526720.1601130.042347167
    Yes0.0656600.7103450.1872500.15413449
    ThurNo0.0729610.2663120.1602980.038774112
    Yes0.0900140.2412550.1638630.03938940

    只有当多个函数用于至少一列的时候,DataFrame才会有多层级列(hierarchical columns

    2 Returning Aggregated Data Without Row Indexes(不使用行索引返回聚合数据)

    目前为止提到的所有例子,最后返回的聚合数据都是有索引的,而且这个索引默认是多层级索引,这个索引是由不同的组键的组合构成的(unique group key combinations)。因为我们并不是总需要返回这种索引,所以我们可以取消这种模式,在调用groupby的时候设定as_index=False即可:

    tips.groupby(['day', 'smoker'], as_index=False).mean()
    
    • 1
    daysmokertotal_billtipsizetip_pct
    0FriNo18.4200002.8125002.2500000.151650
    1FriYes16.8133332.7140002.0666670.174783
    2SatNo19.6617783.1028892.5555560.158048
    3SatYes21.2766672.8754762.4761900.147906
    4SunNo20.5066673.1678952.9298250.160113
    5SunYes24.1200003.5168422.5789470.187250
    6ThurNo17.1131112.6737782.4888890.160298
    7ThurYes19.1905883.0300002.3529410.163863

    当然,我们也可以在上面的结果上直接调用reset_index,这样的话就能得到之前那种多层级索引的结果。不过使用as_index=False方法可以避免一些不必要的计算。

  • 相关阅读:
    基于ANSYS Polyflow的逆向挤出模头设计攻略
    Unity VideoPlayer 指定位置开始播放
    [tsai.shen@mailfence.com].faust勒索病毒的最新威胁:如何恢复您的数据?
    PyTorch搭建LSTM神经网络实现文本情感分析实战(附源码和数据集)
    计组 | 【二 数据的表示和运算】强化阶段 —— 应用题总结
    面试题:SpringBoot 自动装配原理
    Ubuntu20.04 安装配置 Ros2
    HarmonyOS 音视频开发概述
    C语言pow函数简单介绍
    开源创新突破之旅,开放原子开源大赛激励科技前行(4月13-14日获奖名单公布)
  • 原文地址:https://blog.csdn.net/weixin_46530492/article/details/134431864