04 pandas 分组

# 1 分组模式及其对象

# 01 分组的一般模式

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

# 02分组依据的本质

# 03 groupby组对象

# 2 聚合函数

# 01 内置聚合函数

# 02 agg

# a. 使用多个函数 以列表形式传入对应字符串

# b. 对特定的列使用特定的聚合函数

# c. 使用自定义函数

# d 聚合结果重命名

# 3 变换和过滤

# 01变换函数与transform

# 常用内置变换函数是累计函数 cumcount/cumsum/cumprod/cummax/cummin

# 用自定义变换时 用transform

# 02 组索引与过滤

# 4 跨列分组

# -*- coding: utf-8 -*-
"""
Created on Thu Sep  1 10:46:10 2022

@author: lenovo
"""
import pandas as pd
import numpy as np
# print(pd.__version__)
path = r'C:\Users\lenovo\Desktop\最近要用\pandas\joyful-pandas\data'
df = pd.read_csv('{}/learn_pandas.csv'.format(path))

# 1 分组模式及其对象
# 01 分组的一般模式
# df.groupby(分组依据)[数据来源].使用操作
df.groupby('Gender')['Height'].median()
# 02分组依据的本质
df.groupby(['School','Gender'])['Height'].mean()

condition = df.Weight > df.Weight.mean()
df.groupby(condition)['Height'].mean()

item = np.random.choice(list('abcd'),df.shape[0])
df.groupby(item)['Height'].mean()

df.groupby([condition,item])['Height'].mean()

df[['School','Gender']].drop_duplicates() # 得到具体的组类别
df.groupby([df['School'],df['Gender']])['Height'].mean()
# 03 groupby组对象
gb = df.groupby(['School','Grade'])
gb.ngroups # 分了多少组
res = gb.groups
res.keys() # 返回从组名映射到组索引列表的字典
gb.size() # 统计每个组的元素个数
gb.get_group(('Fudan University','Freshman')).iloc[:3,:3] # 获取所在组对应行


# 2 聚合函数
# 01 内置聚合函数
# max/min/mean/median/count/all/any/idxmin/idxmax/nunique/skew/quantie/sum/std/var/sem/size/prod
# skew 返回分布的偏斜度。偏斜度表明分布相对于平均值的不对称程度。正偏斜度表明分布的不对称尾部趋向于更多正值。 负偏斜度表明分布的不对称尾部趋向于更多负值。
# sem 返回所请求轴上的平均值的无偏标准误差。统计量的标准误差(SE)(通常是参数的估计值)是其采样分布的标准偏差,或该标准偏差的估计值。如果参数或统计量是平均值,则称为平均值标准误差(SEM)。
# all 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。
# any 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。
# mad 返回所请求轴的值的平均绝对偏差。数据集的平均绝对偏差是每个数据点与平均值之间的平均距离。

gb = df.groupby('Gender')['Height']
gb.idxmax()
gb.quantile()

gb = df.groupby('Gender')[['Height','Weight']]
gb.max()
# 02 agg
# a. 使用多个函数 以列表形式传入对应字符串
gb.agg(['sum','idxmax','skew'])
# b. 对特定的列使用特定的聚合函数
gb.agg({'Height':['mean','max'], 'Weight':'count'})

# c. 使用自定义函数
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)
# d 聚合结果重命名
# .agg([(重命名,函数)])
gb.agg([('range',lambda x:x.max()-x.min()),('my_sum','sum')])
#.agg({列名:[(重命名,函数),函数,函数...]})
gb.agg({'Height':[('my_func',my_func),'sum'],'Weight':lambda x:x.mean()})
gb.agg([('my_sum','sum')])
gb.agg({'Height':[('my_func',my_func),'sum'],'Weight':[('range',lambda x:x.max())]})


# 3 变换和过滤
# 01变换函数与transform
# 常用内置变换函数是累计函数 cumcount/cumsum/cumprod/cummax/cummin
gb.cummax().head() # 遍历行并在每一列中找到最大值
# 用自定义变换时 用transform
gb.transform(lambda x:(x-x.mean())/x.std()).head() 
gb.transform('mean').head()

# 02 组索引与过滤
gb.filter(lambda x:x.shape[0] > 100).head() # 过滤得到所有容量大于100的组

 
# 4 跨列分组
def BMI(x):
    Height = x['Height']/100
    Weight = x['Weight']
    BMI_value = Weight/Height**2
    return BMI_value.mean()
gb.apply(BMI)

