分组聚合

深入浅出Pandas读书笔记

C6 Pandas分组聚合

6.1 概述

6.1.1 原理

6.1.2 groupby语法

df.groupby()可以按指定字段对DataFrame进行分组, 生成一个分组器对象
各个参数的意义

  • by: 代表分组的依据和方法. 如果by是一个函数, 则会在数据的索引的每个值去调用它, 从而产生值, 按这些值进行分组. 如果传递dict或Series, 则将使用dict或Series的值来确定组, 如果传递ndarray, 则按原样使用这些值来确定组. 传入字典, 键为原索引名, 值为分组名.
  • axis
  • level: 如果轴是多层索引, 则按一个或多个特定的层级进行拆分, 支持数字, 层名及序列
  • as_index: 默认返回带有组标签的对象作为索引
  • sort: 是否对分组进行排序, 传False会让分组数据中第一个出现的值在前, 同时会提高分组性能
  • group_keys
  • obserbed:
  • dropna: 默认会删除NA值
    DataFrame返回DataFrameGroupBy对象, Series返回SeriesGroupBy对象

6.1.3 DataFrame应用分组

df.groupby('team').sum()
# 对不同列采用不同的聚合方式
df.groupby('team').agg({'Q1': sum, 'Q2': 'count', 'Q3': 'mean', 'Q4': max})
# 用一列调用多个聚合方法
df.groupby('team').agg({'Q1': [sum, max, 'std']})

6.1.4 Series应用分组

对Series也可以使用分组聚合, 但先对来说场景叫少. 在下列中, df.Q1是一个Series, 他的分组依据是df.team. 根据groupby语法, 如果传入一个Series, 此Series与被分组数据的索引对齐后, 按Series的值进行分组

# 对Series df.Q1按team分组, 求和
df.Q1.groupby(df.team).sum()

6.2 分组

6.2.1 分组对象

df.groupby('team') # <pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000222810C9220>
df.Q1.groupby(df.team) # <pandas.core.groupby.generic.SeriesGroupBy object at 0x00000222810C9DF0>

6.2.2 按标签分组

grouped = df.groupby('col1')
grouped = df.groupby('col1', axis=1)
grouped = df.groupby(['col1', 'col2'])
# 可以使用get_group()查看分组对象单个分组内容
grouped.get_group('D')

6.2.3 表达式

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

df.groupby(lambda x: x%2==0).sum() # 传入表达式, 表达式会对index进行操作
df.groupby(df.index%2==0).sum() # 同上
# 按索引值是否>50分为两组
df.groupby(lambda x: x>50).sum()
df.groupby(df.index>50).sum()
# 按列名中是否含有Q分为两列
df.groupby(lambda x: 'Q' in x, axis=1).sum()
# 按姓名首字母
df.groupby(df.name.str[0]).sum()
# 按AB和其他分组
df.groupby(df.team.isin(['A', 'B'])).sum()
# 按姓名第一个字母和第二个字母
df.groupby([df.name.str[0], df.name.str[1]]).sum()
# 按日期和小时分组
df.groupby([df.time.date, df.time.hour]).sum()

6.2.4 函数分组

by参数可以调用一个函数来通过计算返回一个分组依据

# 从时间列time中提取年份来分组
df.groupby(df.time.apply(lambda x: x.year)).count()
# 按姓名首字母为元音, 辅音分组
(
    df.groupby(df.name.str.lower().str[0].isin(list('aeiou'))).sum()
    .rename(index={True: '元音', False: '辅音'})
)
# 方法二, 先定义方法, 再将df的name列设为index, 调用方法, 方法会针对index进行聚合
def get_letter_type(letter):
    if letter[0].lower() in 'aeiou':
        return '元音'
    else: 
        return '辅音'
    
df.set_index('name').groupby(get_letter_type).sum()

6.2.5 多种方法混合

# 先按照name, 在按照name的首字母聚合
df.groupby(['team', df.name.apply(get_letter_type)]).sum()

6.2.6 用pipe调用分组方法

df.pipe(pd.DataFrame.groupby, 'team').sum()

6.2.7 分组器Grouper

6.2.8 索引

如果不想让分组字段作为索引, as_index=False

6.2.9 排序

groupby操作后分组字段会成为索引, 数据对索引进行排序, sort=False不想排序

6.3 分组对象的操作

6.3.1 选择分组

分组对象的groups方法会生成一个字典(Pandas定义的PrettyDict), 这个字典包含分组的名称和分组的内容索引列表, 可以使用点的.keys()提取分组名称

df.groupby('team').groups
df.groupby('team').groups.keys() # 查看分组名
# MultiIndex
df.groupby(['team', df.name.str[0]]).get_group(('B', 'A')) # 找到team是B, 首字母是A的记录
# groupby对象.indices
df.groupby('team').indices # 返回与predictDict相似的字典, key是分组条件, values是索引组成的array
df.groupby('team').indices['A'] # 选择A

6.3.2 迭代分组

grouped = df.groupby('team')

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

6.3.3 选择列

# 选择分组中的某一列
grouped.Q1
grouped['Q1']
grouped[['Q1', 'Q2']]
grouped[['Q1', 'Q2']].sum() # 对groupby后的小分组按0轴, 进行sum

