看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gr99123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
对数据进行分组后,我们就可以收获果实了,除了常见的数学统计方法,还可以使用 agg() 和 transform() 等函数进行操作。
.aggregate() 简写为 .agg()。它的作用是将分组后的对象给定统计方法,也支持按字段分别给定不同的统计方法。
# 所有列使用一个计算计算方法
df.groupby('team').aggregate(sum)
df.groupby('team').agg(sum)
grouped.agg(np.size)
grouped['Q1'].agg(np.mean)
## 多个计算方法
# 所有列指定多个计算方法
grouped.agg([np.sum, np.mean, np.std])
# 指定列使用多个计算方法
grouped['Q1'].agg([sum, np.mean, np.std])
# 一列使用多个计算方法
df.groupby('team').agg({'Q1': ['min', 'max'], 'Q2': 'sum'})
# 指定列名,列表是为原列和方法
df.groupby('team').Q1.agg(Mean='mean', Sum='sum')
df.groupby('team').agg(Mean=('Q1', 'mean'), Sum=('Q2', 'sum'))
df.groupby('team').agg(
    Q1_max=pd.NamedAgg(column='Q1', aggfunc='max'),
    Q2_min=pd.NamedAgg(column='Q2', aggfunc='min')
)
# 如果列名不是有效的 python 变量,则可以用以下方法
df.groupby('team').agg(**{
    '1_max':pd.NamedAgg(column='Q1', aggfunc='max')})
# 聚合结果使用函数
# lambda/函数 所有方法都可以用
def max_min(x):
    return x.max() - x.min()
# 定义函数
df.groupby('team').Q1.agg(Mean='mean',
                          Sum='sum',
                          Diff=lambda x: x.max() - x.min(),
                          Max_min=max_min
                         )
# 不同列不同的计算方法
df.groupby('team').agg({'Q1': sum,  # 总和
                        'Q2': 'count', # 总数
                        'Q3':'mean', # 平均
                        'Q4': max}) # 最大值
# 分组对象使用函数
# 定义函数
def max_min(var):
    return var.max() - var.min()
# 调用函数
df.groupby('team').agg(max_min)
关于 agg 可以查看 agg 聚合操作 详细介绍。
transform 类似于 agg,但不同的是它返回的是一个 df,每个会将原来的值一一替换成统计后的值,比如按组计算平均成绩,那么返回的新 df 中每个学生的成绩就是它所在组的平均成绩。
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]
如果想按组对此组的值填充一个列表值,可以用以下方法:
df.groupby('a').b.transform(lambda x: [x.to_list()]*len(x))
df['a'].map(df.groupby('a')['b'].agg(list))
更加详细的介绍可查看教程pandas transform 数值转换部分。
filter() 筛选后后显示筛选后的原 dataframe,内容是满足组条件的所有明细:
# 值的长度都大于等于 3 的
df.groupby('team').filter(lambda x: len(x) >= 3)
# Q1成绩只要有一个大于97的组
df.groupby(['team']).filter(lambda x: (x['Q1'] > 97).any())
# 所有成员平均成绩大于 60 的组
df.groupby(['team']).filter(lambda x: (x.mean() >= 60).all())
# Q1 所有成员成绩之和超过 1060 的组
df.groupby('team').filter(lambda g: g.Q1.sum() > 1060)
关于 filter 的详细介绍,可以查阅:pandas filter 筛选标签。
对对象应用函数进行处理,以下是对所有的值乘以2:
df.groupby('team').apply(lambda x: x*2)
实现 hive sql 中的 collect_list:
df.groupby('team').apply(lambda x: x['name'].to_list())
# 输出一个 np.array
df.groupby('team').apply(np.array)
我们有个需求,是看每组 Q1 成绩的最高的前五个:
# 各组 Q1(为参数) 成绩的前五个
def first_5(df_, c):
    return df_[c].sort_values(ascending = False).head()
# 调用函数
df.set_index('name').groupby('team').apply(first_5, 'Q1')
# group_keys 可以使分组字段不做为索引
(df.set_index('name')
 .groupby('team', group_keys=False)
 .apply(first_5, 'Q1')
)
可以使用 Series 来使用,索引是列表,值是统计方法。以下使用 lambda 构造一个 Series:
(df.groupby('team')
  .apply(lambda x: pd.Series({
      'Q1_sum'       : x['Q1'].sum(),
      'Q1_max'       : x['Q1'].max(),
      'Q2_mean'      : x['Q2'].mean(),
      'Q4_prodsum'   : (x['Q4'] * x['Q4']).sum()
  }))
)
# 可以定义一个函数
def f_mi(x):
        d = []
        d.append(x['Q1'].sum())
        d.append(x['Q2'].max())
        d.append(x['Q3'].mean())
        d.append((x['Q4'] * x['Q4']).sum())
        return pd.Series(d, index=[['Q1', 'Q2', 'Q3', 'Q4'], 
                                   ['sum', 'max', 'mean', 'prodsum']])
# 使用函数
df.groupby('team').apply(f_mi)
'''
          Q1    Q2         Q3       Q4
         sum   max       mean  prodsum
team
A     1066.0  87.0  51.470588  51129.0
B      975.0  99.0  54.636364  76696.0
C     1056.0  96.0  48.545455  68571.0
D      860.0  97.0  65.315789  87473.0
E      963.0  98.0  44.050000  71317.0
'''
不过以上建议在复杂的需求场景下使用。一行代码可以按分组导出 Excel 文件:
# 一个分组导出一个 Excel 文件
(
    df.groupby('team')
    .apply(lambda x: x.to_excel(f'{x.name}.xlsx'))
)
要注意的是,name 是分组名,不是 name 列,要取同名列的话可以使用切片 x['name']。可以查看此案例 pandas 按分组一个分组导出一个文件 。
关于 apply 的详细介绍,可以查阅:pandas apply 函数应用。
类似于 df 的管道方法,分组对象的管道方法是接收之前的分组对象将同组里的所有数据应用在方法中。
# 最大值和最小值之间的差值
df.groupby('team').pipe(lambda x: x.max() - x.min())
# 使用函数
def mean_diff(x):
    return x.get_group('A').mean() - x.get_group('B').mean()
# 以上定义了 A 组和 B 组平均值的差值
df.groupby('team').pipe(mean_diff)
关于 pipe 的详细介绍,可以查阅:pandas pipe 管道方法。
更新时间:2023-04-15 15:36:38 标签:pandas 分组 函数