场景
需要按照各个省份及全网进行分组,对各个指标的率值进行排序。
其实这一步一般是在sql中完成的,但由于
- 要分组排序的列较多,达30列以上,代码十分不简洁,python一两行即可搞定
- 连接的数据库为mysql,本身没有
row_number() over( paritition by··· ···)
的语句,实现起来较为麻烦。 - 本次的原始数据每日只有5000行左右并不大
故直接用python将数据down下来进行处理。
实现
按照常理,直接groupby()分组再rank()排序即可
In[12]: result1.groupby(by='省份')[['投诉完结率', '二次投诉率']].rank(ascending=False, method = 'min')
Out[12]:
投诉完结率 二次投诉率
0 1.0 4.0
1 1.0 5.0
2 1.0 1.0
3 1.0 2.0
4 1.0 3.0
.. ... ...
162 1.0 1.0
163 1.0 1.0
164 1.0 1.0
165 1.0 1.0
166 1.0 1.0
[167 rows x 2 columns]
但实际上得到的结果与sum() mean() max()等聚合运算得到的结果并不相同,这些聚合运算返回的Series或DataFrame的index为“by=”的列。每一组仅返回一行数据。
In[20]: result1.groupby(by='省份').max()[['投诉完结率', '二次投诉率']].head()
Out[20]:
投诉完结率 二次投诉率
省份
上海 1.0 0.0121
云南 1.0 0.0244
全网 1.0 0.0120
内蒙古 1.0 0.0323
北京 1.0 0.0084
但rank()方法在原本数据的基础上每一行都返回一个排名,最后得到的数据index仍为原本的index,其他列是不显示的。
为了在结果中显示其他列,有两种方法:
- 如果需要将上述结果join回原result1,直接按index进行join/merge/concat即可, 但不可使用
result1[['rank1','rank2']]=result1.groupby(by='省份')[['投诉完结率', '二次投诉率']].rank(ascending=False, method = 'min')
这种方式直接增加一列,行的顺序并不能自动对齐 ; - 如仅需一列或几列+计算得到的排名,而不需要原数据中其他列,则对result1在分组之前,将需要的列set_index更为简便
In [14]: result1.set_index(['省份','公司名称']).groupby(by='002省份')[['投诉完结率', '二次投诉率']]\
.rank(ascending=False, method = 'min')
Out[14]:
投诉完结率 二次投诉率
省份 公司名称
全网 s 1.0 4.0
b 1.0 5.0
yd 1.0 1.0
y 1.0 2.0
z 1.0 3.0
... ... ...
香港特别行政区 b 1.0 1.0
s 1.0 1.0
z 1.0 1.0
台湾 z 1.0 1.0
yd 1.0 1.0
[167 rows x 2 columns]