自学内容网 自学内容网

05_Pandas分组聚合

分组

df.groupby()方法可以先按指定字段对DataFrame进行分组,生成一个分组器对象,然后把这个对象的各个字段按一定的聚合方法输出。

groupby()方法最终输出的是一个分组对象,DataFrameGroupBy和SeriesGroupBy(分别是DataFrame和Series应用groupby方法产生的)。分组对象是聚合统计操作的基础。

df.groupby('team')
df.Q1.groupby(df.team)

## 按标签分组

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

grouped = df.groupby('col') #单列
grouped = df.groupby('col',axis='columns') # 按行
grouped = df.groupby(['col1','col2']) # 多列
grouped = df.groupby('team') # 分组
grouped.get_group('D') # 查看D组

## 表达式

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

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

# 列名包含Q的分成一组
df.groupby(lambda x:'Q' in x,axis=1).sum()

df.groupby(df.index%2==0) # 按索引奇偶分为True和False两组
df.groupby(df.team.isin(['A','B'])) # 按照A、B、其他团队分组
df.groupby([df.time.date,df.time.hour]) #按照日期和小时分组

函数分组

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

df.groupby(df.time.apply(lambda x:x.year)).count() # 从时间列time中提取年份来分组
# 按姓名首字母为元音、辅音分组
def get_letter_type(letter):
    if letter[0].lower() in 'aeiou':
        return '元音'
    else:
        return '辅音'
# 使用函数
df.set_index('name').groupby(get_letter_type).sum()

多种方式混合

分组可以按多个依据,在同一次分组中可以混合使用不同的分组方法

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

分组器Grouper

Pandas提供了一个分组器pd.Grouper(),它能完成分组工作,还可以服用分组。

# 分组器语法
pandas.Grouper(key=None,level=None,freq=None,axis=0,sort=False)
# 示例
team_grouper = df.groupby(pd.Grouper('team'))
team_grouper.sum()
# 如果是时间,可以60秒分一组
df.groupby(pd.Grouper(key='date',freq='60s'))

# 轴方向
df.groupby(pd.Grouper(key='date',freq='60s',axis=1))
# 按索引
df.groupby(pd.Grouper(level=1)).sum()
# 多列
df.groupby([pd.Grouper(freq='1M',key='date'),'Buyer']).sum()

索引

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

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

排序

groupby操作后分组字段会成为索引,数据会对索引排序,如果不想排序,可以指定sort=False。

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

分组对象操作

选择分组

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

grouped = df.groupby('team')
grouped.groups # 输出一个字典结构
grouped.keys()  # dict_keys(['A', 'B', 'C', 'D', 'E'])

多层索引

grouped2 = df.groupby(['team',df.name.str[0]])
# 选择B组、姓名以A开头的数据
grouped2.get_group(('B','A')) # 注意参数个数:元组

grouped.insices 返回一个字典,其键位组名,值为本组索引的array格式,可以实现对单分组数据的选取

grouped.indices # 输出字典结构
grouped.indices['A'] #选择A组

迭代分组

# 迭代
for g in grouped:
    print(g)  # 迭代元素是一个元组,是由分组名和分组内容组成
    
for name,grp in grouped:
    print(name) # 分组名
    print(grp)  # 分组数据 dataframe结构

应用函数apply()

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

# 将数据中的所有元素乘以2
df.groupby('team').apply(lambda x:x*2)
# 按分组将一列输出为列表
df.groupby('team').apply(lambda x: x['name'].tolist())

# 查看某个分组
df.groupby('team').apply(lambda x: x['name'].tolist()).A
# 调用函数,实现每组Q1成绩最高的前三个
def first_3(df_,c):
    return df_[c].sort_values(asscending=False).head(3)

df.set_index('name').groupby('team').apply(first_3,'Q1')

传入一个Series,映射系列不同的聚合统计算法

df.groupby('team').apply(lambda x:pd.Series(
    {
        'Q1_sum' : x['Q1'].sum(),
        'Q2_max' : x['Q2'].max(),
        'Q2_mean' : x['Q2'].mean(),
        'Q4_prodsum': (x['Q4']*x['Q4']).sum()
    }
))

管道方法pipe()

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

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

使用函数,通过计算,最终返回的是一个Series

# 定义了A组合B组平均值的差值
def mean_diff(x):
    return x.get_group('A').mean() - x.get_group('B').mean()

# 使用函数
df.groupby('team').pipe(mean_diff)

## 转换方法transform()

transform类似于agg(),与agg不同的是它返回一个与原始数据相同形状的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))

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

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

## 筛选方法filter()

使用filter()对组作为整体进行筛选,如果满足条件,则整个组会被显示。传入它调用函数中的默认变量为每个分组的DataFrame,经过计算,最终返回一个bool值(不是bool序列),为真的DataFrame会全部显示。

