pandas 学习task04分组
作者:互联网
这是在datawhale学习小组学习pandas的第四章内容,分组,以下是学习笔记,仅供参考,不喜勿喷
DataWhale
第四章 分组
一、分组模式及其对象
1. 分组的一般模式
df.groupby(分组依据)[数据来源].使用操作 #分组的常用形式
学生体测的数据集上,如果想要按照性别统计身高中位数
import numpy as np
import pandas as pd
df = pd.read_csv(r'C:\Users\zhoukaiwei\Desktop\joyful-pandas\data\learn_pandas.csv').head()
df
School | Grade | Name | Gender | Height | Weight | Transfer | Test_Number | Test_Date | Time_Record | |
---|---|---|---|---|---|---|---|---|---|---|
0 | Shanghai Jiao Tong University | Freshman | Gaopeng Yang | Female | 158.9 | 46.0 | N | 1 | 2019/10/5 | 0:04:34 |
1 | Peking University | Freshman | Changqiang You | Male | 166.5 | 70.0 | N | 1 | 2019/9/4 | 0:04:20 |
2 | Shanghai Jiao Tong University | Senior | Mei Sun | Male | 188.9 | 89.0 | N | 2 | 2019/9/12 | 0:05:22 |
3 | Fudan University | Sophomore | Xiaojuan Sun | Female | NaN | 41.0 | N | 2 | 2020/1/3 | 0:04:08 |
4 | Fudan University | Sophomore | Gaojuan You | Male | 174.0 | 74.0 | N | 2 | 2019/11/6 | 0:05:22 |
df.groupby('Gender')['Height'].median()
Gender
Female 158.9
Male 174.0
Name: Height, dtype: float64
2. 分组依据的本质
根据学校和性别进行分组,统计身高的均值
df.groupby(['School','Gender'])['Height'].mean()
School Gender
Fudan University Female NaN
Male 174.0
Peking University Male 166.5
Shanghai Jiao Tong University Female 158.9
Male 188.9
Name: Height, dtype: float64
根据学生体重是否超过总体均值来分组,同样还是计算身高的均值。
con = df.Weight > df.Weight.mean()
df.groupby(con)['Height'].mean()
Weight
False 158.900000
True 176.466667
Name: Height, dtype: float64
item = np.random.choice(list('abc'), df.shape[0])
df.groupby(item)['Height'].mean()#根据传入的字母序列分组
a 166.5
b 173.9
c 174.0
Name: Height, dtype: float64
如果传入多个序列进入 groupby ,那么最后分组的依据就是这两个序列对应行的唯一组合:
df.groupby([con,item])['Height'].mean()
Weight
False b 158.9
True a 166.5
b 188.9
c 174.0
Name: Height, dtype: float64
分组的依据来自于数据来源组合的unique值,通过 drop_duplicates 就能知道具体的组类别:
df[['School','Gender']].drop_duplicates()
School | Gender | |
---|---|---|
0 | Shanghai Jiao Tong University | Female |
1 | Peking University | Male |
2 | Shanghai Jiao Tong University | Male |
3 | Fudan University | Female |
4 | Fudan University | Male |
df.groupby([df['School'], df['Gender']])['Height'].mean()
School Gender
Fudan University Female NaN
Male 174.0
Peking University Male 166.5
Shanghai Jiao Tong University Female 158.9
Male 188.9
Name: Height, dtype: float64
3. Groupby对象
A = df.groupby(['School', 'Grade'])
A
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001DBF09A1BA8>
A.ngroups#获取分组数
4
#通过 groups 属性,可以返回从 组名 映射到 组索引列表 的字典:
B=A.groups
B.keys()
dict_keys([('Fudan University', 'Sophomore'), ('Peking University', 'Freshman'), ('Shanghai Jiao Tong University', 'Freshman'), ('Shanghai Jiao Tong University', 'Senior')])
当 size 作为 DataFrame 的属性时,返回的是表长乘以表宽的大小,但在 groupby 对象上表示统计每个组的元素个数:
A.size()
School Grade
Fudan University Sophomore 2
Peking University Freshman 1
Shanghai Jiao Tong University Freshman 1
Senior 1
dtype: int64
通过 get_group 方法可以直接获取所在组对应的行,此时必须知道组的具体名字:
A.get_group(('Peking University','Freshman')).iloc[:3,:3]
School | Grade | Name | |
---|---|---|---|
1 | Peking University | Freshman | Changqiang You |
二、聚合函数
1. 内置聚合函数
gb = df.groupby('Gender')['Height']
gb.idxmin()
Gender
Female 0
Male 1
Name: Height, dtype: int64
gb.quantile(0.95)
Gender
Female 158.90
Male 187.41
Name: Height, dtype: float64
2. agg方法
虽然在 groupby 对象上定义了许多方便的函数,但仍然有以下不便之处:
无法同时使用多个函数
无法对特定的列使用特定的聚合函数
无法使用自定义的聚合函数
无法直接对结果的列名在聚合前进行自定义命名
通过 agg 函数解决这四类问题:
【a】使用多个函数
gb.agg(['sum', 'idxmax', 'skew'])#当使用多个聚合函数时,需要用列表的形式把内置聚合函数
#对应的字符串传入,先前提到的所有字符串都是合法的。
sum | idxmax | skew | |
---|---|---|---|
Gender | |||
Female | 158.9 | 0 | NaN |
Male | 529.4 | 2 | 0.927959 |
【b】对特定的列使用特定的聚合函数
对于方法和列的特殊对应,可以通过构造字典传入 agg 中实现,其中字典以列名为键,以聚合字符串或字符串列表为值。
gb.agg({'Height':['mean','max'], 'Weight':'count'})
---------------------------------------------------------------------------
SpecificationError Traceback (most recent call last)
<ipython-input-32-12a17380636d> in <module>
----> 1 gb.agg({'Height':['mean','max'], 'Weight':'count'})
D:\Anaconda3\lib\site-packages\pandas\core\groupby\generic.py in aggregate(self, func, engine, engine_kwargs, *args, **kwargs)
244 # but not the class list / tuple itself.
245 func = maybe_mangle_lambdas(func)
--> 246 ret = self._aggregate_multiple_funcs(func)
247 if relabeling:
248 ret.columns = columns
D:\Anaconda3\lib\site-packages\pandas\core\groupby\generic.py in _aggregate_multiple_funcs(self, arg)
290 # GH 15931
291 if isinstance(self._selected_obj, Series):
--> 292 raise SpecificationError("nested renamer is not supported")
293
294 columns = list(arg.keys())
SpecificationError: nested renamer is not supported
【c】使用自定义函数
在 agg 中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算
gb.agg(lambda x: x.mean()-x.min())#分组计算身高和体重的极差
Gender
Female 0.000000
Male 9.966667
Name: Height, dtype: float64
由于传入的是序列,因此序列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可。下面的例子是指,如果组的指标均值,超过该指标的总体均值,返回High,否则返回Low。
def my_function(s):
res = 'High'
if s.mean() <= df[s.name].mean():
res = 'Low'
return res
gb.agg(my_function)
Gender
Female Low
Male High
Name: Height, dtype: object
【d】聚合结果重命名
如果想要对聚合结果的列名进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数
gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])
range | my_sum | |
---|---|---|
Gender | ||
Female | 0.0 | 158.9 |
Male | 22.4 | 529.4 |
gb.agg({'Height': [(my_function='my_function'), 'sum'],'Weight': lambda x:x.max()})
File "<ipython-input-43-03327d66caa9>", line 1
gb.agg({'Height': [(my_function='my_function'), 'sum'],'Weight': lambda x:x.max()})
^
SyntaxError: invalid syntax
三、变换和过滤
1. 变换函数与transform方法
变换函数的返回值为同长度的序列,最常用的内置变换函数是累计函数: cumcount/cumsum/cumprod/cummax/cummin ,它们的使用方式和聚合函数类似,只不过完成的是组内累计操作。
gb.cummax().head()
0 158.9
1 166.5
2 188.9
3 NaN
4 188.9
Name: Height, dtype: float64
当用自定义变换时需要使用 transform 方法,被调用的自定义函数, 其传入值为数据源的序列 ,与 agg 的传入类型是一致的,其最后的返回结果是行列索引与数据源一致的 DataFrame 。
#身高和体重进行分组标准化,即减去组均值后除以组的标准差
gb.transform(lambda x: (x-x.mean())/x.std()).head()
0 NaN
1 -0.874123
2 1.090461
3 NaN
4 -0.216338
Name: Height, dtype: float64
2. 组索引与过滤
索引和过滤的区别
过滤在分组中是对于组的过滤,而索引是对于行的过滤,在第二章中的返回值,无论是布尔列表还是元素列表或者位置列表,本质上都是对于行的筛选,即如果符合筛选条件的则选入结果表,否则不选入。
组过滤作为行过滤的推广,指的是如果对一个组的全体所在行进行统计的结果返回 True 则会被保留, False 则该组会被过滤,最后把所有未被过滤的组其对应的所在行拼接起来作为 DataFrame 返回。
在 groupby 对象中,定义了 filter 方法进行组的筛选,其中自定义函数的输入参数为数据源构成的 DataFrame 本身,在之前例子中定义的 groupby 对象中,传入的就是 df[[‘Height’, ‘Weight’]] ,因此所有表方法和属性都可以在自定义函数中相应地使用,同时只需保证自定义函数的返回为布尔值即可。
#原表中通过过滤得到所有容量大于100的组:
gb.filter(lambda x: x.shape[0] > 100).head()
Series([], Name: Height, dtype: float64)
四、跨列分组
1. apply的引入
现在如下定义身体质量指数BMI:
BMI=Weight/Height**2
其中体重和身高的单位分别为千克和米,需要分组计算组BMI的均值。
这显然不是过滤操作,因此 filter 不符合要求;其次,返回的均值是标量而不是序列,因此 transform 不符合要求;最后,似乎使用 agg 函数能够处理,但是之前强调过聚合函数是逐列处理的,而不能够 多列数据同时处理 。由此,引出了 apply 函数来解决这一问题。
2. apply的使用
import pandas as pd
import numpy as np
def BMI(x):
Height = x['Height']/100
Weight = x['Weight']
BMI_value = Weight/Height**2
return BMI_value.mean()
df = pd.read_csv(r'C:\Users\zhoukaiwei\Desktop\joyful-pandas\data\learn_pandas.csv')
gb = df.groupby(['School', 'Grade'])
gb.apply(BMI)
School Grade
Fudan University Freshman 19.969411
Junior 20.115806
Senior 19.573687
Sophomore 21.712565
Peking University Freshman 21.225845
Junior 19.674964
Senior 21.577345
Sophomore 17.611119
Shanghai Jiao Tong University Freshman 21.208448
Junior 19.429628
Senior 21.451075
Sophomore 19.772455
Tsinghua University Freshman 18.465769
Junior 21.352761
Senior 20.189919
Sophomore 20.458102
dtype: float64
【 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']))
a | b | ||
---|---|---|---|
Gender | Test_Number | ||
Female | 1 | 0 | 0 |
2 | 0 | 0 | |
3 | 0 | 0 | |
Male | 1 | 0 | 0 |
2 | 0 | 0 | |
3 | 0 | 0 |
【c】 DataFrame 情况:得到的是 DataFrame ,行索引最内层在每个组原先 agg 的结果索引上,再加一层返回的 DataFrame 行索引,同时分组结果 DataFrame 的列索引和返回的 DataFrame 列索引一致。
gb.apply(lambda x: pd.DataFrame(np.ones((2,2)),
index = ['a','b'],
columns=pd.Index([('w','x'),('y','z')])))
w | y | |||
---|---|---|---|---|
x | z | |||
Gender | Test_Number | |||
Female | 1 | a | 1.0 | 1.0 |
b | 1.0 | 1.0 | ||
2 | a | 1.0 | 1.0 | |
b | 1.0 | 1.0 | ||
3 | a | 1.0 | 1.0 | |
b | 1.0 | 1.0 | ||
Male | 1 | a | 1.0 | 1.0 |
b | 1.0 | 1.0 | ||
2 | a | 1.0 | 1.0 | |
b | 1.0 | 1.0 | ||
3 | a | 1.0 | 1.0 | |
b | 1.0 | 1.0 |
五、练习
Ex1:汽车数据集
现有一份汽车数据集,其中 Brand, Disp., HP 分别代表汽车品牌、发动机蓄量、发动机输出。
df = pd.read_csv(r'C:\Users\zhoukaiwei\Desktop\joyful-pandas\data\car.csv')
df.head()
Brand | Price | Country | Reliability | Mileage | Type | Weight | Disp. | HP | |
---|---|---|---|---|---|---|---|---|---|
0 | Eagle Summit 4 | 8895 | USA | 4.0 | 33 | Small | 2560 | 97 | 113 |
1 | Ford Escort 4 | 7402 | USA | 2.0 | 33 | Small | 2345 | 114 | 90 |
2 | Ford Festiva 4 | 6319 | Korea | 4.0 | 37 | Small | 1845 | 81 | 63 |
3 | Honda Civic 4 | 6635 | Japan/USA | 5.0 | 32 | Small | 2260 | 91 | 92 |
4 | Mazda Protege 4 | 6599 | Japan | 5.0 | 32 | Small | 2440 | 113 | 103 |
1.先过滤出所属 Country 数超过2个的汽车,即若该汽车的 Country 在总体数据集中出现次数不超过2则剔除,再按 Country 分组计算价格均值、价格变异系数、该 Country 的汽车数量,其中变异系数的计算方法是标准差除以均值,并在结果中把变异系数重命名为 CoV 。
2.按照表中位置的前三分之一、中间三分之一和后三分之一分组,统计 Price 的均值。
3.对类型 Type 分组,对 Price 和 HP 分别计算最大值和最小值,结果会产生多级索引,请用下划线把多级列索引合并为单层索引。
4.对类型 Type 分组,对 HP 进行组内的 min-max 归一化。
5.对类型 Type 分组,计算 Disp. 与 HP 的相关系数。
df.groupby('Country').filter(lambda x:x.shape[0]>2).groupby('Country')['Price'].agg([(
'CoV', lambda x: x.std()/x.mean()), 'mean', 'count'])
CoV | mean | count | |
---|---|---|---|
Country | |||
Japan | 0.387429 | 13938.052632 | 19 |
Japan/USA | 0.240040 | 10067.571429 | 7 |
Korea | 0.243435 | 7857.333333 | 3 |
USA | 0.203344 | 12543.269231 | 26 |
df.shape[0]
condition = ['Head']*20+['Mid']*20+['Tail']*20
df.groupby(condition)['Price'].mean()
Head 9069.95
Mid 13356.40
Tail 15420.65
Name: Price, dtype: float64
res = df.groupby('Type').agg({'Price': ['max'], 'HP': ['min']})
res.columns = res.columns.map(lambda x:'_'.join(x))
res
Price_max | HP_min | |
---|---|---|
Type | ||
Compact | 18900 | 95 |
Large | 17257 | 150 |
Medium | 24760 | 110 |
Small | 9995 | 63 |
Sporty | 13945 | 92 |
Van | 15395 | 106 |
标签:task04,df,dtype,Height,groupby,分组,gb,pandas 来源: https://blog.csdn.net/qq_36559719/article/details/111708461