zoukankan      html  css  js  c++  java
  • Pandas中的GroupBy及agg详解


    pandas提供了一个灵活高效的groupby功能,它使你能以一种自然的方式对数据集进行切片、切块、摘要
    等操作。根据一个或多个键(可以是函数、数组或DataFrame列名)拆分pandas对象。计算分组摘要统
    计,如计数、平均值、标准差,或用户自定义函数。对DataFrame的列应用各种各样的函数。应用组内转换
    或其他运算,如规格化、线性回归、排名或选取子集等。计算透视表或交叉表。执行分位数分析以及其他分
    组分析。
    groupby()是一个分组函数,对数据进行分组操作的过程可以概括为:split-apply-combine三步:

    1. 按照键值(key)或者分组变量将数据分组。
    2. 对于每组应用我们的函数,这一步非常灵活,可以是python自带函数,可以是我们自己编写的函数。
    3. 将函数计算后的结果聚合。

      返回值:返回重构格式的DataFrame,特别注意,groupby里面的字段内的数据重构后都会变成索引
      groupby(),一般和sum()、mean()一起使用,如下例:

    官网:https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html

    groupby分组函数:

    DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
    

    by : 接收映射、函数、标签或标签列表;用于确定聚合的组
    axis : 接收 0/1;用于表示沿行(0)或列(1)分割。
    level : 接收int、级别名称或序列,默认为None;如果轴是一个多索引(层次化),则按一个或多个特定级别分组
    as_index : 接收布尔值,默认Ture;Ture则返回以组标签为索引的对象,False则不以组标签为索引

    基本操作

    在进行对groupby函数进行学习之前,首先需要明确的是,通过对DataFrame对象调用groupby()函数返回的结果是一个DataFrameGroupBy对象,而不是一个DataFrame或者Series对象,所以,它们中的一些方法或者函数是无法直接调用的,需要按照GroupBy对象中具有的函数和方法进行调用。

    import pandas as pd
    import numpy as np
    
    df = pd.DataFrame({'key1':list('aabba'),
                      'key2': ['one','two','one','two','one'],
                      'data1': [8,6,2,4,3],
                      'data2': [6,9,5,2,-7]})
    
      key1 key2  data1  data2
    0    a  one      8      6
    1    a  two      6      9
    2    b  one      2      5
    3    b  two      4      2
    4    a  one      3     -7 
    
    grouped = df.groupby('key2')
    print(type(grouped))
    print(grouped)
    
    #输出结果如下:
    <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
    <pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000292E0778B50>
    

    普通分组,单值分组

    按key1分组并求均值

    print(df.groupby('key1').mean(),'
    ')
    
             data1     data2
    key1                    
    a     5.666667  2.666667
    b     3.000000  3.500000 
    
    print(df.groupby('key1',as_index=False).mean(),'
    ')
    #注意使用as_index=False和不使用的区别
      key1     data1     data2
    0    a  5.666667  2.666667
    1    b  3.000000  3.500000
    
    #功能与上一句相同:print(df.groupby(['key1'],as_index=False).mean(),'
    ')
    

    普通分组,多值分组

    按states,years分组并求均值

    states=np.array(['Ohio','California','California','Ohio','Ohio'])
    years=np.array([2005,2005,2006,2005,2006])
    print(df['data1'].groupby([states,years]).mean(),'
    ')
    
    #输出结果如下:
    California  2005    6
                2006    2
    Ohio        2005    6
                2006    3
    

    指定多个列名个单个列名后的区别在于,分组的主键或者索引(indice)将一个是单个主键,另一个则是一个元组的形式:

    grouped = df.groupby('key1')
    grouped_muti = df.groupby([states,years])
    
    print(grouped.size())
    
    key1
    a    3
    b    2
    dtype: int64
    
    print(grouped_muti.size())
    
    California  2005    1
                2006    1
    Ohio        2005    2
                2006    1
    dtype: int64
    
    print(grouped.get_group('a'))
    
      key1 key2  data1  data2
    0    a  one      8      6
    1    a  two      6      9
    4    a  one      3     -7
    
    print(grouped_muti.get_group(('Ohio', 2005)))
      key1 key2  data1  data2
    0    a  one      8      6
    3    b  two      4      2
    
    

    使用Series和字典作为分组

    除了使用上述List类型的数据作为分组依据,还可以使用Series和字典作为分组依据。下面仅以字典类型为例:

    import pandas as pd
    import numpy as np
    data=pd.DataFrame(np.arange(20).reshape(4,5),index=list('1234'),columns=list('12345'))
    

    by_dict={'1':'red','2':'yellow','3':'yellow','4':'black','5':'white'}
    by_dict1={'1':'red','2':'yellow','3':'yellow','5':'white'}
    data_1=data.groupby(by_dict)
    print("按by_dict分组的结果:")
    for key,group in data_1:
        print(key)
        print(group)
    

    data_2=data.groupby(by_dict1)
    print("按by_dict1分组的结果:")
    data_3=data.groupby(by_dict1)
    for key,group in data_3:
        print(key)
        print(group)
    

    注意:

    使用字典或Series作为依据对数据进行分组时,如果行索引或列索引在分组依据(代码中的by_dict和by_dict1变量)中并没有找到对应关系,则对应的行或列是不参与最终的分组的(不是自成一组,可以从by_dict1分组的结果中看出此结论)。
    分组依据中可以出现行索引或列索引中没有出现的值。比如by_dict1中的5
    使用Series和字典时,可以设置axis参数。

    grouped的函数操作

    通过调用get_group()函数可以返回一个按照分组得到的DataFrame对象,所以可以将DataFrameGroupBy对象理解为是多个DataFrame组成的。
    而没有调用get_group()函数之前,此时的数据结构任然是DataFrameGroupBy,此时进行对DataFrameGroupBy按照列名进行索引,
    就可以得到SeriesGroupBy对象,取多个列名,则得到的任然是DataFrameGroupBy对象,这里可以类比DataFrame和Series的关系。

    #A single group can be selected using get_group():
    grouped.get_group("bar")
    #Out: 
         A      B         C         D
    1  bar    one  0.254161  1.511763
    3  bar  three  0.215897 -0.990582
    5  bar    two -0.077118  1.211526
    Or for an object grouped on multiple columns:
    
    #for an object grouped on multiple columns:
    df.groupby(["A", "B"]).get_group(("bar", "one"))
    

    因此,在没有进行调用get_group(),也就是没有取出特定某一组数据之前,此时的数据结构任然是DataFrameGroupBy,其中也有很多函数和方法可以调用,
    如max()、count()、std()等,返回的结果是一个DataFrame对象。
    调用get_group()函数后得到了Series的对象,下面的操作就可以按照Series对象中的函数行了。

    print(grouped.count())
    print(grouped.max()[['Age', 'Score']])
    print(grouped.mean()[['Age', 'Score']])
    

    如果其中的函数无法满足需求,也可以选择使用聚合函数aggregate,传递numpy或者自定义的函数,前提是返回一个聚合值。
    关于使用自定义对数据进行分组时,要注意以下两点:

    1. 除了自定义的函数,python中的内建函数,比如len等,也可以直接用来进行分组。(此处并没有举例)
    2. 这些函数不仅可以作用在索引列上,也可以自己指定作用列。
    3. 指定了自定义函数的作用列之后,作用列中的每个值都会传入到函数中执行。并根据其函数运行结果对原始数据进行分组。

    具体可参考官网的例子:https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html

    gb = df.groupby("key1")
    gb.<TAB>   #(输入gb.后按Tab键,可以看到以下提示:)
    gb.agg        gb.boxplot    gb.cummin     gb.describe   gb.filter     gb.get_group  gb.height     gb.last       gb.median     gb.ngroups
    gb.plot       gb.rank       gb.std        gb.transform  gb.aggregate  gb.count      gb.cumprod    gb.dtype      gb.first      gb.groups 
    gb.hist       gb.max        gb.min        gb.nth        gb.prod       gb.resample   gb.sum        gb.var        gb.apply      gb.cummax
    gb.cumsum     gb.fillna     gb.gender     gb.head       gb.indices    gb.mean       gb.name       gb.ohlc       gb.quantile   gb.size
    gb.tail       gb.weight
    
    def getSum(data):
        total = 0
        for d in data:
            total+=d
        return total
    
    print(grouped.aggregate(np.median))
    print(grouped.aggregate({'Age':np.median, 'Score':np.sum}))
    print(grouped.aggregate({'Age':getSum}))
    

    aggregate函数不同于apply,前者是对所有的数值进行一个聚合的操作,而后者则是对每个数值进行单独的一个操作:

    def addOne(data):
        return data + 1
    
    df['Age'] = df['Age'].apply(addOne)
    df['Age'] = df['Age'].apply(int)
    

    更复杂的agg方法 pd.NamedAgg

    animals = pd.DataFrame({"kind": ["cat", "dog", "cat", "dog"],
    "height": [9.1, 6.0, 9.5, 34.0],
    "weight": [7.9, 7.5, 9.9, 198.0]})
    

    grouped_agg = animals.groupby("kind").agg(min_height=pd.NamedAgg(column="height", aggfunc="min"),
                                          max_height=pd.NamedAgg(column="height", aggfunc="max"),
                                          average_weight=pd.NamedAgg(column="weight", aggfunc=np.mean))
    

    对grouped里的元素进行遍历

    for name, group in grouped:
        print(name)
        print(group)
    

    通过循环,对value进行拼接。

    # 循环拼接
    for key, value in data_group:
        new_data = pd.concat([new_data, value])
    print(new_data)
    

    在x,y轴上进行分组

    Pandas中使用groupby时默认是在axis=0轴上进行分组的,也可以通过设置在axis=1轴上进行分组。

    import pandas as pd
    import numpy as np
    def odd(num):
        return int(num)%2==0
    data=pd.DataFrame(np.arange(20).reshape(4,5),index=list('1234'),columns=list('12345'))
    print("原始数据:")
    print(data)
    data_axis0=data.groupby(odd,axis=0)#默认依据index在odd上的运行结果进行分组
    print("按axis=0进行分组结果如下:")
    for key,group in data_axis0:
        print(key)
        print(group)
    data_axis1=data.groupby(odd,axis=1)#默认依据column在odd上的运行结果进行分组
    print("按axis=1进行分组结果如下:")
    for key,group in data_axis1:
        print(key)
        print(group)
    

    分组频率计数

    使用nunique或value_counts方法获取Series的唯一计数值和计数频率。

    df.groupby('year')['country'].nunique()   #对df按'year'分组,并求出'country'列的唯一值的数目
    

    案例应用

    本例使用在openml.org网站上称为“ credit-g”的数据集。 该数据集由提出贷款申请的客户的许多功能和一个目标变量组成,该目标变量指示信贷是否还清。
    可以在此处下载数据(https://www.openml.org/d/31),也可以使用Scikit-learn API导入数据,如下所示。

    import pandas as pd 
    import numpy as np 
    from sklearn.datasets import fetch_openml 
    
    X,y = fetch_openml(name='credit-g', as_frame=True, return_X_y=True) 
    df = X 
    df['target'] = y 
    print(df)
    #输出:
        checking_status  duration  ... foreign_worker target
    0                <0       6.0  ...            yes   good
    1          0<=X<200      48.0  ...            yes    bad
    2       no checking      12.0  ...            yes   good
    3                <0      42.0  ...            yes   good
    4                <0      24.0  ...            yes    bad
    ..              ...       ...  ...            ...    ...
    995     no checking      12.0  ...            yes   good
    996              <0      30.0  ...            yes   good
    997     no checking      12.0  ...            yes   good
    998              <0      45.0  ...            yes    bad
    999        0<=X<200      45.0  ...            yes   good
    

    将所有内容按工作类型分组并计算了所有数值变量的平均值。 输出显示在代码下方。

    df.groupby(['job']).mean()
    Out[1]: 
                                duration  ...  num_dependents
    job                                   ...                
    unemp/unskilled non res    17.363636  ...        1.136364
    unskilled resident         16.535000  ...        1.260000
    skilled                    21.411111  ...        1.125397
    high qualif/self emp/mgmt  25.168919  ...        1.141892
    

    想要更具体一些,可以取dataframe的一个子集,只计算特定列的统计信息。在下面的代码中,只选择credit_amount。

    df[['job', 'credit_amount']].groupby(['job']).mean()
    Out[2]: 
                               credit_amount
    job                                     
    unemp/unskilled non res      2745.136364
    unskilled resident           2358.520000
    skilled                      3070.965079
    high qualif/self emp/mgmt    5435.493243
    

    也可以按多个变量分组。这里按工作和住房类型计算了平均信贷金额。

    df[['job', 'housing','credit_amount']].groupby(['job', 'housing']).mean()
    Out[3]: 
                                        credit_amount
    job                       housing                
    unemp/unskilled non res   rent        3110.600000
                              own         2739.538462
                              for free    2306.500000
    unskilled resident        rent        2376.947368
                              own         2289.376623
                              for free    3602.000000
    skilled                   rent        3107.226087
                              own         2900.807522
                              for free    4225.587302
    high qualif/self emp/mgmt rent        4558.523810
                              own         5139.436170
                              for free    6836.878788
    

    多聚合
    groupby后面使用agg函数能够计算变量的多个聚合。在下面的代码计算了每个作业组的最小和最大值。

    df[['job', 'credit_amount']].groupby(['job']).agg([min, max])
    Out[4]: 
                              credit_amount         
                                        min      max
    job                                             
    unemp/unskilled non res           609.0  14555.0
    unskilled resident                250.0  11998.0
    skilled                           338.0  15945.0
    high qualif/self emp/mgmt         629.0  18424.0
    

    也可以对不同的列使用不同的聚合。以下计算credit_amount的最小和最大金额以及每种工作类型的平均年龄。

    df[['job', 'credit_amount', 'age']].groupby(['job']).agg( 
    {'credit_amount': ['min', 'max'], 'age': 'mean'})
    Out[15]: 
                              credit_amount                 age
                                        min      max       mean
    job                                                        
    unemp/unskilled non res           609.0  14555.0  40.090909
    unskilled resident                250.0  11998.0  36.540000
    skilled                           338.0  15945.0  34.253968
    high qualif/self emp/mgmt         629.0  18424.0  39.027027
    

    聚合命名
    NamedAgg函数允许为多个聚合提供名称,从而提供更清晰的输出。

    df[['target', 'credit_amount', 'age']].groupby('target').agg( 
    min_credit_amount=pd.NamedAgg('credit_amount', 'min'), 
    max_credit_amount=pd.NamedAgg('credit_amount', 'max'), 
    average_age=pd.NamedAgg('age', 'mean'))
    Out[5]: 
            min_credit_amount  max_credit_amount  average_age
    target                                                   
    good                250.0            15857.0    36.224286
    bad                 433.0            18424.0    33.963333
    

    自定义聚合
    也可以将自定义功能应用于groupby对聚合进行自定义的扩展。例如,如果我们要计算每种工作类型的不良贷款的百分比,我们可以使用下面的代码。

    job_count = df[['job', 'target']].groupby(['job', 'target']).agg({'target': 'count'}) 
    job_percent = job_count.groupby(level=0).apply(lambda x: 
    100 * x / float(x.sum())) 
    job_percent
    Out[13]: 
                                         target
    job                       target           
    unemp/unskilled non res   good    68.181818
                              bad     31.818182
    unskilled resident        good    72.000000
                              bad     28.000000
    skilled                   good    70.476190
                              bad     29.523810
    high qualif/self emp/mgmt good    65.540541
                              bad     34.459459
    

    可视化绘图
    我们可以将pandas 内置的绘图功能添加到GroupBy,以更好地可视化趋势和模式。
    用上一节中创建的代码,以创建堆叠的条形图,以更好地可视化每种工作类型的好坏贷款的分布。

    df.groupby(['job', 'target'])['job'].count().unstack('target').fillna(0).plot(kind='bar', stacked=True)
    

    ————————————————

    参考文件:

    https://blog.csdn.net/jingshuiliushen_zj/article/details/83211650
    https://blog.csdn.net/m0_45210226/article/details/109406562
    https://blog.csdn.net/FrankieHello/article/details/97272990
    https://blog.csdn.net/brucewong0516/article/details/78768443
    https://blog.csdn.net/yeshang_lady/article/details/102488971

  • 相关阅读:
    Elasticsearch 分词
    Elasticsearch:文档乐观锁控制 if_seq_no与if_primary_term
    调用javaAPI访问hive
    sqoop笔记
    hive学习
    添加用户到sudo组
    HTTP协议用的TCP但是只建立单向连接
    Hadoop基本操作
    Hadoop原理介绍
    sed用法
  • 原文地址:https://www.cnblogs.com/treasury-manager/p/14474963.html
Copyright © 2011-2022 走看看