一、分组模式及其对象
1. 分组的一般模式
- 想要实现分组操作,必须明确三个要素: 分 组 依 据 \color{#FF0000}{分组依据} 分组依据、 数 据 来 源 \color{#00FF00}{数据来源} 数据来源、 操 作 及 其 返 回 结 果 \color{#0000FF}{操作及其返回结果} 操作及其返回结果。同时从充分性的角度来说,如果明确了这三方面,就能确定一个分组操作,从而分组代码的一般模式即:
df.groupby(分组依据)[数据来源].使用操作
- 现在返回到学生体测的数据集上,如果想要按照性别统计身高中位数,就可以如下写出:
df.groupby('Gender')['Height'].median()
2. 分组依据的本质
- 前面提到的若干例子都是以单一维度进行分组的,比如根据性别,如果对多个维度进行分组,只需在groupby中传入相应列名构成的列表即可.
df.groupby(['School', 'Gender'])['Height'].mean()
- 目前为止,
groupby
的分组依据都是直接可以从列中按照名字获取的,那如果想要通过一定的复杂逻辑来分组,例如根据学生体重是否超过总体均值来分组,同样还是计算身高的均值。
condition = df.Weight > df.Weight.mean()
df.groupby(condition)['Height'].mean()
- 从索引可以看出,其实最后产生的结果就是按照条件列表中元素的值(此处是
True
和False
)来分组 - 最后分组的依据来自于数据来源组合的unique值,通过drop_duplicates就能知道具体的组类别:
3. Groupby对象
- 最终具体做分组操作时,所调用的方法都来自于
pandas
中的groupby
对象,这个对象上定义了许多方法,也具有一些方便的属性。这里列出了2个属性和2个方法,而先前的mean
、median
都是groupby
对象上的方法,这些函数和许多其他函数的操作具有高度相似性,
gb1 = df.groupby(['School', 'Gender'])
gb1
ngroups
属性: 可以访问分为了多少组:
gb1.ngroups # 通过ngroups属性,可以访问分为了多少组
groups
属性: 可以返回从 组 名 \color{#FF0000}{组名} 组名映射到 组 索 引 列 表 \color{#FF0000}{组索引列表} 组索引列表的字典:
res1 = gb1.groups #
res1.keys() # 字典的值由于是索引,元素个数过多,此处只展示字典的键
size()
方法: 当size
作为DataFrame
的属性时,返回的是表长乘以表宽的大小,但在groupby
对象上表示统计每个组的元素个数:
gb1.size() # 在groupby对象上表示统计每个组的元素个数
get_group
方法: 可以直接获取所在组对应的行,此时必须知道组的具体名字
gb1.get_group(('Fudan University', 'Male'))
二、聚合函数
1. 内置聚合函数
- 在介绍agg之前,首先要了解一些直接定义在groupby对象的聚合函数,因为它的速度基本都会经过内部的优化,使用功能时应当优先考虑。根据返回标量值的原则,包括如下函数:
max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod。
- 多行或多列中存在多个最大值或最小值时,idxmax() 与 idxmin() 只返回匹配到的第一个值的 Index
函数 | 描述 |
---|---|
count | 统计非空值数量 |
sum | 汇总值 |
mean | 平均值 |
mad | 平均绝对偏差 |
median | 算数中位数 |
min | 最小值 |
max | 最大值 |
mode | 众数 |
abs | 绝对值 |
prod | 乘积 |
std | 贝塞尔校正的样本标准偏差 |
var | 无偏方差 |
sem | 平均值的标准误差 |
skew | 样本偏度 (第三阶) |
kurt | 样本峰度 (第四阶) |
quantile | 样本分位数 (不同 % 的值) |
cumsum | 累加 |
cumprod | 累乘 |
cummax | 累积最大值 |
cummin | 累积最小值 |
idxmax() | 最大值对应的索引 |
idxmin() | 最小值对应的索引 |
- 这些聚合函数当传入的数据来源包含多个列时,将按照列进行迭代计算:
gb = df.groupby('Gender')[['Height', 'Weight']]
gb.max()
2. agg方法
(1), 使用多个函数
- 当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的。
In [25]: gb.agg(['sum', 'idxmax', 'skew'])
Out[25]:
Height Weight
sum idxmax skew sum idxmax skew
Gender
Female 21014.0 28 -0.219253 6469.0 28 -0.268482
Male 8854.9 193 0.437535 3929.0 2 -0.332393
- 从结果看,此时的列索引为多级索引,第一层为数据源,第二层为使用的聚合方法,分别逐一对列使用聚合,因此结果为6列。
(2), 对特定的列使用特定的聚合函数
- 对于方法和列的特殊对应,可以通过构造字典传入
agg
中实现,其中字典以列名为键,以聚合字符串或字符串列表为值。
In [26]: gb.agg({'Height':['mean','max'], 'Weight':'count'})
Out[26]:
Height Weight
mean max count
Gender
Female 159.19697 170.2 135
Male 173.62549 193.9 54
(3), 使用自定义函数
- 在
agg
中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算 。下面分组计算身高和体重的极差:
In [27]: gb.agg(lambda x: x.mean()-x.min())
Out[27]:
Height Weight
Gender
Female 13.79697 13.918519
Male 17.92549 21.759259
- 由于传入的是序列,因此序列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可。下面的例子是指,如果组的指标均值,超过该指标的总体均值,返回High,否则返回Low。
def my_func(s):
res = 'High'
# print(s, s.name)
if s.mean() <= df[s.name].mean():
res = 'Low'
return res
gb.agg(my_func)
(4), 聚合结果重命名
- 如果想要对结果进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数,现举若干例子说明:
gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])
- 另外需要注意,使用对一个或者多个列使用单个聚合的时候,重命名需要加方括号,否则就不知道是新的名字还是手误输错的内置函数字符串:
gb.agg([('my_sum', 'sum')])
gb.agg({'Height': [('my_func', my_func), 'sum'], 'Weight': [('range', lambda x:x.max())]})