Datawhale学习记录4

本文详细介绍了Pandas的分组操作,包括分组模式、Groupby对象的属性和操作,如聚合、变换和过滤。讲解了内置聚合函数、agg方法的使用,以及如何进行自定义函数操作。此外,还探讨了transform方法、filter方法和apply方法在分组操作中的应用,最后通过汽车数据集的练习加深理解。
摘要由CSDN通过智能技术生成

分组模式及其对象

分组的一般模式

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

// A code block
df.groupby('Gender')['Height'].median()
// An highlighted block
Gender
Female    159.6
Male      173.4
Name: Height, dtype: float64

分组依据的本质

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

// A code block
df.groupby(['School', 'Gender'])['Height'].mean()
// An highlighted block
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

Groupby对象

通过ngroups属性,可以访问分为了多少组。

通过groups属性,可以返回从 组名 映射到 组索引列表 的字典。

分组的三大操作------聚合、变换和过滤

聚合函数

内置聚合函数

直接定义在groupby对象的聚合函数,max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod

// A code block
gb.quantile(0.95)
// An highlighted block
Gender
Female    166.8
Male      185.9
Name: Height, dtype: float64

agg方法

虽然在groupby对象上定义了许多方便的函数,但仍然有以下不便之处:

无法同时使用多个函数
无法对特定的列使用特定的聚合函数
无法使用自定义的聚合函数
无法直接对结果的列名在聚合前进行自定义命名
下面说明如何通过agg函数解决这四类问题:

【a】使用多个函数
当使用多个聚合函数时,需要用列表的形式把内置聚合函数的对应的字符串传入,先前提到的所有字符串都是合法的。

// A code block
gb.agg(['sum', 'idxmax', 'skew'])
// An highlighted block
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

【b】对特定的列使用特定的聚合函数
对于方法和列的特殊对应,可以通过构造字典传入agg中实现,其中字典以列名为键,以聚合字符串或字符串列表为值.

// A code block
gb.agg({'Height':['mean','max'], 'Weight':'count'})
// An highlighted block
Height	Weight
mean	max	count
Gender			
Female	159.19697	170.2	135
Male	173.62549	193.9	54

【c】使用自定义函数

在agg中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算 。下面分组计算身高和体重的极差:

// A code block
gb.agg(lambda x: x.mean()-x.min())
// An highlighted block
Height	Weight
Gender		
Female	13.79697	13.918519
Male	17.92549	21.759259

【d】聚合结果重命名

如果想要对结果进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数,现举若干例子说明:

// A code block
gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])
// An highlighted block
Height	Weight
range	my_sum	range	my_sum
Gender				
Female	24.8	21014.0	29.0	6469.0
Male	38.2	8854.9	38.0	3929.0

变换和过滤

变换函数与transform方法

变换函数的返回值为同长度的序列,最常用的内置变换函数是累计函数:cumcount/cumsum/cumprod/cummax/cummin,它们的使用方式和聚合函数类似,只不过完成的是组内累计操作。

// A code block
gb.cummax().head()
// An highlighted block
Height	Weight
0	158.9	46.0
1	166.5	70.0
2	188.9	89.0
3	NaN	46.0
4	188.9	89.0

当用自定义变换时需要使用transform方法,被调用的自定义函数, 其传入值为数据源的序列 ,与agg的传入类型是一致的,其最后的返回结果是行列索引与数据源一致的DataFrame。

// A code block
gb.transform(lambda x: (x-x.mean())/x.std()).head()
// An highlighted block
Height	Weight
0	-0.058760	-0.354888
1	-1.010925	-0.355000
2	2.167063	2.089498
3	NaN	-1.279789
4	0.053133	0.159631

组索引与过滤

在groupby对象中,定义了filter方法进行组的筛选,其中自定义函数的输入参数为数据源构成的DataFrame本身,在之前例子中定义的groupby对象中,传入的就是df[[‘Height’, ‘Weight’]],因此所有表方法和属性都可以在自定义函数中相应地使用,同时只需保证自定义函数的返回为布尔值即可。

// A code block
gb.filter(lambda x: x.shape[0] > 100).head()
// An highlighted block
Height	Weight
0	158.9	46.0
3	NaN	41.0
5	158.0	51.0
6	162.5	52.0
7	161.9	50.0

跨列分组—apply

在设计上,apply的自定义函数传入参数与filter完全一致,只不过后者只允许返回布尔值。除了返回标量之外,apply方法还可以返回一维Series和二维DataFrame,但它们产生的数据框维数和多级索引的层数应当如何变化?下面举三组例子就非常容易明白结果是如何生成的:
【a】标量情况:结果得到的是 Series ,索引与 agg 的结果一致
【b】Series情况:得到的是DataFrame,行索引与标量情况一致,列索引为Series的索引
【c】DataFrame情况:得到的是DataFrame,行索引最内层在每个组原先agg的结果索引上,再加一层返回的DataFrame行索引,同时分组结果DataFrame的列索引和返回的DataFrame列索引一致。

练习

EX1:汽车数据集

// A code block
def normalize(s):
    s_min, s_max = s.min(), s.max()
    res = (s - s_min)/(s_max - s_min)
    return res
df.groupby('Type')['HP'].transform(normalize).head()
// An highlighted block
0    1.00
1    0.54
2    0.00
3    0.58
4    0.80
Name: HP, dtype: float64
// A code block
df.groupby('Type')[['HP', 'Disp.']].apply(lambda x:np.corrcoef(x['HP'].values, x['Disp.'].values)[0,1])
// An highlighted block
Type
Compact    0.586087
Large     -0.242765
Medium     0.370491
Small      0.603916
Sporty     0.871426
Van        0.819881
dtype: float64

EX2:实现transform函数

// A code block
class my_groupby:
    def __init__(self, my_df, group_cols):
        self.my_df = my_df.copy()
        self.groups = my_df[group_cols].drop_duplicates()
        if isinstance(self.groups, pd.Series):
            self.groups = self.groups.to_frame()
        self.group_cols = self.groups.columns.tolist()
        self.groups = {i: self.groups[i].values.tolist() for i in self.groups.columns}
        self.transform_col = None
    def __getitem__(self, col):
        self.pr_col = [col] if isinstance(col, str) else list(col)
        return self
    def transform(self, my_func):
        self.num = len(self.groups[self.group_cols[0]])
        L_order, L_value = np.array([]), np.array([])
        for i in range(self.num):
            group_df = self.my_df.reset_index().copy()
            for col in self.group_cols:
                group_df = group_df[group_df[col]==self.groups[col][i]]
            group_df = group_df[self.pr_col]
            if group_df.shape[1] == 1:
                group_df = group_df.iloc[:, 0]
            group_res = my_func(group_df)
            if not isinstance(group_res, pd.Series):
                group_res = pd.Series(group_res,index=group_df.index,name=group_df.name)
            L_order = np.r_[L_order, group_res.index]
            L_value = np.r_[L_value, group_res.values]
        self.res = pd.Series(pd.Series(L_value, index=L_order).sort_index().values,index=self.my_df.reset_index().index, name=my_func.__name__)
        return self.res

my_groupby(df, 'Type')
// An highlighted block
<__main__.my_groupby at 0x1c4f9c0c208>
// A code block
def f(s):
    res = (s-s.min())/(s.max()-s.min())
    return res
my_groupby(df, 'Type')['Price'].transform(f).head()
// An highlighted block
0    0.733592
1    0.372003
2    0.109712
3    0.186244
4    0.177525
Name: f, dtype: float64

总结

总体感觉这一章的内容相比第三章简单。内容也相对较少。
加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值