# 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)