Pandas 還能這么玩兒?這樣排序才叫真功夫!

標簽:排序
時間:2019 年 12 月 31 日,晚 9 點
地點:XXX 公司 數(shù)據(jù)分析部 辦公室
龍少終于完成了年終分析報表,把郵件發(fā)給了嚴總,正收拾東西打卡回家,突然,叮鈴鈴……,手機響了,“怎么是嚴總的電話!”,龍少心里一緊,但也不能不接,別看嚴總歲數(shù)比龍少小,但平時的嚴厲可是名副其實。
剛一接通,電話那頭就傳來嚴總一通霹靂巴拉的責問,“你這表怎么搞的,知不知道北京、上海、廣州是咱們最重要的分公司,要把這幾家擺在前面,不能按拼音順序顯示,這樣讓人怎么看!細節(jié)決定成??!懂不懂!??!趕緊改?。。 ?說完,嚴總就掛了電話,龍少本已穿上羽絨服開始冒汗的身體,猶如被屋外的寒風一樣吹了個透心涼。
龍少心想,這 Pandas 提供的 sort_index() 也好,sort_values() 也罷,都不支持自定義排序啊,天啊,嚴總,你沒女朋友,可以全情投入工作,我還想趕緊回家陪老婆娃娃過新年呢。咋整,求助呆鳥吧。
呆鳥云:“為了幫龍少實現(xiàn)趕緊回家老婆孩子熱炕頭的愿望,呆鳥總結(jié)了 Pandas 幾種自定義排序方法:
自定義索引排序
自定義列值排序
利用輔助列實現(xiàn)自定義列值排序
自定義列序
掌握了這幾種方式,就能更靈活的排序,這才叫排序真功夫?!?/p>
0. 準備工作
導入 Pandas:
import pandas as pd
導入數(shù)據(jù),銷售數(shù)據(jù)放在 data/自定義排序.xlsx 的 銷售數(shù)據(jù) 表里,在導入時,把序號列設(shè)為索引。
sales = pd.read_excel("data/自定義排序.xlsx","銷售數(shù)據(jù)",index_col="序號")

1. 自定義索引排序
要是數(shù)據(jù)量不多,可以嘗試一下自定義索引排序。
只需提供索引列表,用 reindex() 就能重設(shè)數(shù)據(jù)排序。
reorder = [4,8,9,1,5,7,2,6,3]
sales.reindex(reorder)

看到了嗎,上圖就是我們想排列數(shù)據(jù)的順序,分公司按北京、上海、廣州、深圳排序,門店按一店、二店、三店排序。
雖然只有 9 條數(shù)據(jù),但這種一個一個寫索引,呆鳥已經(jīng)看的頭暈眼花了,這要再多點,更受不了了,顯然,數(shù)據(jù)量稍微多一些,這種方式就不適合了。
2. 自定義列值排序
下面,我們學習一種更簡單,更直觀的方式。
這里的原理是把列轉(zhuǎn)換為類別型,通過指定類別排序,按列值排序。
sales['分公司'] = pd.Categorical(sales['分公司'], categories=[
'北京', '上海', '廣州', '深圳'], ordered=True)
sales.sort_values(by='分公司')

