第四章 分组

第四章 分组

import numpy as np
import pandas as pd

一、分组模式及其对象

df.groupby(分组依据)[数据来源].使用操作

例如第一个例子中的代码就应该如下:

df.groupby('Gender')['Longevity'].mean()
df = pd.read_csv('../data/learn_pandas.csv')
df.groupby('Gender')['Height'].median()
Gender
Female    159.6
Male      173.4
Name: Height, dtype: float64

2. 分组依据的本质

根据学校和性别进行分组,统计身高的均值就可以如下写出:

df.groupby(['School', 'Gender'])['Height'].mean()
School                         Gender
Fudan University               Female    158.776923
                               Male      174.212500
Peking University              Female    158.666667
                               Male      172.030000
Shanghai Jiao Tong University  Female    159.122500
                               Male      176.760000
Tsinghua University            Female    159.753333
                               Male      171.638889
Name: Height, dtype: float64

首先写出分组条件:

condition = df.Weight > df.Weight.mean()

然后传入groupby中:

df.groupby(condition)['Height'].mean()
Weight
False    159.034646
True     172.705357
Name: Height, dtype: float64

3. Groupby对象

gb = df.groupby(['School', 'Grade'])
gb
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000024E9F837288>

ngroups可以访问分为了多少组:

gb.ngroups
16
gb.size()
School                         Grade    
Fudan University               Freshman      9
                               Junior       12
                               Senior       11
                               Sophomore     8
Peking University              Freshman     13
                               Junior        8
                               Senior        8
                               Sophomore     5
Shanghai Jiao Tong University  Freshman     13
                               Junior       17
                               Senior       22
                               Sophomore     5
Tsinghua University            Freshman     17
                               Junior       22
                               Senior       14
                               Sophomore    16
dtype: int64

通过get_group方法可以直接获取所在组对应的行,此时必须知道组的具体名字:

gb.get_group(('Fudan University', 'Freshman'))
SchoolGradeNameGenderHeightWeightTransferTest_NumberTest_DateTime_Record
15Fudan UniversityFreshmanChangqiang YangFemale156.049.0N32020/1/10:05:25
28Fudan UniversityFreshmanGaoqiang QinFemale170.263.0N22020/1/70:05:24
63Fudan UniversityFreshmanGaofeng ZhaoFemale152.243.0N22019/10/310:04:00
70Fudan UniversityFreshmanYanquan WangFemale163.555.0N12019/11/190:04:07
73Fudan UniversityFreshmanFeng WangMale176.374.0N12019/9/260:03:31
105Fudan UniversityFreshmanQiang ShiFemale164.552.0N12019/12/110:04:23
108Fudan UniversityFreshmanYanqiang XuFemale152.438.0N12019/12/80:05:03
157Fudan UniversityFreshmanXiaoli LvFemale152.545.0N22019/9/110:04:17
186Fudan UniversityFreshmanYanjuan ZhaoFemaleNaN53.0N22019/10/90:04:21

4. 分组的三大操作

  • 第一个例子中,每一个组返回一个标量值,可以是平均值、中位数、组容量size
  • 第二个例子中,做了原序列的标准化处理,也就是说每组返回的是一个Series类型
  • 第三个例子中,既不是标量也不是序列,返回的整个组所在行的本身,即返回了DataFrame类型

二、聚合函数

1. 内置聚合函数

包括如下函数:max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod

gb = df.groupby('Gender')['Height']
gb.idxmin()
Gender
Female    143
Male      199
Name: Height, dtype: int64
gb.quantile(0.95)
Gender
Female    166.8
Male      185.9
Name: Height, dtype: float64
gb = df.groupby('Gender')[['Height', 'Weight']]
gb.max()
HeightWeight
Gender
Female170.263.0
Male193.989.0

2. agg方法

【a】使用多个函数

gb.agg(['sum', 'idxmax', 'skew'])
HeightWeight
sumidxmaxskewsumidxmaxskew
Gender
Female21014.028-0.2192536469.028-0.268482
Male8854.91930.4375353929.02-0.332393

【b】对特定的列使用特定的聚合函数

gb.agg({'Height':['mean','max'], 'Weight':'count'})
HeightWeight
meanmaxcount
Gender
Female159.19697170.2135
Male173.62549193.954

【c】使用自定义函数

gb.agg(lambda x: x.mean()-x.min())
HeightWeight
Gender
Female13.7969713.918519
Male17.9254921.759259
def my_func(s):
    res = 'High'
    if s.mean() <= df[s.name].mean():
        res = 'Low'
    return res