gb = df.groupby(['Gender','Test_Number'])[['Height','Weight']]
gb.apply(lambda x: 0)
gb.apply(lambda x:[0,0])
gb.apply(lambda x:pd.Series([0,0], index=['a','b']))
gb.apply(lambda x:pd.DataFrame(np.ones((2,2)),
                               index=['a','b'],
                               columns=pd.Index([('l','q'),('y','x')])))

# Ex1 汽车数据集
df = pd.read_csv('{}/car.csv'.format(path))
df.head(3)
# 筛选出所属Country数超过2个的汽车,即若该汽车的Country在总体数据集中出现次数不超过2则剔除。
res = df.Brand[df.groupby("Country")["Country"].transform(lambda x: x.shape[0] > 2)]
res.values
# df2 = df.groupby('Country')['Country']
# df2.size()
# df2.groups.keys()

# 按Country分组计算价格均值、价格变异系数以及该Country的汽车数量,其中变异系数的计算方法是标准差除以均值,并在结果中把变异系数重命名为CoV。
df.groupby('Country')['Price'].agg([('价格均值','mean'),
                                    ('价格变异系数',lambda x:x.std()/x.mean()),
                                    ('汽车数量','count')])
# 按照表中位置的前三分之一、中间三分之一和后三分之一分组,统计Price的均值。
s = pd.Series(np.empty(df.shape[0]))
l = df.shape[0]
s.iloc[:l//3] = '前三分之一'
s.iloc[l//3+1:l//3*2] = '中间三分之一'
s.iloc[l//3*2+1:] = '后三分之一'
df.groupby(s.values)["Price"].mean()
# 按照类型Type分组,解决如下问题:
# 对Price和HP分别计算最大值和最小值,结果会产生多级列索引,请用下划线连接的方式把多级列索引合并为单层索引。
res = df.groupby('Type').agg({'Price':['max'],'HP':['min']})
res.columns = res.columns.map(lambda x :'_'.join(x))
# 对HP进行组内的min-max归一化,即每个元素减去组内HP的最小值后,再除以组内HP的极差。
def MIN_MAX(x):
    return (x-x.min())/(x.max()-x.min())
df.groupby('Type')['HP'].transform(MIN_MAX).head()


# Ex2 实现transform()函数
# 请按照如下要求实现transform()函数:
# groupby对象的构造方法为my_groupby(df, group_cols)
class my_groupby:
    def __init__(self,_df,group_cols):
        df = _df.copy()
        self.df = df
        if isinstance(group_cols, str):
            group_cols = [group_cols]
        self.used_col = df.columns.difference(pd.Index(group_cols))
        self.groups = df[group_cols].drop_duplicates().values.tolist()
        self.groups_dict = {}
        for items in self.groups:
            condition = np.ones(df.shape[0])
            for i in range(len(items)):
                condition = condition * (df[group_cols[i]] == items[i]).values
            self.groups_dict[tuple(items)] = df.index[condition.astype('bool')]
    def __getitem__(self,col):
        if isinstance(col,str):
            col = [col]
        self.used_col = col
        return self
    def transform(self,f):
        res = []
        for col in self.used_col:
            cur_res = pd.Series(
                np.empty(self.df.shape[0],dtype='int'),
                index=self.df.index,
                name=col)
            for items in self.groups_dict:
                idx = self.groups_dict[items]
                cur_res[idx] = f(self.df.loc[idx,col])
            res.append(cur_res)
        return cur_res if len(self.used_col)==1 else pd.concat(res,axis=1)
    


# TypeError: my_groupby takes no arguments / 'my_groupby' object is not subscriptable
# __init__这个地方前后是两个"_"
df = pd.DataFrame({'A':list('abbbllll'),
                   'B':list('qqqyyyyx'),
                   'C':[1,2,3,4,5,6,7,8],
                   'D':[9,10,11,12,13,14,15,16]})
# 支持单列分组功能(group_cols为单个列名)
my_res = my_groupby(df,'A')['C'].transform(lambda x: x.cumsum())
pd_res = df.groupby('A')['C'].transform('cumsum')
my_res.equals(pd_res)
# 支持多列分组功能(group_cols为列名列表)
my_res = my_groupby(df,'A')[['C','D']].transform(lambda x: x.cumsum())
pd_res = df.groupby('A')[['C','D']].transform('cumsum')
my_res.equals(pd_res)
# 支持标量广播功能
my_res = my_groupby(df,['A','B'])['C','D'].transform(lambda x: x.cumsum())
pd_res = df.groupby(['A','B'])['C','D'].transform('cumsum')
my_res.equals(pd_res)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值