6 pandas分组聚合

1.1  groupby语法

df.groupby()方法可以按指定字段对DataFrame进行分组,生成一个分组器对象。分组操作会按指定的规则对数据进行拆分,groupby完成的就是拆分工作。groupby也能对Series完成分组操作。各个参数的意义如下。

1.2  DataFrame应用分组

在下例中,我们对df按team进行了分组,然后对各分组求和:

# 按team分组对应列并相加
df.groupby('team').sum()

如果想对不同列采用不同的聚合计算方式,可以对分组对象使用agg方法:

# 对不同列使用不同的计算方法
df.groupby('team').agg({'Q1': sum,  # 总和               
                         'Q2': 'count', # 总数               
                         'Q3':'mean', # 平均               
                         'Q4': max}) # 最大值

还可以对同一列使用不同的计算方法:

# 对同一列使用不同的计算方法
df.groupby('team').agg({'Q1': [sum, 'std', max],  # 使用三个方法               
         'Q2': 'count', # 总数               
         'Q3':'mean', # 平均               
         'Q4': max}) # 最大值

2.1 按标签分组

最简单的分组方法是指定DataFrame中的一列,按这列的去重数据分组。也可以指定多列,按这几列的排列组合去重进行分组。示例如下:

grouped = df.groupby('col') # 单列
grouped = df.groupby('col', axis='columns') # 按行
grouped = df.groupby(['col1', 'col2']) # 多列

可以使用get_group()查看分组对象单个分组的内容:

# 分组
grouped = df.groupby('team')
# 查看D组
grouped.get_group('D')

2.2 表达式

通过行和列的表达式,生成一个布尔数据的序列,从而将数据分为True和False两组。

# 索引值是否为偶数,分成两组
df.groupby(lambda x:x%2==0).sum()
df.groupby(df.index%2==0).sum() # 同上

以下为按索引值是否大于等于50为标准分为两组:

# 按索引是否大于或等于50分为True和False两组
df.groupby(lambda x:x>=50)
df.groupby(df.index>=50).sum() # 同上

用之前介绍的在查询中用到的筛选条件函数对数据进行分组:

# 按索引奇偶行分为True和False两组
df.groupby(df.index%2==0) # 同上例
# 按姓名首字母分组
df.groupby(df.name.str[0])
# 按A及B、其他团队分组
df.groupby(df.team.isin(['A','B']))
# 按姓名第一个字母和第二个字母分组
df.groupby([df.name.str[0], df.name.str[1]])
# 按日期和小时分组
df.groupby([df.time.date, df.time.hour])

2.3 函数分组

by参数可以调用一个函数来通过计算返回一个分组依据。假如我们有一个时间列,如果按年进行分组,可以简单使用lambda提取年份,如:

# 从时间列time中提取年份来分组
df.groupby(df.time.apply(lambda x:x.year)).count()

如果DataFrame和Series函数接收到的参数是数值,想传入其他列的值,可以与上例一样使用列的apply来调用。接下来,我们实现一个按姓名的首字母为元音、辅音分组的案例。

# 按姓名首字母为元音、辅音分组
def get_letter_type(letter):    
    if letter[0].lower() in 'aeiou':
        return '元音'    
    else:        
        return '辅音'

# 使用函数
df.set_index('name').groupby(get_letter_type).sum()

2.4 多种方法混合

由于分组可以按多个依据,在同一次分组中可以混合使用不同的分组方法。下例中,我们先按team分组,接着调用函数按是否元音字母分组。

# 按team、姓名首字母是否为元音分组
df.groupby(['team', df.name.apply(get_letter_type)]).sum()

2.5 索引

groupby操作后分组字段会成为索引,如果不想让它成为索引,可以使用as_index=False进行设置:

df.groupby('team', as_index=False).sum()

2.6 排序

groupby操作后分组字段会成为索引,数据会对索引进行排序,如果不想排序,可以使用sort=False进行设置。不排序的情况下会按索引出现的顺序排列:

# 不对索引进行排序
df.groupby('team', sort=False).sum()

3 分组对象的操作

创建一个分组对象:

# 分组,为了方便案例介绍,删去name列,分组后全为数字
grouped = df.drop('name', axis=1).groupby('team')

