場景
需要按照各個省份及全網進行分組,對各個指標的率值進行排序。
其實這一步一般是在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]