python—group by

原文链接:公众号数据森麟  https://mp.weixin.qq.com/s/SScvQEjgnsSZWna-n-38Sg

01 如何理解pandas中的groupby操作

groupby是pandas中用于数据分析的一个重要功能,其功能与SQL中的分组操作类似,但功能却更为强大。理解groupby的原理可参考官网给出的解释:

其中:

  • split:按照某一原则(groupby字段)进行拆分,相同属性分为一组

  • apply:对拆分后的各组执行相应的转换操作

  • combine:输出汇总转换后的各组结果

02 分组(split)——groupby

groupby首先要指定分组原则,这也是groupby函数的第一步,其常用参数包括:

  • by,分组字段,可以是列名/series/字典/函数,常用为列名

  • axis,指定切分方向,默认为0,表示沿着行切分

  • as_index,是否将分组列名作为输出的索引,默认为True;当设置为False时相当于加了reset_index功能

  • sort,与SQL中groupby操作会默认执行排序一致,该groupby也可通过sort参数指定是否对输出结果按索引排序

import pandas as pd
import numpy as np
df=pd.DataFrame({'班级':['A','B','A','B']
               ,'姓名':['赵赵','马王','马一','马二']
               ,'语文':np.random.rand(4)*100
               ,'math':np.random.rand(4)*100}).round()
df
  班级 姓名 语文 math
0 A 赵赵 25.036201 74.422613
1 B 马王 32.131182 6.105996
2 A 马一 16.424866 46.760133
3 B 马二 59.372510 46.458523
  • 单列作为分组字段,不设置索引
df.groupby('班级',as_index=False).mean()
  班级 语文 math
0 A 29.5 48.5
1 B 60.5 26.5

                     默认as_index=True 等同于  df.groupby('班级').mean()

  语文 math
班级    
A 29.5 48.5
B 60.5 26.5
  • 单列字段的转换格式作为分组字段
df.groupby(df['姓名'].str[0]).mean()
  语文 math
姓名    
52.000000 7.000000
42.666667 47.666667
  • 字典,根据索引对记录进行映射分组
persons={0:'aa',1:'bb',2:'aa',3:'aa'}
df.groupby(persons).mean()
语文 math
aa 42.333333 46.0
bb 53.000000 12.0
  • 函数,根据函数对索引的执行结果进行分组
df.groupby(lambda x:'奇数'if x%2 else '偶数').mean()
  语文 math
偶数 29.5 48.5
奇数 60.5 26.5

03 转换(apply)——agg/apply/transform

分组之后的第二个步骤即为分组转换操作,也就是应用(apply)一定的函数得到相应的结果。常用的执行操作方式有4种:

  1. 直接加聚合函数,但只能实现单一功能,常用聚合函数包括:mean/sum/median/min/max/last/first等,最为简单直接的聚合方式

  2. agg(或aggregate),执行更为丰富的聚合功能,常用列表、字典等形式作为参数
  • 两门课程分别统计平均分和最低分,则可用列表形式传参如下:
df.groupby(['班级','姓名']).agg([np.mean,min])
  语文 math
    mean min mean min
班级 姓名        
A 赵赵 52.0 52.0 7.0 7.0
马一 7.0 7.0 90.0 90.0
B 马二 68.0 68.0 41.0 41.0
马王 53.0 53.0 12.0 12.0
  • 对语文课求平均分和最低分,而数学课求平均分和最高分,则可用字典形式参数:
df.groupby(['班级','姓名']).agg({'语文':[np.mean,min],'math':[np.max,min]})
  语文 math
    mean min amax min
班级 姓名        
A 赵赵 52.0 52.0 7.0 7.0
马一 7.0 7.0 90.0 90.0
B 马二 68.0 68.0 41.0 41.0
马王 53.0 53.0 12.0 12.0

     3、apply,除了agg丰富的可选聚合函数外,apply还可以自定义面向分组的聚合函数

