首页 > 技术文章 > 10 数据聚合和分组运算

lhjc 2020-05-22 15:02 原文

  • 前言

对数据集进行分组并对各组进行聚合或转换,是数据分析工作的重要环节。Pandas提供的groupby功能以一种自然的方式对数据集进行切片、切块、摘要等操作。具体地,包括以下功能:

  • 计算分组摘要统计,如计数、平均值、标准差,或用户自定义函数。
  • 组内数据转换及运算,如归一化、线性回归、组内排名、子集选取等。
  • 计算透视表或交叉表。
  • 组内分位数分析及其他分析。

《Python for Data Analysis》这本书第9章详细的介绍了这方面的用法,根据书中的章节,这部分知识包括以下四部分:

1.GroupBy Mechanics(groupby技术)

2.Data Aggregation(数据聚合)

3.Group-wise Operation and Transformation(分组级运算和转换)

4.Pivot Tables and Cross-Tabulation(透视表和交叉表)

10.1 GroupBy Mechanics(groupby技术)

Groupby即分组运算,其过程可概括为“split-apply-combine”(拆分-应用-合并)。拆分的对象为pandas对象(Series、DataFrame等);拆分的依据是分组键,可以是列表、数组(长度与待分组的轴一样)、字典、Series、函数、DataFrame列名。

10.1.1 一个简单例子

###exe10.1.1 一个简单例子
ipl_data = {'Team': ['Riders', 'Riders', 'Devils', 'Devils', 'Kings',
         'kings', 'Kings', 'Kings', 'Riders', 'Royals', 'Royals', 'Riders'],
         'Rank': [1, 2, 2, 3, 3,4 ,1 ,1,2 , 4,1,2],
         'Year': [2014,2015,2014,2015,2014,2015,2016,2017,2016,2014,2015,2017],
         'Points':[876,789,863,673,741,812,756,788,694,701,804,690]}
df = pd.DataFrame(ipl_data)

df.groupby('Team') #直接用groupby函数不会输出数据,而是存成了一个DataFrameGroupBy object,类似于字典
df.groupby('Team').groups #查看各组的索引及数据类型
df.groupby('Team').mean() #mean、sum等函数仅对数值型数据进行操作
df.groupby(['Team','Year']).groups #按多个属性分组需加上中括号
df.groupby(['Team','Year']).size() #返回每个分组内值的个数
df.groupby('Year').get_group(2014)#group的属性元素类型若为数字,则可以直接访问数字
df.groupby('Team').filter(lambda x:len(x) >=3)##按Team分类后,选择数据条目至少有3条数据的组的信息

10.1.2 选择单列或部分列进行分组
###exe10.1.2 选择单列或部分列进行分组
df['Points'].groupby([df['Team'],df['Year']]).mean()#便于理解的格式
df.groupby(['Team','Year'])[['Points']].mean() #同上一条命令

 

 10.1.3 分组迭代

###exe10.1.3 分组迭代
##只有1个分组键
grouped=df.groupby('Year') 
for year,group in grouped: #year为分组键,group为组内其他内容
    print(year)
    print(group)

##包含两个分组键
for (k1,k2),group in df.groupby(['Team','Year']):
    print(k1,k2)
    print(group)

 10.1.4 根据dtype对列进行分组

###exe10.1.4 根据dtype对列进行分组
grouped = df.groupby(df.dtypes,axis=1)#设置axis=1,表示对列分组
dict(list(grouped)) #转换成字典格式方便查看内容

  10.1.5 分组键可以为数组,但长度要和待分组的轴一样

###exe10.1.5 分组键可以为数组,但长度要和待分组的轴一样
df = pd.DataFrame({'key1':list('ababa'),
                  'key2': ['one','two','one','two','one'],
                  'data1': np.random.randn(5),
                  'data2': np.random.randn(5)})

states=np.array(['Ohio','California','California','Ohio','Ohio'])
years=np.array([2005,2005,2006,2005,2006])
#states第一层索引,years第二层分层索引
df['data1'].groupby([states,years]).mean()

  10.1.6 通过字典或Series进行分组

###10.1.6 通过字典或Series进行分组
people=pd.DataFrame(np.random.randn(5,5),
                   columns=list('abcde'),
                   index=['Joe','Steve','Wes','Jim','Travis'])

people.ix[2:3,['b','c']]=np.nan 

mapping={'a':'red','b':'red','c':'blue','d':'blue','e':'red','f':'orange'}
by_column=people.groupby(mapping)#必须指明axis=1,否则结果为空DataFrame
by_column.sum()

by_column=people.groupby(mapping,axis=1)#仅对已出现的列名进行对应
by_column.sum()

map_series=pd.Series(mapping)
people.groupby(map_series,axis=1).count() #series类似

  10.1.7 通过函数进行分组

###10.1.7 通过函数进行分组
people.groupby(len).sum()#默认对行操作,对每个行索引进行len,再按长度进行分组
key_list=['one','one','one','two','two'] 
people.groupby([len,key_list]).sum()#相当于两层分组,先以行索引长度为主要分组键,再进行key_list分组

  10.1.8 通过索引级别进行分组