這樣很方便地就實現(xiàn)了按分公司排列,但并未實現(xiàn)門店列的排序,比如,廣州與深圳的二店排在了一店前面。
雖然,也可以把門店列改為類別型,但門店很多就不直觀了,因此,我們再引入一種方式。
同時,也請注意,類似這種分公司與門店名稱的排序,要在設(shè)計數(shù)據(jù)庫時提供分公司與門店代碼,在數(shù)據(jù)導出時也要導出相應代碼,代碼依據(jù)規(guī)律創(chuàng)建,在排序時就可以用上。
如果代碼規(guī)律與排序規(guī)律還是不一致,就只好自行維護一套自定義規(guī)律表了。
3. 利用輔助列實現(xiàn)自定義列值排序
首先,要導入自定義的分公司與門店代碼表。
這兩個表分別放在了自定義排序的 Excel 文件里,如果從數(shù)據(jù)庫導出的文件較大,推薦單獨放在一個 Excel 文件里,避免打開大文件效率低。
branch = pd.read_excel('data/自定義排序.xlsx','分公司代碼')
department = pd.read_excel('data/自定義排序.xlsx','門店代碼')
然后,把為分公司與門店匹配對應的代碼,這個操作,類似 Excel 的 vlookup()。
sales = pd.merge(sales, branch, on="分公司", how="left")
sales = pd.merge(sales, department, on="門店",how="left")
這里簡要說明一下 merge(),
sales 是 left ~ 左,因為 sales 放在左邊;
branch 或 department 是 right ~ 右,因為這兩個 df 放在了右邊,瞧,就是這么簡單;
on="分公司",以兩個 df 的
分公司列為依據(jù),進行合并;how="left",如何 merge,以左邊的為主,還可以寫
right、outer、inner,分別表示按右邊的 df、兩個 df 的并集、兩個 df 的交集;
merge() 還有其它參數(shù),這里先不多說,有興趣的朋友可以去看下 Pandas 官檔里關(guān)于 merge() 的說明。
現(xiàn)在,用 sort_values(),就可以實現(xiàn)自定義排序了。
sales.sort_values(['分公司代碼','門店代碼'],inplace=True)

看到了嗎?行序已經(jīng)按要求排好了。
4. 自定義列序
至此,列序還有些亂,嚴總看了,肯定又會批評不注意細節(jié)了,還要按以下順序重排列序:分公司代碼、分公司、門店代碼、門店、銷售額。
Pandas 的 sort_index() 可以實現(xiàn)按列名排序,但對中文不友好不說,還不支持自定義排序:
sales.sort_index(axis='columns',ascending=True)

從上面的列子可以看到,sort_index() 只支持按字母或數(shù)字排序,不支持自定義列排序,咱們還得想辦法。
其實很簡單!
sales = sales[['分公司代碼', '分公司', '門店代碼', '門店', '銷售額']]

至此,列序也排好了,但這就完了嗎?
如果這樣給嚴總,肯定還會挨批!
為什么呢?
大家看下 index, 也就是索引序號,是不是亂序的?另外,對嚴總來說,他并不關(guān)心這些代碼,這些只是冗余信息,影響他看數(shù)據(jù)的效率與效果,也應該去掉。
# 刪掉沒用的列
del sales['分公司代碼'], sales['門店代碼']
# 重置索引
sales.reset_index(drop=True,inplace=True)
這里說明一下 reset_index():
drop=True,代表不保留原來的索引,如果是False,就會在生成的新 df 里保留原來的索引。inplace=True,代表直接在這個 df 里重置索引,無需生成新的 df。

到此為止,這個簡單的小表,已經(jīng)完工了。
龍少又學了幾招,終于在 10 點前把新的年度銷售表交給了嚴總,嚴總也沒再提出什么異議。龍少對呆鳥說:“這方法真是不錯,省了我大事啦,改天我請你吃飯!”,呆鳥云:“吃飯就免啦,多幫我把文章轉(zhuǎn)轉(zhuǎn)朋友圈,點點在看就好啦。另外,我接的廣告,也看一下,我的文章可都是費心寫的,也不收費,就靠廣告金主大佬施舍點午飯錢,要不我就白忙活啦。”
龍少說:“轉(zhuǎn)朋友圈,點在看,看廣告,都是舉手之勞,小事一樁!”
就在此時,嚴總的聲音也在空中回蕩:“轉(zhuǎn)朋友圈,點在看,看廣告,別讓呆鳥當白勞!”
龍少一看嚴總回公司了,趕緊來了句“嚴總,2020,新年快樂”,就速速溜了。
最后,再總結(jié)下:
數(shù)據(jù)量少,可以直接寫自定義列表,用
reindex()重排行序;單列,且列中唯一值少,適合改為類別型,用類別值排序;
多列,或列中唯一值較多,可以做輔助列自定義排序;
列排序,按列名寫個自定義列表就可以了。
希望大家有所收獲,感謝龍少、嚴總友情出演。

相關(guān)閱讀:
