pandas 之分组利器groupby使用注意
部分参考
https://blog.csdn.net/weixin_39750084/article/details/81008259
Pandas中groupby+agg+merge及describe实现各类分组统计及一些实用技巧
和
https://blog.csdn.net/m0_37870649/article/details/80979809
Pandas分组运算(groupby)修炼
以及
https://blog.csdn.net/flyfoxs/article/details/81346885
在Pandas中如何给多层索引降级
数据挖掘中分组做统计是家常便饭,
如果是分组字段是一个,被分组统计字段是多个,可按这个模板来:
get_count = lambda x:x.count(if x>10)
data=data[col1].groupby(data[col2]).agg({'maxValue':'max','minValue':'min','filterCount':get_count}).reset_index()
add_suffix(suffix),add_prefix(prefix)添加前缀和后缀
根据代码看目的很容易,
即根据col2进行分组后,col1的组内最大值、最小值以及组内大于10的值的个数,加上col2这一列,共同构成一个新的DataFrame对象。
Pandas中groupby+agg+merge及describe实现各类分组统计及一些实用技巧
中讲到了为什么这么写最好,大致是三点:
- 为什么要用agg:
如果你不用agg,也可以实现同样的功能,因为Pandas自带了max、min、mean这些聚合函数,用法如下:
data = data[col1].groupby(data[col2]).max()
data = data[col1].groupby(data[col2]).mean()
data = data[col1].groupby(data[col2]).min()
但是这样既笨重,又没办法通过自定义的聚合函数来实现自定义统计,而用agg可以传入自定义的聚合函数。
- 为什么agg里面要放字典
agg里面可以不传字典,但是新生成的列是不能直接生成你想要的列名的。如果你想要定义列名,那你得这么做:
#先初始化列为1,再给这一列传入统计后的值
data['max'] = 1
data['max'] = data[col1].groupby(data[col2]).agg('max').reset_index()
print(data)
add_prefix()
用这种方法,如果同时实现最大值、最小值、均值、计数等参数统计的话,需要写八行代码,而在agg中传入字典参数,同时定义列名和所用的聚合函数,只需要一列就能完成了,甚好。
- 为什么要写reset_index
这是因为 比如groupby 用户字段,那么 用户字段会默认认定为是index,这并不符合预期,因为往往用户字段应该还原为普通列字段,方便后面和其他含用户字段的表的join或merge
如果是分组字段是多个,被分组统计字段是一个(或多个)时,用上面的模板好像有点问题:
要用到unstack()
比如
import pandas as pd
import numpy as np
df=pd.DataFrame({'uid':[1001,1001,1002,1003,1003,1003],'tran_type':[0,1,0,1,0,1],'amount':[1,2,3,4,np.NaN,5]})
uid tran_type amount
0 1001 0 1.0
1 1001 1 2.0
2 1002 0 3.0
3 1003 1 4.0
4 1003 0 NaN
5 1003 1 5.0
df.groupby(['uid','tran_type'])['amount'].mean()
uid tran_type
1001 0 1.0
1 2.0
1002 0 3.0
1003 0 NaN
1 4.5
Name: amount, dtype: float64
df.groupby(['uid','tran_type'])['amount'].mean().unstack()
tran_type 0 1
uid
1001 1.0 2.0
1002 3.0 NaN
1003 NaN 4.5
df=df.groupby(['uid','tran_type'])['amount'].mean().unstack()
df.columns=['tran_type_0','tran_type_1']
df.reset_index()
uid tran_type_0 tran_type_1
0 1001 1.0 2.0
1 1002 3.0 NaN
2 1003 NaN 4.5
这样的数据差不多是我们想要的了
但是总感觉有点麻烦,
能不能用agg的方法,试了一下
df=pd.DataFrame({'uid':[1001,1001,1002,1003,1003,1003],'tran_type':[0,1,0,1,0,1],'amount':[1,2,3,4,np.NaN,5]})
uid tran_type amount
0 1001 0 1.0
1 1001 1 2.0
2 1002 0 3.0
3 1003 1 4.0
4 1003 0 NaN
5 1003 1 5.0
df.groupby(['uid','tran_type'])['amount'].agg({'mean'}).unstack()
mean
tran_type 0 1
uid
1001 1.0 2.0
1002 3.0 NaN
1003 NaN 4.5
牵扯到了多级索引了
如何转化为大致
uid tran_type_0 tran_type_1
0 1001 1.0 2.0
1 1002 3.0 NaN
2 1003 NaN 4.5
的样子呢
https://blog.csdn.net/flyfoxs/article/details/81346885
在Pandas中如何给多层索引降级 给出了骚操作的写法:
df=df[['uid','amount']].groupby(['uid']).agg({'mean','min'})
amount
mean min
uid
1001 1.5 1.0
1002 3.0 3.0
1003 4.5 4.0
可读性还好,但是这没办法和后面的含有uid的表join或merge吧
因为是多级索引,要是能降级就好了,
df.columns
MultiIndex(levels=[['amount'], ['mean', 'min']],
labels=[[0, 0], [0, 1]])
一种想法是直接去掉一级就好了
#直接去掉一层索引,df.columns = df.columns.droplevel(0),这是鸵鸟策略吧
#df.columns=['uid','amount_mean','amount_min']
#第二种想法手动去写列名,是不是显得不够专业啊,下面大神给出的# 把2层合并到一层的 骚写法,
df.columns = ["_".join(x) for x in df.columns.ravel()]
df.reset_index()
uid amount_mean amount_min
0 1001 1.5 1.0
1 1002 3.0 3.0
2 1003 4.5 4.0
这样就是我们想要的数据了
再次回到我们开始提出的问题,能不能用agg的方法对groupby多个字段,
同样用到上面的对多级索引降级的方式就可以很好解决 问题了
df=df.groupby(['uid','tran_type'])['amount'].agg({'mean'}).add_prefix('amount_').unstack()
amount_mean
tran_type 0 1
uid
1001 1.0 2.0
1002 3.0 NaN
1003 NaN 4.5
使用层次索引合并降级
df.columns = [x[0]+"_"+str(x[1]) for x in df.columns.ravel()]
#这里因为0是数值型,直接和字符串amount_mean不能用"_".join(x) 方式连接
df.columns
df.reset_index()
uid amount_mean_0 amount_mean_1
0 1001 1.0 2.0
1 1002 3.0 NaN
2 1003 NaN 4.5
这正是我们想要的
和pandas.groupby常一起用的还有transform函数和describe函数,cut函数,
以及计数的size()函数(计np.NaN)和count()函数(不计np.NaN)
describe函数比较简单易懂,常见统计量的summary
transform函数则在于,在原df的索引index不变下方便的在原df中增加groupby之后的聚合结果
前面进行聚合运算的时候,得到的结果是一个以分组名为 index 的结果对象。如果我们想使用原数组的 index 的话,就需要进行 merge 转换。transform(func, args, *kwargs) 方法简化了这个过程,它会把 func 参数应用到所有分组,然后把结果放置到原数组的 index 上(如果结果是一个标量,就进行广播):即transform隐式地帮我们完成了一次merge操作,
df['count_B']=df.groupby(['group1', 'group2'])['B'].transform('count')
上面运算的结果分析: {‘group1’:’A’, ‘group2’:’C’}的组合共出现3次,即index为0,1,2。对应”B”列的值分别是”one”,”NaN”,”NaN”,由于count()计数时不包括Nan值,因此{‘group1’:’A’, ‘group2’:’C’}的count计数值为1。
transform()方法会将该计数值在dataframe中所有涉及的rows都显示出来(我理解应该就进行广播)
对连续数值变化的列做分组,会用到cut函数,cut函数中需自定义bins的范围
聚合方法size()和count()
size跟count的区别: size计数时包含NaN值,而count不包含NaN值
浅红色文字:浅红色文字:
深红色文字:深红色文字
浅绿色文字:浅绿色文字
深绿色文字:深绿色文字
浅蓝色文字:浅蓝色文字
深蓝色文字:深蓝色文字
浅黄色文字:浅黄色文字
深黄色文字:深黄色文字
浅青色文字:浅青色文字
深青色文字:深青色文字
浅紫色文字:浅紫色文字
深紫色文字:深紫色文字