6.3.4 应用函数apply()

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

df.groupby('team').apply(lambda x: x*2)
# 实现Hive SQL 中的collect_list函数功能, 即将分组中的一列输出为列表
df.groupby('team').apply(lambda x: x['name'].to_list()) # 将分组好的group对象选择name列, 将name这个series调用to_list(), 返回一个Series, 值是list
# 查看某个组
df.groupby('team').apply(lambda x: x['name'].to_list()).A # 相当于查看Series的某个值
# 查看每组成绩最高的三个
df.set_index('name').groupby('team').apply(lambda x: x.Q1.nlargest(3)) # 希望看到名字, 将name设为index, 对Q1进行nlargest取最大的三个值
df.set_index('name').groupby('team', group_keys=False).apply(lambda x: x.Q1.nlargest(3)) # 通过group_keys=False 不显示分组索引
# 传入一个Series, 映射系列不同的聚合统计方法
(
    df.groupby('team')
    .apply(lambda x: pd.Series({
        'Q1_SUM': x['Q1'].sum(),
        'Q2_min': x['Q2'].min()
    }))
)
# 等同于, 相对来说agg看起来更方便
df.groupby('team').agg(Q1_sum=('Q1', 'sum'), 
                       Q2_min=('Q2', 'min') 
                      )

6.3.5 管道方法pipe()

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

df.groupby('team').pipe(lambda x: x.max() + x.min())

6.3.6 转换方法transform()

transform()类似于agg(), 但与agg()不同的是它返回的是和原始数据相同形状的DataFrame, 会将每个数据原来的值一一替换成统计后的值

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

6.3.7 筛选方法filter()

对组作为整体进行筛选, 如果满足条件, 则整个组会被显示, 经过计算返回一个布尔值, 不是布尔序列, 为真的dataFrame会被显示

# 分组后, 所有分组中有一个Q1>97
df.groupby('team').filter(lambda x: (x.Q1>97).any())
# 分组后, 所有分组按0轴聚合计算mean, 每列mean值都>=48
df.groupby('team').filter(lambda x: (x.mean()>=48).all())
# 分组中Q1的和>1060
df.groupby('team').filter(lambda x: x.Q1.sum()>1060)

6.3.8 其他功能

df.groupby('team').first() # 组内第一个值
df.groupby('team').last() # 组内最后一个值
df.groupby('team').ngroups # 有5个分组
df.groupby('team').ngroup() # 每个元素的分组序号

6.4 聚合统计

6.4.1 描述统计 describe()

6.4.2 统计函数

对分组对象直接使用统计函数, 对分组内的所有数据进行此计算, 最终以DataFrame形式显示数据

df.groupby('team').describe()
df.groupby('team').sum()
df.groupby('team').count()
df.groupby('team').max()
df.groupby('team').min()
df.groupby('team').size()
df.groupby('team').mean()
df.groupby('team').median() # 中位数
df.groupby('team').corr() # 分组后的相关性系数
# groupby没有求众数的方法

6.4.3 聚合方法agg()

df.groupby('team').agg(sum)
# 使用agg为了实现一个字段使用多种统计方法, 不同字段使用不同方法
df.groupby().agg([np.sum, np.mean, np.std])
df.groupby().agg('Q1': [min, max], 'Q2': sum)
# agg 可以指定新列的名字
df.groupby('team').agg(Q1_Mean=('Q1', 'mean'), Q2_Sum=('Q2', 'sum'))
# agg调用自定义函数
def maxmin(s):
    return s.max() - s.min()
df.groupby('team').agg(maxmin=('Q1', lambda x: maxmin(x)))

6.4.4 时序重采样方法 resample()

# data
idx = pd.date_range('2020-1-1', periods=100, freq='T')
df2 = pd.DataFrame(data={'a': [0, 1]*50, 'b': 1, }, index=idx)
# 每20分钟聚合一次, 按照a
df2.groupby('a').resample('20T').sum()

6.4.5 组内头尾值 first(), last()

6.4.6 组内分位数 quantile()

df.groupby('team').quantile() # 同median()

6.4.7 组内差值 diff()

和DataFrame的diff()一样, 分组对象的diff()方法会在组内进行前后数据的差值计算, 并以原DataFrame形状返回数据

6.5 数据分箱 pd.cut(), pd.qcut()

pd.cut(): 根据指定分界点对连续数据进行分箱处理
pd.qcut(): 根据指定区间数量对连续数据进行等宽分箱处理, 每个区间中的数据量是相同的

6.5.1 定界分箱 pd.cut()

pd.cut(df.Q1, bins=[0, 60, 100]) # 返回分箱后的结果Series
pd.cut(df.Q1, bins=[0, 60, 100]).value_counts()
pd.cut(df.Q1, bins=[0, 60, 100], labels=['不及格', '及格'])
pd.cut(df.Q1, bins=[0, 60, 100], right=False) # 右闭

6.5.2 等宽分箱 pd.qcut()

pd.qcut(df.Q1, q=3)

6.6 分组可视化

6.6.1 绘图方法 plot()

6.6.2 直方图 hist()

6.6.3 箱线图 boxplot()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值