gb.agg(my_func)
HeightWeight
Gender
FemaleLowLow
MaleHighHigh

【d】聚合结果重命名

gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])
HeightWeight
rangemy_sumrangemy_sum
Gender
Female24.821014.029.06469.0
Male38.28854.938.03929.0
gb.agg({'Height': [('my_func', my_func), 'sum'], 'Weight': lambda x:x.max()})
HeightWeight
my_funcsum<lambda>
Gender
FemaleLow21014.063.0
MaleHigh8854.989.0
gb.agg([('my_sum', 'sum')])
HeightWeight
my_summy_sum
Gender
Female21014.06469.0
Male8854.93929.0
gb.agg({'Height': [('my_func', my_func), 'sum'], 'Weight': [('range', lambda x:x.max())]})
HeightWeight
my_funcsumrange
Gender
FemaleLow21014.063.0
MaleHigh8854.989.0

三、变换和过滤

1. 变换函数与transform方法

gb.cummax().head()
HeightWeight
0158.946.0
1166.570.0
2188.989.0
3NaN46.0
4188.989.0

现对身高和体重进行分组标准化,即减去组均值后除以组的标准差:

gb.transform(lambda x: (x-x.mean())/x.std()).head()
HeightWeight
0-0.058760-0.354888
1-1.010925-0.355000
22.1670632.089498
3NaN-1.279789
40.0531330.159631
gb.transform('mean').head() # 传入返回标量的函数也是可以的
HeightWeight
0159.1969747.918519
1173.6254972.759259
2173.6254972.759259
3159.1969747.918519
4173.6254972.759259

2. 组索引与过滤

gb.filter(lambda x: x.shape[0] > 100).head()
HeightWeight
0158.946.0
3NaN41.0
5158.051.0
6162.552.0
7161.950.0

四、跨列分组

1. apply的引入

之前几节介绍了三大分组操作,但事实上还有一种常见的分组场景,无法用前面介绍的任何一种方法处理,例如现在如下定义身体质量指数BMI:
B M I = W e i g h t H e i g h t 2 {\rm BMI} = {\rm\frac{Weight}{Height^2}} BMI=Height2Weight
其中体重和身高的单位分别为千克和米,需要分组计算组BMI的均值。

首先,这显然不是过滤操作,因此filter不符合要求;其次,返回的均值是标量而不是序列,因此transform不符合要求;最后,似乎使用agg函数能够处理,但是之前强调过聚合函数是逐列处理的,而不能够 多 列 数 据 同 时 处 理 \color{#FF0000}{多列数据同时处理} 。由此,引出了apply函数来解决这一问题。

2. apply的使用

在设计上,apply的自定义函数传入参数与filter完全一致,只不过后者只允许返回布尔值。现如下解决上述计算问题:

def BMI(x):
    Height = x['Height']/100
    Weight = x['Weight']
    BMI_value = Weight/Height**2
    return BMI_value.mean()
gb.apply(BMI)
Gender
Female    18.860930
Male      24.318654
dtype: float64

apply方法还可以返回一维Series和二维DataFrame

【a】标量情况:结果得到的是 Series ,索引与 agg 的结果一致

gb = df.groupby(['Gender','Test_Number'])[['Height','Weight']]
gb.apply(lambda x: 0)
Gender  Test_Number
Female  1              0
        2              0
        3              0
Male    1              0
        2              0
        3              0
dtype: int64
gb.apply(lambda x: [0, 0]) # 虽然是列表,但是作为返回值仍然看作标量
Gender  Test_Number
Female  1              [0, 0]
        2              [0, 0]
        3              [0, 0]
Male    1              [0, 0]
        2              [0, 0]
        3              [0, 0]
dtype: object

【b】Series情况:得到的是DataFrame,行索引与标量情况一致,列索引为Series的索引

gb.apply(lambda x: pd.Series([0,0],index=['a','b']))
ab
GenderTest_Number
Female100
200
300
Male100
200
300
gb.apply(lambda x: pd.DataFrame(np.ones((2,2)), index = ['a','b'], columns=pd.Index([('w','x'),('y','z')])))
wy
xz
GenderTest_Number
Female1a1.01.0
b1.01.0
2a1.01.0
b1.01.0
3a1.01.0
b1.01.0
Male1a1.01.0
b1.01.0
2a1.01.0
b1.01.0
3a1.01.0
b1.01.0

本文参考了Datawhale组队学习资料!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值