这里apply函数实际上是一个应用非常广泛的转换函数,例如面向series对象,apply函数的处理粒度是series的每个元素(标量);面向dataframe对象,apply函数的处理粒度是dataframe的一行或一列(series对象);而现在面向groupby后的group对象,其处理粒度则是一个分组(dataframe对象)。例如,需要计算每个班级语文平均分与数学平均分之差,则用apply会是一个理想的选择:

df.groupby('班级').apply(lambda x:x['语文'].mean()-x['math'].mean())

班级
A   -19.0
B    34.0
dtype: float64

    4、transform,又一个强大的groupby利器,其与agg和apply的区别相当于SQL中窗口函数和分组聚合的区别:transform并不对数据进行聚合输出,而只是对每一行记录提供了相应聚合结果;而后两者则是聚合后的分组输出

  • 想对比个人成绩与班级平均分,则如下操作会是首选:
dfm=df.groupby(['班级']).transform(np.mean).rename(columns={'语文':'chinese mean','math':'math mean'})
  chinese mean math mean
0 81.0 56.0
1 80.0 58.0
2 81.0 56.0
3 80.0 58.0
pd.concat([df,dfm],axis=1)
  班级 姓名 语文 math chinese mean math mean
0 A 赵赵 52.0 7.0 29.5 48.5
1 B 马王 53.0 12.0 60.5 26.5
2 A 马一 7.0 90.0 29.5 48.5
3 B 马二 68.0 41.0 60.5 26.5
  • 也可以通过mean聚合+merge连接实现:

dfm=df.groupby(['班级']).mean().rename(columns={'语文':'chinese mean','math':'math mean'})

chinese mean math mean
班级    
A 29.5 48.5
B 60.5 26.5
pd.merge(df,dfm,left_on='班级',right_index=True)
班级 姓名 语文 math chinese mean math mean
0 A 赵赵 52.0 7.0 29.5 48.5
2 A 马一 7.0 90.0 29.5 48.5
1 B 马王 53.0 12.0 60.5 26.5
3 B 马二 68.0 41.0 60.5 26.5

04 时间序列的groupby——resample

再次指出,groupby相当于是按照某一规则对数据进行分组聚合,当分组的规则是时间序列时,还存在另一种特殊的分组方式——重采样resample。理解groupby的split-apply-combine三步走处理流程,那么自然也很容易理解resample处理流程:按照时间split——apply——combine。同时,也正因为resample是一种特殊的分组聚合,所以groupby的4种转换操作自然也都适用于resample。

生成以下含有时间序列的样例数据:

df=pd.DataFrame(data={'class':list('ABCD'*15),'score':np.random.randint(60,100,size=60)},
                index=pd.date_range(start='2020-01-01',end='2020-02-29'))
df.head()
  class score
2020-01-01 A 74
2020-01-02 B 93
2020-01-03 C 61
2020-01-04 D 91
2020-01-05 A 94
  • 需统计每15天的平均分数,用resample可实现如下:
df.resample('15D').mean()
  score
2020-01-01 80.333333
2020-01-16 74.866667
2020-01-31 81.533333
2020-02-15 85.666667

当然,这是直接用了聚合函数,更复杂的例如agg、apply和transform等用法也是一样的。换句话说,resample与groupby的核心区别仅在于split阶段:前者按照时间间隔进行分组,而后者是按照定义的某种规则进行分组。

另外,还可将groupby与resample链式使用,但仅可以是resample在groupby之后,反之则会报错。例如:

df.groupby('class').resample('15D').mean()
  score
class    
A 2020-01-01 85.750000
2020-01-16 73.750000
2020-01-31 78.250000
2020-02-15 79.666667
B 2020-01-02 81.750000
2020-01-17 73.500000
2020-02-01 81.500000
2020-02-16 83.666667
C 2020-01-03 73.000000
2020-01-18 74.500000
2020-02-02 87.500000
2020-02-17 93.666667
D 2020-01-04 79.000000
2020-01-19 79.500000
2020-02-03 84.000000
2020-02-18 85.666667

   需要指出,resample等价于groupby操作一般是指下采样过程;同时,resample也支持上采样,此时需设置一定规则进行插值填充。

猜你喜欢

转载自www.cnblogs.com/broccoli919/p/13402736.html