# 应用聚合函数
grouped.sum()

3.1 选择分组

分组对象的groups方法会生成一个字典(其实是Pandas定义的PrettyDict),这个字典包含分组的名称和分组的内容索引列表,然后我们可以使用字典的.keys()方法取出分组名称:

# 查看分组内容
df.groupby('team').groups

# 查看分组名
df.groupby('team').groups.keys()
grouped.indices返回一个字典,其键为组名,值为本组索引的array格式,可以实现对单分组数据的选取:
# 获取分组字典数据
grouped.indices

# 选择A组
grouped.indices['A']

3.2 选择列

# 选择分组后的某一列
grouped.Q1grouped['Q1'] # 同上

# 选择多列
grouped[['Q1','Q2']]

# 对多列进行聚合计算
grouped[['Q1','Q2']].sum()

3.3 应用函数apply()

分组对象使用apply()调用一个函数,传入的是DataFrame,返回一个经过函数计算后的DataFrame、Series或标量,然后再把数据组合。

# 将所有元素乘以2
df.groupby('team').apply(lambda x: x*2)

调用函数,实现每组Q1成绩最高的前三个:

# 各组Q1(为参数)成绩最高的前三个
def first_3(df_, c):
    return df_[c].sort_values(ascending=False).head(3)

# 调用函数
df.set_index('name').groupby('team').apply(first_3, 'Q1')

3.4 管道方法pipe()

类似于DataFrame的管道方法,分组对象的管道方法是接收之前的分组对象,将同组的所有数据应用在方法中,最后返回的是经过函数处理过的返回数据格式。

# 每组最大值和最小值之和
df.groupby('team').pipe(lambda x: x.max() + x.min())

# 定义了A组和B组平均值的差值
def mean_diff(x):
    return x.get_group('A').mean()- x.get_group('B').mean()
# 使用函数
df.groupby('team').pipe(mean_diff)

3.5 转换方法transform()

transform()类似于agg(),但与agg()不同的是它返回的是一个与原始数据相同形状的DataFrame,会将每个数据原来的值一一替换成统计后的值。例如按组计算平均成绩,那么返回的新DataFrame中每个学生的成绩就是它所在组的平均成绩。

# 将所有数据替换成分组中的平均成绩
df.groupby('team').transform(np.mean)

使用函数时,分别传入每个分组的子DataFrame的每一列,经过计算后每列返回一个结果,然后再将每组的这列所有值都替换为此计算结果,最后以原DataFrame形式显示所有数据。以下是一些其他的使用方法。

df.groupby('team').transform(max) # 最大值
df.groupby('team').transform(np.std) # 标准差
# 使用函数,和上一个学生的差值(没有处理姓名列)
df.groupby('team').transform(lambda x: x.shift(-1))
# 函数
def score(gb):
    return (gb - gb.mean()) / gb.std()*10
# 调用
grouped.transform(score)

可以用它来进行按组筛选:

# Q1成绩大于60的组的所有成员
df[df.groupby('team').transform('mean').Q1 > 60]

3.6 筛选方法filter()

使用filter()对组作为整体进行筛选,如果满足条件,则整个组会被显示。传入它调用函数中的默认变量为每个分组的DataFrame,经过计算,最终返回一个布尔值(不是布尔序列),为真的DataFrame全部显示。
我们来看这样一个需求,按团队分组,然后每组的每个季度成绩为本季度的平均分,全年的成绩又是这个季度平均分的平均分,最终需要筛选出团队中分数高于51的所有成员。

# 每组每个季度的平均分
df.groupby('team').mean()
# 每组4个季度的平均分的平均分为本组的总平均分
df.groupby('team').mean().mean(1)
# 筛选出所在组总平均分大于51的成员
df.groupby('team').filter(lambda x: x.mean(1).mean()>51)

其他的一些案例
# Q1成绩至少有一个大于97的组
df.groupby(['team']).filter(lambda x: (x['Q1'] > 97).any())# 所有成员平均成绩大于60的组df.groupby(['team']).filter(lambda x: (x.mean() >= 60).all())# Q1所有成员成绩之和超过106
0的组
df.groupby('team').filter(lambda g: g.Q1.sum() > 1060)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值