#筛选出所在组总平均分大于51的成员
df.groupby('team').filter(lambda x:x.mean(1).mean()>51)
# Q1成绩至少有大于97的组
df.groupby('team').filter(lambda x:(x['Q1'] > 97).any())
# 所有成员平均成绩大于50的组
df.groupby('team').filter(lambda x:(x.select_dtypes(include='number').mean() >= 50).all())
# Q1所有成员成绩之和超过1060的组
df.groupby('team').filter(lambda g:g.Q1.sum() > 1060)

聚合统计

分组对象如同df.describe(),也支持describe(),用来对总体进行描述

# 描述统计
# df.groupby('team').describe()
# 由于列过多,可以进行转置T
df.groupby('team').describe().T

统计函数

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

# 计算各组平均数
grouped = df.groupby('team')
grouped.mean()

# 还支持一下常用的统计方法
grouped.describe() # 描述性统计
grouped.sum() # 求和
grouped.count() # 每组数量,不包含缺失值
grouped.max() #求最大值
grouped.min() # 求最小值
grouped.size() # 分组数量
grouped.mean() # 平均值
grouped.median() # 中位数
grouped.std() # 标准差
grouped.var() # 方差
grouped.corr() # 相关系数
grouped.sem()  # 标准误差
grouped.prod() # 乘积
grouped.cummax() # 每组的累积最大值
grouped.cumsum() # 累加
grouped.cumsum() # 累加
grouped.mad() #平均绝对偏差

聚合方法agg()

分组对象的方法.aggregate()简写为.agg()。它的作用是将分组后的对象给定统计方法,也支持按字段分别给定不同的统计方法。

# 所有列使用一个计算方法
df.groupby('team').aggregate(sum)
df.groupby('team').agg(sum)
grouped.aggregate(np.size)
grouped['Q1'].agg(np.mean)

一个字段多个统计方法

grouped[['Q1','Q3']].agg([np.mean,np.std])

不同列使用不同计算方法,且一个列使用多个计算方法:

grouped.agg({'Q1':['min','max'],'Q2':['sum']})

统计方法可以使用函数

# 聚合结果使用函数
# lambda/函数,所有方法都可以用
def max_min(x):
    return x.max()-x.min()

grouped.Q1.agg(Mean='mean',
               Sum='sum',
               Diff=lambda x:x.max()-x.min(),
               Max_min=max_min)
# 全局应用
grouped.agg(max_min)

其他

### 组内头尾值

grouped.first() # 每组第一个 
grouped.last()  # 每组最后一个

组内分位数

中位数是分位数的一种特殊情形,为二分位。如果需要看其他指定分位的数据,可以使用.quantile()来实现。

df.groupby('team').median() # 中位数
df.groupby('team').quantile()  # 同上
df.groupby('team').quantile(0.5) # 同上

组内差值

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

df.select_dtypes(include='number').groupby(df.team).diff() # 注意字符串

数据分箱

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

定界分箱pd.cut()

pud.cut()可以指定区间将数字进行划分

# 将Q1成绩按60分及以上,60分以下进行分类
pd.cut(df.Q1,bins=[0,60,100])

将分箱结果应用到groupby分组中:

# Series使用
df.Q1.groupby(pd.cut(df.Q1,bins=[0,60,100])).count()
# DataFrame使用
df.groupby(pd.cut(df.Q1,bins=[0,60,100])).count()

其它参数

pd.cut(df.Q1,bins=[0,60,100],labels=False) # 不显示区间,使用数据作为每个箱子的标签(0,1,2,n)
pd.cut(df.Q1,bins=[0,60,100],labels=['及格','不及格']) # 指定签名
pd.cut(df.Q1,bins=[0,60,100],include_lowest=True) # 包含最低部分
pd.cut(df.Q1,bins=[0,89,100],right=False) # 是否是右闭区间[89,100)

等宽分箱pd.qcut()

pd.qcut()可以指定所分区间的数量,Pandas会自动进行分箱

#按Q1成绩分为两组
pd.qcut(df.Q1,q=2)
#应用到分组
df.Q1.groupby(pd.qcut(df.Q1,q=2)).count()
df.groupby(pd.qcut(df.Q1,q=2)).max()

其他参数

pd.qcut(range(5),4)
pd.qcut(range(5),4,labels=False)
# 指定标签名
pd.qcut(range(5),4,labels=['good','medium','bad'])
# 返回箱子标签
pd.qcut(df.Q1,q=2,retbins=True)
# 分箱位小数
pd.qcut(df.Q1,q=2,precision=3)
# 排名分3个层次
pd.qcut(df.Q1.rank(method='first'),3)

原文地址:https://blog.csdn.net/qq_27953479/article/details/143843249

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!