文章目录
第四章 分组
首先,为什么要分组?分组的的目的是将一堆数据按照特定的条件划分成几份,再对每一份做相同或者特定的操作,这完全可以用之前我们学到的索引去实现,但是索引的缺点在于这些划分的数据你几乎有多少份就要操作多少次。比如统计年级10个班种每个班级的平均分数,这就需要重复操作十次,而且这些操作大部分是相同或者相似的。分组的作用在于只需要一次就可以把这些事情做完,简化了操作,更符合我们的逻辑。
一、分组模式及其对象
1. 分组的一般模式
分组依据 、 数据来源(要操作的数据,如身高、体重、分数) 、 操作及其返回结果
df.groupby(分组依据)[数据来源].使用操作
比如要实现按性别分组计算平均值可以用如下代码实现
df.groupby('Gender')['Height'].mean()
这里补充一下中位数、平均数、众数三者的区别:
- 中位数median
如果一堆数据中特别大的和特别小的都毕竟多,我们通常使用中位数来计算——排序得来,不受极端数据变化影响,有比较好的代表性。 - 众数
如果很大一部分数据毕竟集中,取众数——反应集中情况,最佳,最受欢迎,最满意都是说的众数。 - 平均数
其他的一般情况(大部分情况)取平均数——计算得来,会因为每一个数的变化而变化。
四分位数(quantile)是中位数的延申,quantile(0.25),quantile(0.5),quantile(0.75)分别是三个分位点
2.分组的本质-按条件列表中元素的值的组合来分组(数据来源组合的unique值)
多属性分组
传入多属性组成的列表即可,比如按学校,年纪,性别进行分组统计体重中位数
复杂逻辑分组
传入判断条件即可
练一练
请根据上下四分位数分割,将体重分为high、normal、low三组,统计身高的均值。
m = df.Weight
cond=m.mask(m>m.quantile(0.75),'high').mask(m<m.quantile(0.25),'low').mask((m>=m.quantile(0.25))&(m<=m.quantile(0.75)),'normal')
df.groupby(cond)['Height'].mean()
3.Groupby对象
- ngroups 得到分组个数
- groups 属性,可以返回从 组名 映射到 组索引列表 的字典
- size表示统计每个组的元素个数
- get_group 方法可以直接获取所在组对应的行,此时必须知道组的具体名字
In [19]: gb.get_group(('Fudan University', 'Freshman')).iloc[:3, :3] # 展示一部分
Out[19]:
School Grade Name
15 Fudan University Freshman Changqiang Yang
28 Fudan University Freshman Gaoqiang Qin
63 Fudan University Freshman Gaofeng Zhao
4. 分组的三大操作
分组的三大操作:聚合、变换和过滤
下面分别介绍相应的 agg 、 transform 和 filter 函数及其操作。
二、聚合函数
解决groupby对象上方法的不足。
1. 内置聚合函数
直接定义在groupby对象的聚合函数(速度快):max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod
2.agg方法
groupby上函数的不足之处:
- 无法同时使用多个函数
- 无法对特定的列使用特定的聚合函数
- 无法使用自定义的聚合函数
- 无法直接对结果的列名在聚合前进行自定义命名
解决:
- 使用多个函数
gb.agg(['sum', 'idxmax', 'skew'])
- 对特定的列使用特定的聚合函数
gb.agg({'Height':['mean','max'], 'Weight':'count'})
- 使用自定义函数
gb.agg(lambda x: x.mean()-x.min())
def my_func(s):
res = 'High'
if s.mean() <= df[s.name].mean():
res = 'Low'
return res
gb.agg(my_func)
- 聚合结果重命名
改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数
三、变换和过滤
区别于groupby的是transform完成的是组内累计操作
1. 变换函数与transform方法
变换函数的返回值为同长度的序列,最常用的内置变换函数是累计函数:cumcount/cumsum/cumprod/cummax/cummin
2. 组索引与过滤
过滤在分组中是对于组的过滤,而索引是对于行的过滤,在第二章中的返回值,无论是布尔列表还是元素列表或者位置列表,本质上都是对于行的筛选,即如果符合筛选条件的则选入结果表,否则不选入。
从概念上说,索引功能是组过滤功能的子集
四、跨列分组
1. apply的引入
过滤操作filter
返回的均值是序列, transform ;
分组后聚合,agg 函数能够处理,聚合函数是逐列处理的,而不能够多列数据同时处理 。由此,引出了 apply 函数来解决这一问题。
2.apply的使用
在设计上, apply 的自定义函数传入参数与 filter 完全一致,只不过后者只允许返回布尔值。
In [38]: def BMI(x):
....: Height = x['Height']/100
....: Weight = x['Weight']
....: BMI_value = Weight/Height**2
....: return BMI_value.mean()
....:
In [39]: gb.apply(BMI)
五、练习
Ex1:汽车数据集
1.太难了,答案写的也太妙了,题意比较难理解。看完答案自己敲了一遍。
2.
total_num=df.shape[0]
top=int(total_num/3)
mid=top
bottom=int(total_num-top-mid)
condition=["top"]*top+["mid"]*mid+["bottom"]*bottom
df.groupby(condition)['Price'].mean()
3.很好解决,这里我还再实践了下聚合函数以及重命名。
4.一开始用的agg,发现不行,得用transform函数。
df.groupby('Type')['HP'].agg(lambda x:(x-min(x))/(max(x)-min(x)))
df.groupby('Type')['HP'].transform(lambda x:(x-min(x))/(max(x)-min(x)))
5.得到的结果是一个series,顺便对结果试验了下更改index和更改名称
Ex2:实现transform函数
有点难,不太会,全程看答案。