Pandas数据分析groupby函数深度总结(2)
本文为系列(2),总结应用函数转换:apply , agg(regate) , transform 以及 filter,代码采用jupyter notebook演示。
应用函数转换:apply , agg(regate) , transform 以及 filter
系列(1)深度讨论了如何对数据进行各种情况分组,本系列讨论在把分组数据合并前,如何对数据进行映射计算处理。
主要为以下四个函数:
- apply;
- agg(regate);
- transform;
- filter
通常情况下,第1、2个方法使用的比较频繁,例如
group = df.groupby('GROUP')
group.apply(mean)
group.agg(mean)
group['INTERSTING COLUMN'].apply(mean)
group.agg({'INTERSTING COLUMN':mean})
group.mean()
以上所有的处理都ok,但是apply和agg还是有一些区别的。并且更重要的是,还有很强大的两个函数transform和filter很少被使用。
apply
首先从apply函数开始说起。apply函数沿着DataFrame的某个axis进行映射,即安招columns或者rows。并且apply在除了groupby的条件下,也可以使用,如下所示,apply可以直接应用到整个DataFrame。
输入:
_ = pd.DataFrame(
np.random.random((2,6)),
columns=list('ABCDEF')
)
_
输出:
A B C D E F
0 0.984852 0.531421 0.119650 0.634738 0.400878 0.611369
1 0.949040 0.387844 0.280624 0.087846 0.722161 0.045949
输入:
_.apply(sum, axis=0) # axis=0 is default, so you can drop that
输出:
A 0.436743
B 1.694425
C 0.759197
D 0.583244
E 1.509123
F 1.501406
dtype: float64
输入:
_.apply(sum, axis=1)
输出:
0 3.376695
1 3.107443
dtype: float64
apply也可以用在groupby的条件下,因为每个group本质上来说就是一个DataFrame的子集,apply默认是按列进行函数映射。
输入:
df.groupby(
pd.Grouper(key='Date',freq='Y')
)['Sale'].apply(sum)
输出:
Date
2014-12-31 3681
2015-12-31 3800
2016-12-31 3881
2017-12-31 3068
2018-12-31 2478
Freq: A-DEC, Name: Sale, dtype: int64
输入:
df.groupby(
pd.Grouper(key='Date',freq='Y')
)[['Val','Sale']].apply(sum)
输出:
Val Sale
Date
2014-12-31 100422394 3681
2015-12-31 101724648 3800
2016-12-31 101789642 3881
2017-12-31 101957784 3068
2018-12-31 100399962 2478
agg
首先我们来看一张agg函数分组应用数学函数进行映射的示意图:
该图说明在分组应用agg函数后,每个类别都只输出一个最终的映射结果。
例:
输入:
df.groupby('Sales Rep').agg({
'Order Id':'size',
'Val':['sum','mean'],
'Sale':['sum','mean']
})
输出:
Order Id Val Sale
size sum mean sum mean
Sales Rep
Aaron Hendrickson 292 1550608 5310.301370 46 0.157534
Adam Sawyer 324 1587828 4900.703704 48 0.148148
Adele Kimmel 115 527339 4585.556522 20 0.173913
Adrian Daugherty 369 1841274 4989.902439 51 0.138211
Adrianna Shelton 37 186651 5044.621622 8 0.216216
... ... ... ... ... ...
Willie Lin 44 254128 5775.636364 6 0.136364
Willie Rau 95 434918 4578.084211 19 0.200000
Willie Sanchez 309 1525229 4936.016181 52 0.168285
Yvonne Jones 74 416388 5626.864865 12 0.162162
Yvonne Lindsey 67 324334 4840.805970 18 0.268657
499 rows × 5 columns
我们将字典传递给聚合函数,其中key(例如Order Id, Val, Sale)是列的值,(‘size’, [‘sum’,‘mean’], [‘sum’,‘mean’])是格列需要各自进行的映射。请注意,该函数可以是单个函数或函数列表(其中每个都将应用)。另外,请注意agg可以使用函数名(如字符串)或函数(如Python对象)。
目前支持的自带方法如下:
df.mean() # 返回所有列的均值
df.mean(1) # 返回所有行的均值,下同
df.corr() # 返回列与列之间的相关系数
df.count() # 返回每一列中的非空值的个数
df.max() # 返回每一列的最大值
df.min() # 返回每一列的最小值
df.abs() # 绝对值
df.median() # 返回每一列的中位数
df.std() # 返回每一列的标准差, 贝塞尔校正的样本标准偏差
df.var() # 无偏方差
df.sem() # 平均值的标准误差
df.mode() # 众数
df.prod() # 连乘
df.mad() # 平均绝对偏差
df.cumprod() # 累积连乘,累乘
df.cumsum(axis=0) # 累积连加,累加
df.nunique() # 去重数量,不同值的量
df.idxmax() # 每列最大的值的索引名
df.idxmin() # 最小
df.cummax() # 累积最大值
df.cummin() # 累积最小值
df.skew() # 样本偏度 (第三阶)
df.kurt() # 样本峰度 (第四阶)
df.quantile() # 样本分位数 (不同 % 的值)
也可以使用自定义函数,并且还可以直接在聚合字典中修改列名。
例:
输入:
def cr(x):
return round(np.mean(x),2)
aggregation = {
'Potential Sales':('Val','size'),
'Sales':('Sale','sum'),
'Conversion Rate':('Sale',cr)
}
df.groupby('Sales Rep').agg(**aggregation)
输出:
Potential Sales Sales Conversion Rate
Sales Rep
Aaron Hendrickson 292 46 0.16
Adam Sawyer 324 48 0.15
Adele Kimmel 115 20 0.17
Adrian Daugherty 369 51 0.14
Adrianna Shelton 37 8 0.22
... ... ... ...
Willie Lin 44 6 0.14
Willie Rau 95 19 0.20
Willie Sanchez 309 52 0.17
Yvonne Jones 74 12 0.16
Yvonne Lindsey 67 18 0.27
499 rows × 3 columns
结果与下面相同,但比以下方法更有效:
输入:
def cr(x):
return round(np.mean(x),2)
_ = df.groupby('Sales Rep').agg({
'Val':'size',
'Sale':['sum',cr]
})
_.columns = ['Potential Sales','Sales','Conversion Rate']
_
输出:
Potential Sales Sales Conversion Rate
Sales Rep
Aaron Hendrickson 292 46 0.16
Adam Sawyer 324 48 0.15
Adele Kimmel 115 20 0.17
Adrian Daugherty 369 51 0.14
Adrianna Shelton 37 8 0.22
... ... ... ...
Willie Lin 44 6 0.14
Willie Rau 95 19 0.20
Willie Sanchez 309 52 0.17
Yvonne Jones 74 12 0.16
Yvonne Lindsey 67 18 0.27
499 rows × 3 columns
更高阶的agg使用示例:
输入:
```python
# Grouping by 3 evenly cut "Order Value" buckets
df.groupby(
pd.qcut(df['Val'],3,['low','mid','high'])
).agg({'Val':['mean','std'],'Sale':['sum','size']})
输出:
Val Sale
mean std sum size
Val
low 1759.218063 955.198008 5653 33339
mid 5078.456234 957.488032 5577 33336
high 8352.541395 945.261300 5678 33325
输入:
# Grouping by cutom "Order Value" buckets
df.groupby(
pd.cut(df['Val'],[0,3000,5000,7000,10000])
).agg({'Val':['mean','std'],'Sale':['sum','size']})
输出:
Val Sale
mean std sum size
Val
(0, 3000] 1555.837474 839.194392 4913 29220
(3000, 5000] 3998.367283 579.842580 3389 19892
(5000, 7000] 5999.759369 582.543076 3399 20359
(7000, 10000] 8488.592355 868.442376 5207 30529
在高阶的还有:
输入:
df.groupby(
pd.cut(df['Val'],[0,5000,10000],labels=['low','high'])
).agg(
**{'Conversion Rate':pd.NamedAgg(column='Sale',aggfunc=lambda x: sum(x)/len(x))}
)
输出:
Conversion Rate
Val
low 0.169042
high 0.169116
输入:
# grouping by year and a cut oder value column
df.groupby(
[pd.Grouper(key='Date',freq='Y'),pd.qcut(df['Val'],3,['low','mid','high'])]
).agg(
**{'Conversion Rate':pd.NamedAgg(column='Sale',aggfunc=lambda x: sum(x)/len(x))}
).unstack()
输出:
Conversion Rate
Val low mid high
Date
2014-12-31 0.185964 0.185123 0.182244
2015-12-31 0.192487 0.184618 0.191403
2016-12-31 0.193346 0.189693 0.195297
2017-12-31 0.151350 0.151591 0.155423
2018-12-31 0.123520 0.124867 0.127470
transform
agg函数返回的是每个组的一个输出,而transform返回的是与列长度相同的一一映射输出,典型示例如下:
输入:
df.groupby('Sales Rep')['Val'].transform(lambda x: x/sum(x))
输出:
0 0.004991
1 0.005693
2 0.003976
3 0.000799
4 0.003300
...
99995 0.012088
99996 0.000711
99997 0.013741
99998 0.010695
99999 0.001533
Name: Val, Length: 100000, dtype: float64
与agg不同的是,transform通常是为了在原始数据中生成新的一列。
df['%'] = df.groupby('Sales Rep')['Val'].transform(
lambda x: x/sum(x)
)
filter
如图所示为分组后的filter处理过程,可以看到filter主要是为了过滤不符合条件的分组,把复合条件的分组留下。
示例:
输入:
df.groupby('Sales Rep').filter(
lambda x: (x['Val'] * x['Sale']).sum() > 200000
)
输出:
Order Id Company Id Company Name Date Val Sale Sales Rep Sales Rep Id
0 HZSXLI1IS9RGABZW D0AUXPP07H6AVSGD Melancholy Social-Role 2017-10-13 6952 0 William Taylor ZTZA0ZLYZR85PTUJ
1 582WPS3OW8T6YT0R D0AUXPP07H6AVSGD Melancholy Social-Role 2017-09-02 7930 0 William Taylor ZTZA0ZLYZR85PTUJ
2 KRF65MQZBOYG4Y9T D0AUXPP07H6AVSGD Melancholy Social-Role 2016-12-21 5538 1 William Taylor ZTZA0ZLYZR85PTUJ
3 N3EDZ5V1WGSWW828 D0AUXPP07H6AVSGD Melancholy Social-Role 2018-06-03 1113 0 William Taylor ZTZA0ZLYZR85PTUJ
4 QXBC8COXEXGFSPLP D0AUXPP07H6AVSGD Melancholy Social-Role 2014-07-26 4596 0 William Taylor ZTZA0ZLYZR85PTUJ
... ... ... ... ... ... ... ... ...
99129 GVRNNUAPFE0IUBHW BLURX3WQK51RI6P7 Baptismal Tensions 2014-01-11 2677 0 Richard Dugas Y43HS5H6PAP34ER5
99130 GX4S2LXPU3VZOS4Z BLURX3WQK51RI6P7 Baptismal Tensions 2018-05-12 6877 0 Richard Dugas Y43HS5H6PAP34ER5
99131 FU4ELGDQEGSGOHFZ BLURX3WQK51RI6P7 Baptismal Tensions 2014-10-16 9189 0 Richard Dugas Y43HS5H6PAP34ER5
99132 S553MU5UDAUID8TG BLURX3WQK51RI6P7 Baptismal Tensions 2016-05-01 2013 0 Richard Dugas Y43HS5H6PAP34ER5
99133 CV3ANPEYZAGVDGGT BLURX3WQK51RI6P7 Baptismal Tensions 2016-06-20 4543 0 Richard Dugas Y43HS5H6PAP34ER5
68313 rows × 8 columns
示例:
输入:
# Let's add this for verification
df['cr'] = df.groupby('Sales Rep')['Sale'].transform('mean')
df.groupby('Sales Rep').filter(lambda x: x['Sale'].mean() > .3)
输出:
Order Id Company Id Company Name Date Val Sale Sales Rep Sales Rep Id cr
3897 4MWBSVADRWSNLBA0 4D9PJORE7YYNDV2E Qualitative Asimov'S 2014-03-17 2637 0 Teddy Cook 36I1D97CG4V8ATKV 0.315789
3898 8C13U50FF5ZKU1TJ 4D9PJORE7YYNDV2E Qualitative Asimov'S 2015-10-26 9495 0 Teddy Cook 36I1D97CG4V8ATKV 0.315789
3899 D6Y3HVKNT480ADL1 4D9PJORE7YYNDV2E Qualitative Asimov'S 2017-12-09 9156 0 Teddy Cook 36I1D97CG4V8ATKV 0.315789
3900 JXO5XMLWEDZKLGCG 4D9PJORE7YYNDV2E Qualitative Asimov'S 2016-07-05 1219 0 Teddy Cook 36I1D97CG4V8ATKV 0.315789
3901 37PVLZLXMXRCZLNK 4D9PJORE7YYNDV2E Qualitative Asimov'S 2018-03-07 4374 0 Teddy Cook 36I1D97CG4V8ATKV 0.315789
... ... ... ... ... ... ... ... ... ...
94627 H72B7571AMUFAH2A JQ7DP9EX0HY1OKRU Road-Shy Small-Town 2015-08-07 2764 0 Jennifer Peck UTJ4LM946FZQSCN1 0.304348
94628 SFQVIH3PNXVLR7PM JQ7DP9EX0HY1OKRU Road-Shy Small-Town 2014-11-28 1008 0 Jennifer Peck UTJ4LM946FZQSCN1 0.304348
94629 B4GFG6BPT1HOHJOU JQ7DP9EX0HY1OKRU Road-Shy Small-Town 2018-06-27 8999 0 Jennifer Peck UTJ4LM946FZQSCN1 0.304348
94630 7ZO3XQ1C3U3BOM6T JQ7DP9EX0HY1OKRU Road-Shy Small-Town 2015-02-19 2079 0 Jennifer Peck UTJ4LM946FZQSCN1 0.304348
94631 CN1LKURFMOKKK15D JQ7DP9EX0HY1OKRU Road-Shy Small-Town 2018-09-06 1681 0 Jennifer Peck UTJ4LM946FZQSCN1 0.304348
366 rows × 9 columns