分组
- 简介
- 常见apply过程的问题
- groupby函数
- 1.按照某一列来分组
- 获得某个school的信息:
- 2.按照多列来分组
- 3.其他调用方法
- 4. 可通过以下代码查看groupby对象可用的函数
- 5. groupby对象的head和first
- 6. 分组的依据规则
- 根据奇偶行分组
- 若使用的是多层索引,则lambda表达式中输入为元组
- groupby中的[]操作
- 对连续性变量使用cut函数做分组
- 聚合(Aggregation)
- 常用聚合函数
- 同时使用单个或多个聚合函数
- 利用元组来重命名
- 指定哪个函数作用哪个列
- 使用自定义函数
- NamedAgg函数做多个聚合
- 带参数的聚合函数
- 【可无视】使用多个函数,同时至少还带一个参数,wrapper
- 过滤Filteration
- 变换Transformation
- 对组内进行标准化
- 对组内进行缺失值的均值填充
- Apply函数
- 返回值的多样性
- 用apply同时统计多个指标
- 练习题
简介
分组操作流程有三:split , apply , combine
首先基于规则把数据拆分成若干组,然后每一组独立使用函数,最后将每一组结果组合在一起
常见apply过程的问题
- 整合——计算分组的统计量
- 变换——对分组的每个单元的数据做变换,例如标准化
- 过滤——按照某些规则筛选一些组
groupby函数
1.按照某一列来分组
a = df.groupby('School') #按照School列分组
groupby函数会生成一个groupby对象,对象本身不会返回,需要调用其方法才有作用
获得某个school的信息:
a.get_group('S_2').head()
2.按照多列来分组
grouped_mul = df.groupby(['School','Class'])
grouped_mul.get_group(('S_2','C_4')) #必须对应groupby数目
3.其他调用方法
- 组容量
a.size()
- 组数
a.ngroups
- 遍历groupby对象
for name,group in a:
print(name)
display(group.head())
4. 可通过以下代码查看groupby对象可用的函数
print([attr for attr in dir(grouped_single) if not attr.startswith('_')])
5. groupby对象的head和first
- head函数返回每个组的前k行
a.head(2) #返回不同学校的前两行
- first显示以分组为索引的每组的第一个分组信息【等同于head(1)】
a.first()
6. 分组的依据规则
仅需要与被分组数据框长度相同的列表即可,并且支持函数型分组,如下
df.groupby(np.random.choice(['a','b','c'],df.shape[0])).get_group('a').head()
解释:使用np.random.choice随机从abc三个数中找一个生成一个长度为df的长度一致的列表,并按照这个列表进行分组,然后提取属于a组的元素。
df[:5].groupby(lambda x:print(x))
输出:
1101
1102
1103
1104
1105
从代码可以看到,我们利用函数时,传入对象就是索引,因此可以根据这一特性做复杂操作
根据奇偶行分组
df.groupby(lambda x:'奇数行' if not df.index.get_loc(x)%2==1 else '偶数行').groups
输出:
{'偶数行': Int64Index([1102, 1104, 1201, 1203, 1205, 1302, 1304, 2101, 2103, 2105, 2202,
2204, 2301, 2303, 2305, 2402, 2404],
dtype='int64', name='ID'),
'奇数行': Int64Index([1101, 1103, 1105, 1202, 1204, 1301, 1303, 1305, 2102, 2104, 2201,
2203, 2205, 2302, 2304, 2401, 2403, 2405],
dtype='int64', name='ID')}
若使用的是多层索引,则lambda表达式中输入为元组
实现的功能为查看两所学校中男女生分别均分是否及格
反例:这里没有使用元组,只会直接返回均分及格和均分不及格两组
grouped_score = df.set_index(['Gender','School']).sort_index().groupby(lambda x:'均分及格' if math_score[x].mean()>=60 else '均分不及格')
正例:
grouped_score = df.set_index(['Gender','School']).sort_index().\
groupby(lambda x:(x,'均分及格' if math_score[x].mean()>=60 else '均分不及格')
for name,_ in grouped_score:print(name)
输出:
(('F', 'S_1'), '均分及格')
(('F', 'S_2'), '均分及格')
(('M', 'S_1'), '均分及格')
(('M', 'S_2'), '均分不及格')
groupby中的[]操作
- 选出对象的某列或多列的值,不加head还只是一个对象
df.groupby(['Gender','School'])['Math'].head()
df.groupby(['Gender','School'])[['Math','Height']].mean() #求均值
对连续性变量使用cut函数做分组
bins = [0,40,60,80,90,100]
cuts = pd.cut(df['Math'],bins=bins) #可选label添加自定义标签
df.groupby(cuts)['Math'].count()
聚合(Aggregation)
常用聚合函数
聚合的简单理解就是把一堆数,变成一个标量
一共有这几种的聚合函数 | mean | ||
count | std标准差 | var方差 | size |
sem | describe | first | sum |
last | nth取第n行 | min | max |
同时使用单个或多个聚合函数
group_m = grouped_single['Math']
group_m.std().values
group_m.agg(['sum','mean','std'])
利用元组来重命名
(修改名,原名)
group_m.agg([('rename_sum','sum'),('rename_mean','mean')])
指定哪个函数作用哪个列
Math使用mean和max,Height列使用var
grouped_mul.agg({'Math':['mean','max'],'Height':'var'})
使用自定义函数
agg函数的传入是分组逐列进行的,有了这个特性就可以做许多事情
grouped_single['Math'].agg(lambda x:print(x.head(),'间隔'))
通过agg可以容易地实现组内极差计算
grouped_single['Math'].agg(lambda x:x.max()-x.min())
NamedAgg函数做多个聚合
grouped_single.agg(min_score1=pd.NamedAgg(column='Math', aggfunc=R1),
max_score1=pd.NamedAgg(column='Height', aggfunc='max'),
range_score2=pd.NamedAgg(column='Weight', aggfunc=R2)).head()
带参数的聚合函数
def f(s,low,high):
return s.between(low,high).max()
grouped_single['Math'].agg(f,50,52)
【可无视】使用多个函数,同时至少还带一个参数,wrapper
def f_test(s,low,high):
return s.between(low,high).max()
def agg_f(f_mul,name,*args,**kwargs):
def wrapper(x):
return f_mul(x,*args,**kwargs)
wrapper.__name__ = name
return wrapper
new_f = agg_f(f_test,'at_least_one_in_50_52',50,52)
grouped_single['Math'].agg([new_f,'mean']).head()
过滤Filteration
filter函数是用来筛选某些组的(务必记住结果是组的全体),因此传入的值应当是布尔标量
grouped_single[['Math','Physics']].filter(lambda x:(x['Math']>32).all()).head()
all()函数是判断列是否为真,返回布尔值.
grouped_single是以学校名分组,最后是判断每个学校是否符合函数内容,过滤返回
变换Transformation
transform函数中传入的对象是组内的列,并且返回值需要与列长完全一致
grouped_single[['Math','Height']].transform(lambda x:x-x.min()).head()
代码解释:数学和身高会减去自己列的最小值然后替换为这个值
grouped_single[['Math','Height']].transform(lambda x:x.mean()).head()
如果返回了标量值,那么组内的所有元素会被广播为这个值
对组内进行标准化
grouped_single[['Math','Height']].transform(lambda x:(x-x.mean())/x.std()).head()
对组内进行缺失值的均值填充
创建带有空值的df:
df_nan = df[['Math','School']].copy().reset_index()
df_nan.loc[np.random.randint(0,df.shape[0],25),['Math']]=np.nan
df_nan.head()
填充:
df_nan.groupby('School').transform(lambda x: x.fillna(x.mean())).join(df.reset_index()['School']).head()
#因为学校名字是无法求均值的,得到结果是没有School,所以拼接回去
Apply函数
apply函数很灵活,应用广泛,不容易驾驭。
返回值的多样性
- 标量返回值
df[['School','Math','Height']].groupby('School').apply(lambda x:x.max())
- 列表返回值
df[['School','Math','Height']].groupby('School').apply(lambda x:x-x.min()).head()
- 数据框返回值
df[['School','Math','Height']].groupby('School')\
.apply(lambda x:pd.DataFrame({'col1':x['Math']-x['Math'].max(),
'col2':x['Math']-x['Math'].min(),
'col3':x['Height']-x['Height'].max(),
'col4':x['Height']-x['Height'].min()})).head()
用apply同时统计多个指标
借助OrderedDict工具进行快捷的统计
from collections import OrderedDict
def f(df):
data = OrderedDict()
data['M_sum'] = df['Math'].sum()
data['W_var'] = df['Weight'].var()
data['H_mean'] = df['Height'].mean()
return pd.Series(data)
grouped_single.apply(f)