续接读书记录——pandas中的分组方法groupby(一)。
6、逐级多列运用聚合函数
1)简单例子
#使用 小费数据集。进行测试
tips = pd.read_csv('/Users/daxu/vsCodePy/study_ws/pydata-book/examples/tips.csv')
# 增加维度属性,小费比例
tips['tip_pct'] = tips['tip'] / tips['total_bill']
#对 day 和 smoker 来进行分组
grouped = tips.groupby(['day', 'smoker'])
grouped_pct = grouped['tip_pct']
#如果传递的是函数或者函数名的列表,你会获得一个列名是这些函数名的DataFrame:
grouped_pct.agg(['std','mean',peak_to_peak])
std mean peak_to_peak
day smoker
Fri No 0.028123 0.151650 0.067349
Yes 0.051293 0.174783 0.159925
Sat No 0.039767 0.158048 0.235193
Yes 0.061375 0.147906 0.290095
Sun No 0.042347 0.160113 0.193226
Yes 0.154134 0.187250 0.644685
Thur No 0.038774 0.160298 0.193350
Yes 0.039389 0.163863 0.151240
2)也可以给各个统计结果的列名重命名
grouped_pct.agg([('foo','std'),('foo1','mean'),('foo2',peak_to_peak)])
foo foo1 foo2
day smoker
Fri No 0.028123 0.151650 0.067349
Yes 0.051293 0.174783 0.159925
Sat No 0.039767 0.158048 0.235193
Yes 0.061375 0.147906 0.290095
Sun No 0.042347 0.160113 0.193226
Yes 0.154134 0.187250 0.644685
Thur No 0.038774 0.160298 0.193350
Yes 0.039389 0.163863 0.151240
3)在多个列上面应用不同的函数
grouped_pct = grouped['tip_pct','total_bill']
#如果传递的是函数或者函数名的列表,你会获得一个列名是这些函数名的DataFrame:
result = grouped_pct.agg(['std','mean',peak_to_peak])
result
tip_pct total_bill
std mean peak_to_peak std mean peak_to_peak
day smoker
Fri No 0.028123 0.151650 0.067349 5.059282 18.420000 10.29
Yes 0.051293 0.174783 0.159925 9.086388 16.813333 34.42
Sat No 0.039767 0.158048 0.235193 8.939181 19.661778 41.08
Yes 0.061375 0.147906 0.290095 10.069138 21.276667 47.74
Sun No 0.042347 0.160113 0.193226 8.130189 20.506667 39.40
Yes 0.154134 0.187250 0.644685 10.442511 24.120000 38.10
Thur No 0.038774 0.160298 0.193350 7.721728 17.113111 33.68
Yes 0.039389 0.163863 0.151240 8.355149 19.190588 32.77
# 产生的DataFrame拥有分层列,与分别聚合每一列,再以列名作为keys参数使用concat将结果拼接在一起的结果相同:
result['tip_pct']
std mean peak_to_peak
day smoker
Fri No 0.028123 0.151650 0.067349
Yes 0.051293 0.174783 0.159925
Sat No 0.039767 0.158048 0.235193
Yes 0.061375 0.147906 0.290095
Sun No 0.042347 0.160113 0.193226
Yes 0.154134 0.187250 0.644685
Thur No 0.038774 0.160298 0.193350
Yes 0.039389 0.163863 0.151240
4)将不同的函数应用到一个或多个列上。要实现这个功能,需要将含有列名与函数对应关系的字典传递给agg:
grouped.agg({'tip': np.max, 'size':'sum'})
tip size
day smoker
Fri No 3.50 9
Yes 4.73 31
Sat No 9.00 115
Yes 10.00 104
Sun No 6.00 167
Yes 6.50 49
Thur No 6.70 112
Yes 5.00 40
result = grouped.agg({'tip_pct':['sum',np.max,'mean'],'size':'sum'})
result
tip_pct size
sum amax mean sum
day smoker
Fri No 0.606602 0.187735 0.151650 9
Yes 2.621746 0.263480 0.174783 31
Sat No 7.112145 0.291990 0.158048 115
Yes 6.212055 0.325733 0.147906 104
Sun No 9.126438 0.252672 0.160113 167
Yes 3.557756 0.710345 0.187250 49
Thur No 7.213414 0.266312 0.160298 112
Yes 2.785676 0.241255 0.163863 40
#只有多个函数应用于至少一个列时,DataFrame才具有分层列。
result['size']
sum
day smoker
Fri No 9
Yes 31
Sat No 115
Yes 104
Sun No 167
Yes 49
Thur No 112
Yes 40
# 自动去掉了object类型的字段 ,并且 index模式是 分组列 ,不方便操作
index_group = tips.groupby(['day','smoker']).mean()
index_group
total_bill tip size tip_pct
day smoker
Fri No 18.420000 2.812500 2.250000 0.151650
Yes 16.813333 2.714000 2.066667 0.174783
Sat No 19.661778 3.102889 2.555556 0.158048
Yes 21.276667 2.875476 2.476190 0.147906
Sun No 20.506667 3.167895 2.929825 0.160113
Yes 24.120000 3.516842 2.578947 0.187250
Thur No 17.113111 2.673778 2.488889 0.160298
Yes 19.190588 3.030000 2.352941 0.163863
4)不使用索引进行分组,调用reset_index()
index_group.reset_index()
day smoker total_bill tip size tip_pct
0 Fri No 18.420000 2.812500 2.250000 0.151650
1 Fri Yes 16.813333 2.714000 2.066667 0.174783
2 Sat No 19.661778 3.102889 2.555556 0.158048
3 Sat Yes 21.276667 2.875476 2.476190 0.147906
4 Sun No 20.506667 3.167895 2.929825 0.160113
5 Sun Yes 24.120000 3.516842 2.578947 0.187250
6 Thur No 17.113111 2.673778 2.488889 0.160298
7 Thur Yes 19.190588 3.030000 2.352941 0.163863
大多数情况下可以通过向groupby传递as_index=False来禁用分组键作为索引的行为:
tips.groupby(['day','smoker'], as_index=False).mean()
day smoker total_bill tip size tip_pct
0 Fri No 18.420000 2.812500 2.250000 0.151650
1 Fri Yes 16.813333 2.714000 2.066667 0.174783
2 Sat No 19.661778 3.102889 2.555556 0.158048
3 Sat Yes 21.276667 2.875476 2.476190 0.147906
4 Sun No 20.506667 3.167895 2.929825 0.160113
5 Sun Yes 24.120000 3.516842 2.578947 0.187250
6 Thur No 17.113111 2.673778 2.488889 0.160298
7 Thur Yes 19.190588 3.030000 2.352941 0.163863
7、应用: 通用拆分-应用-联合 groupby的 apply应用
1)简单例子
def top(df, n=5, column='tip_pct'):
return df.sort_values(by=column)[:n]
top(tips)
total_bill tip smoker day time size tip_pct
237 32.83 1.17 Yes Sat Dinner 2 0.035638
102 44.30 2.50 Yes Sat Dinner 3 0.056433
57 26.41 1.50 No Sat Dinner 2 0.056797
0 16.99 1.01 No Sun Dinner 2 0.059447
187 30.46 2.00 Yes Sun Dinner 5 0.065660
#我们使用groupby 分组,还可以得到每个组的 结果:
#这里发生了什么?top函数在DataFrame的每一行分组上被调用,之后使用pandas. concat将函数结果粘贴在一起,并使用分组名作为各组的标签。因此结果包含一个分层索引,该分层索引的内部层级包含原DataFrame的索引值
tips.groupby(['smoker']).apply(top)
total_bill tip smoker day time size tip_pct
smoker
No 57 26.41 1.50 No Sat Dinner 2 0.056797
0 16.99 1.01 No Sun Dinner 2 0.059447
48 28.55 2.05 No Sun Dinner 3 0.071804
146 18.64 1.36 No Thur Lunch 3 0.072961
130 19.08 1.50 No Thur Lunch 2 0.078616
Yes 237 32.83 1.17 Yes Sat Dinner 2 0.035638
102 44.30 2.50 Yes Sat Dinner 3 0.056433
187 30.46 2.00 Yes Sun Dinner 5 0.065660
210 30.06 2.00 Yes Sat Dinner 3 0.066534
240 27.18 2.00 Yes Sat Dinner 2 0.073584
2)调用函数式添加函数参数
tips.groupby(['smoker', 'day']).apply(top, n=1, column='total_bill')
total_bill tip smoker day time size tip_pct
smoker day
No Fri 99 12.46 1.50 No Fri Dinner 2 0.120385
Sat 111 7.25 1.00 No Sat Dinner 1 0.137931
Sun 6 8.77 2.00 No Sun Dinner 2 0.228050
Thur 149 7.51 2.00 No Thur Lunch 2 0.266312
Yes Fri 92 5.75 1.00 Yes Fri Dinner 2 0.173913
Sat 67 3.07 1.00 Yes Sat Dinner 1 0.325733
Sun 172 7.25 5.15 Yes Sun Dinner 2 0.710345
Thur 196 10.34 2.00 Yes Thur Lunch 2 0.193424
3)使用apply时,不用分组键做index:
#压缩分组键, 可以看到所得到的对象具有分组键所形成的分层索引以及每个原始对象的索引。你可以通过向groupby传递group_keys=False来禁用这个功能:
tips.groupby('smoker', group_keys=False).apply(top)
total_bill tip smoker day time size tip_pct
57 26.41 1.50 No Sat Dinner 2 0.056797
0 16.99 1.01 No Sun Dinner 2 0.059447
48 28.55 2.05 No Sun Dinner 3 0.071804
146 18.64 1.36 No Thur Lunch 3 0.072961
130 19.08 1.50 No Thur Lunch 2 0.078616
237 32.83 1.17 Yes Sat Dinner 2 0.035638
102 44.30 2.50 Yes Sat Dinner 3 0.056433
187 30.46 2.00 Yes Sun Dinner 5 0.065660
210 30.06 2.00 Yes Sat Dinner 3 0.066534
240 27.18 2.00 Yes Sat Dinner 2 0.073584
欢迎交流!!!