columns=pd.MultiIndex.from_arrays([['US','US','US','JP','JP'],[1,3,5,1,3]],names=['cty','tenor'])
hier_df=pd.DataFrame(np.random.randn(4,5),columns=columns)

hier_df.groupby(level='cty',axis=1).count()#指定level关键字进行分组

 10.2 数据聚合(aggregate)

数据聚合,即任何能从数组产生标量值的数据转换过程。如mean、count、min、sum等,此外可以自定义聚合函数,或是已经定义好的任何方法。Groupby方法后的聚合,是在分组对象上调用聚合方法,再进行汇总。

10.2.1 窗口函数进行聚合

###10.2.1 窗口函数进行聚合
df = pd.DataFrame(np.random.randn(10, 4),
                  index = pd.date_range('1/1/2020', periods=10),
                  columns = ['A', 'B', 'C', 'D'])

df.rolling(window=3).mean()#每列每3行求均值,均值写在第三行,前两行为NaN
df.rolling(window=10).mean()
df.rolling(window=3,min_periods=2).mean() #min_periods设置最小统计个数,小于window值,此处表示先对前两个数求了均值
df.rolling(window=3,min_periods=2,center==True).mean() #center==TRUE表示以当前值为中心,向前向后推一个值求均值

 

 

 

 10.2.2 aggregate函数聚合

###10.2.2 aggregate函数聚合
r = df.rolling(window=3,min_periods=2)  
r['A'].aggregate(np.sum) #对单列滚动求和,aggregate中文含义为合计,总计
r[['A','B']].aggregate(np.sum)#对多列滚动求和
r['A'].aggregate([np.sum,np.mean])#对单列进行多个操作
r[['A','B']].aggregate([np.sum,np.mean])
r.aggregate({'A':np.mean,'B':np.sum})#对不同的列执行不同的操作时以字典形式传入

 

 

 

 10.2.3其他聚合函数

###10.2.3其他聚合函数
df.apply(np.mean)#apply里面的函数不用加括号
df.expanding(min_periods=5).mean()#同window,min_period指定最小多少个来求均值
df.ewm(com=0.5).mean()#指数加权滑动,alpha为0.5,除mean外还可以为corr、std等聚合函数

10.2.4 groupby方法后的聚合

###10.2.4 groupby方法后的聚合
df = pd.DataFrame({'key1':list('ababa'),
                  'key2': ['one','two','one','two','one'],
                  'data1': np.random.randn(5),
                  'data2': np.random.randn(5)})

grouped = df.groupby(['key1','key2'])
grouped_data1 = grouped[['data1']]
grouped_data1.mean()
grouped_data1.agg(['mean','sum','std'])
grouped.agg([("均值",'mean'),("标准差",'std')])#此时()中的第一个元素被用作列名
grouped.agg({'data1':'mean','data2':'max'})#对不同的列使用不同的函数

df.groupby(['key1','key2'],as_index=False).mean()#不用分组键作索引

10.3 分组级运算及转换(transform和apply)

10.3.1 transform函数

##10.3.1 transform函数
people=pd.DataFrame(np.random.randn(5,5),
                   columns=list('abcde'),
                   index=['Joe','Steve','Wes','Jim','Travis'])

key=['one','two','one','two','one']
people.groupby(key).mean()
people.groupby(key).transform(np.mean)#分组计算均值后广播到各个索引值

 

 

 10.3.2 apply函数

df = pd.DataFrame({'key1':list('ababa'),
                  'key2': ['one','two','one','two','one'],
                  'data1': np.random.randn(5),
                  'data2': np.random.randn(5)})
    
def top(df,n=2,column='data1'):
    return df.sort_index(by=column)[-n:]

df.groupby(['key2']).apply(top)
df.groupby(['key1','key2']).apply(top,n=1,column='data2')#注意apply输入格式
df.groupby(['key1','key2'],group_keys=False).apply(top,n=1,column='data2')#不要层次化索引

 

 

10.3.3 分位数和桶(bucket)分析

##cut做相同区间长度划分
frame=pd.DataFrame({'data1':np.random.randn(1000),
                   'data2': np.random.randn(1000)})

factor=pd.cut(frame.data1,4)#将data1按区间长度相等平均分成四个区间,返回每个值所在的区间组成的数组
def get_stats(group):
    return {'min':group.min(),'max':group.max(),'count':group.count(),'mean':group.mean()}

grouped=frame.data2.groupby(factor)
grouped.apply(get_stats).unstack()#unstack将花括号结构变成表格结构,stack相反

 

 

 

 

 

##qcut做相同元素个数划分
grouping=pd.qcut(frame.data1,10,labels=False)#label=false即可值获取分位数的编号,默认labels=True,以区间表示
grouped=frame.data2.groupby(grouping)
grouped.apply(get_stats).unstack()

 10.4 透视表和交叉表

####10.4.1 透视表(pivot_table())
pivot_table(data, values=None, index=None, columns=None,aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')

###10.4.2 交叉表(crosstab())计算分组频率的特殊透视表
pd.crosstab([row,column],group_variable,margins=True)#margins指明是否输出小计项

pivot_table()参数:

 参数表参考博文:https://blog.csdn.net/mingkoukou/article/details/82870960

推荐阅读