點擊上方「濤哥聊Python」,選擇「星標」公眾號
重磅乾貨,第一時間送達
本文翻譯自:https://nbviewer.jupyter.org/github/justmarkham/pandas-videos/blob/master/top_25_pandas_tricks.ipynb ,翻譯如有不當之處,還請批評指正。
首先我們需要先提前下載好示例數據集:
drinksbycountry.csv : http://bit.ly/drinksbycountry
imdbratings.csv : http://bit.ly/imdbratings
chiporders.csv : http://bit.ly/chiporders
smallstockers.csv : http://bit.ly/smallstocks
kaggletrain.csv : http://bit.ly/kaggletrain
uforeports.csv : http://bit.ly/uforeports
利用以下代碼導入上述數據集:
導入示例數據集1. 顯示已安裝的版本
輸入下面的命令查詢pandas版本:
查詢pandas版本
如果你還想知道pandas所依賴的模塊的版本,你可以使用show_versions()函數:
pandas依賴模塊的版本號
你可以查看到Python,pandas, Numpy, matplotlib等的版本信息。2. 創建示例DataFrame
假設你需要創建一個示例DataFrame。有很多種實現的途徑,我最喜歡的方式是傳一個字典給DataFrame constructor,其中字典中的keys為列名,values為列的取值。
利用字典創建DataFrame
現在如果你需要創建一個更大的DataFrame,上述方法則需要太多的輸入。在這種情況下,你可以使用Numpy的random.rand()函數,告訴它行數和列數,將它傳遞給DataFrame constructor:
使用random.rand()創建DataFrame
這種方式很好,但如果你還想把列名變為非數值型的,你可以強制地將一串字符賦值給columns參數:
將列名改為非數值型
你可以想到,你傳遞的字符串的長度必須與列數相同。3. 更改列名
讓我們來看一下剛才我們創建的示例DataFrame:
查看創建的示例DataFrame
我更喜歡在選取pandas列的時候使用點(.),但是這對那麼列名中含有空格的列不會生效。讓我們來修復這個問題。
更改列名最靈活的方式是使用rename()函數。你可以傳遞一個字典,其中keys為原列名,values為新列名,還可以指定axis:
使用這個函數最好的方式是你需要更改任意數量的列名,不管是一列或者全部的列。
如果你需要一次性重新命令所有的列名,更簡單的方式就是重寫DataFrame的columns屬性:
如果你需要做的僅僅是將空格換成下劃線,那麼更好的辦法是使用str.replace()方法,這是因為你都不需要輸入所有的列名:
上述三個函數的結果都一樣,可以更改列名使得列名中不含有空格:
最後,如果你需要在列名中添加前綴或者後綴,你可以使用add_prefix()函數:
列名添加前綴
或者使用add_suffix()函數:
列名添加後綴4. 行序反轉
讓我們來看一下drinks這個DataFame:
查看drinks前幾行
該數據集描述了每個國家的平均酒消費量。如果你想要將行序反轉呢?
最直接的辦法是使用loc函數並傳遞::-1,跟Python中列表反轉時使用的切片符號一致:
行序反轉(使用loc函數)
如果你還想重置索引使得它從0開始呢?
你可以使用reset_index()函數,告訴他去掉完全拋棄之前的索引:
行序反轉並重置索引
你可以看到,行序已經反轉,索引也被重置為默認的整數序號。5. 列序反轉
跟之前的技巧一樣,你也可以使用loc函數將列從左至右反轉:
列序反轉
逗號之前的冒號表示選擇所有行,逗號之後的::-1表示反轉所有的列,這就是為什麼country這一列現在在最右邊。6. 通過數據類型選擇列
這裡有drinks這個DataFrame的數據類型:
查看數據類型
假設你僅僅需要選取數值型的列,那麼你可以使用select_dtypes()函數:
選取數值型列
這包含了int和float型的列。
你也可以使用這個函數來選取數據類型為object的列:
選取object列
你還可以選取多種數據類型,只需要傳遞一個列表即可:
選取多種數據類型的列
你還可以用來排除特定的數據類型:
排除特定的數據類型7. 將字符型轉換為數值型
讓我們來創建另一個示例DataFrame:
新的示例DataFrame
這些數字實際上儲存為字符型,導致其數據類型為object:
為了對這些列進行數學運算,我們需要將數據類型轉換成數值型。你可以對前兩列使用astype()函數:
使用astype()函數將數據類型轉換成數值型
但是,如果你對第三列也使用這個函數,將會引起錯誤,這是因為這一列包含了破折號(用來表示0)但是pandas並不知道如何處理它。
你可以對第三列使用to_numeric()函數,告訴其將任何無效數據轉換為NaN:
對第三列使用to_numeric()函數
如果你知道NaN值代表0,那麼你可以fillna()函數將他們替換成0:
使用fillna函數將NaN替換成0
最後,你可以通過apply()函數一次性對整個DataFrame使用這個函數:
將整個DataFrame轉化成數值型
僅需一行代碼就完成了我們的目標,因為現在所有的數據類型都轉換成float:
8. 減小DataFrame空間大小
pandas DataFrame被設計成可以適應內存,所以有些時候你可以減小DataFrame的空間大小,讓它在你的系統上更好地運行起來。
這是drinks這個DataFrame所佔用的空間大小:
查看drinks的內存使用情況
可以看到它使用了304.KB。
如果你對你的DataFrame有操作方面的問題,或者你不能將它讀進內存,那麼在讀取文件的過程中有兩個步驟可以使用來減小DataFrame的空間大小。
第一個步驟是只讀取那些你實際上需要用到的列,可以調用usecols參數:
只讀取需要用到的列可以減小佔用內存
通過僅讀取用到的兩列,我們將DataFrame的空間大小縮小至13.6KB。
第二步是將所有實際上為類別變量的object列轉換成類別變量,可以調用dtypes參數:
轉換必要的類別變量的列
通過將continent列讀取為category數據類型,我們進一步地把DataFrame的空間大小縮小至2.3KB。
值得注意的是,如果跟行數相比,category數據類型的列數相對較小,那麼catefory數據類型可以減小內存佔用。9. 按行從多個文件中構建DataFrame
假設你的數據集分化為多個文件,但是你需要將這些數據集讀到一個DataFrame中。
舉例來說,我有一些關於股票的小數聚集,每個數據集為單天的CSV文件。這是第一天的:
第一天的股票數據
這是第二天的:
第二天的股票數據
這是第三天的:
第三天的股票數據
你可以將每個CSV文件讀取成DataFrame,將它們結合起來,然後再刪除原來的DataFrame,但是這樣會多佔用內存且需要許多代碼。
更好的方式為使用內置的glob模塊。你可以給glob()函數傳遞某種模式,包括未知字符,這樣它會返回符合該模式的文件列表。在這種方式下,glob會查找所有以stocks開頭的CSV文件:
使用glob模塊查找所有以stocks開頭的CSV文件
glob會返回任意排序的文件名,這就是我們為什麼要用Python內置的sorted()函數來對列表進行排序。
我們以生成器表達式用read_csv()函數來讀取每個文件,並將結果傳遞給concat()函數,這會將單個的DataFrame按行來組合:
按行組合單個的DataFrame
不幸的是,索引值存在重複。為了避免這種情況,我們需要告訴concat()函數來忽略索引,使用默認的整數索引:
解決索引存在重複的問題10. 按列從多個文件中構建DataFrame
上一個技巧對於數據集中每個文件包含行記錄很有用。但是如果數據集中的每個文件包含的列信息呢?
這裡有一個例子,dinks數據集被劃分成兩個CSV文件,每個文件包含三列:
同上一個技巧一樣,我們以使用glob()函數開始。這一次,我們需要告訴concat()函數按列來組合:
按列組合
現在我們的DataFrame已經有六列了。11. 從剪貼板中創建DataFrame
假設你將一些數據儲存在Excel或者Google Sheet中,你又想要儘快地將他們讀取至DataFrame中。
你需要選擇這些數據並複製至剪貼板。然後,你可以使用read_clipboard()函數將他們讀取至DataFrame中:
從剪貼板中創建DataFrame
和read_csv()類似,read_clipboard()會自動檢測每一列的正確的數據類型:
讓我們再複製另外一個數據至剪貼板:
神奇的是,pandas已經將第一列作為索引了:
需要注意的是,如果你想要你的工作在未來可複製,那麼read_clipboard()並不值得推薦。12. 將DataFrame劃分為兩個隨機的子集
假設你想要將一個DataFrame劃分為兩部分,隨機地將75%的行給一個DataFrame,剩下的25%的行給另一個DataFrame。
舉例來說,我們的movie ratings這個DataFrame有979行:
我們可以使用sample()函數來隨機選取75%的行,並將它們賦值給"movies_1"DataFrame:
接著我們使用drop()函數來捨棄「moive_1」中出現過的行,將剩下的行賦值給"movies_2"DataFrame:
你可以發現總的行數是正確的:
你還可以檢查每部電影的索引,或者"moives_1":
或者"moives_2":
需要注意的是,這個方法在索引值不唯一的情況下不起作用。
讀者註:該方法在機器學習或者深度學習中很有用,因為在模型訓練前,我們往往需要將全部數據集按某個比例劃分成訓練集和測試集。該方法既簡單又高效,值得學習和嘗試。13. 通過多種類型對DataFrame進行過濾
讓我們先看一眼movies這個DataFrame:
其中有一列是genre(類型):
比如我們想要對該DataFrame進行過濾,我們只想顯示genre為Action或者Drama或者Western的電影,我們可以使用多個條件,以"or"符號分隔:
DataFrame過濾
但是,你實際上可以使用isin()函數將代碼寫得更加清晰,將genres列表傳遞給該函數:
isin()函數進行過濾
如果你想要進行相反的過濾,也就是你把剛才的三種類型的電影排除掉,那麼你可以在過濾條件前加上破浪號:
這種方法能夠起作用是因為在Python中,波浪號表示「not」操作。14. 從DataFrame中篩選出數量最多的類別
假設你想要對movies這個DataFrame通過genre進行過濾,但是只需要前3個數量最多的genre。
我們對genre使用value_counts()函數,並將它保存成counts(type為Series):
對genre列進行計數
&emp; 該Series的nlargest()函數能夠輕鬆地計算出Series中前3個最大值:
事實上我們在該Series中需要的是索引:
最後,我們將該索引傳遞給isin()函數,該函數會把它當成genre列表:
這樣,在DataFrame中只剩下Drame, Comdey, Action這三種類型的電影了。15. 處理缺失值
讓我們來看一看UFO sightings這個DataFrame:
你將會注意到有些值是缺失的。
為了找出每一列中有多少值是缺失的,你可以使用isna()函數,然後再使用sum():
每一列缺失值數量
isna()會產生一個由True和False組成的DataFrame,sum()會將所有的True值轉換為1,False轉換為0並把它們加起來。
類似地,你可以通過mean()和isna()函數找出每一列中缺失值的百分比。
每一列缺失值佔比
如果你想要捨棄那些包含了缺失值的列,你可以使用dropna()函數:
捨棄包含了缺失值的列
或者你想要捨棄那麼缺失值佔比超過10%的列,你可以給dropna()設置一個閾值:
捨棄缺失值佔比超過10%的列
len(ufo)返回總行數,我們將它乘以0.9,以告訴pandas保留那些至少90%的值不是缺失值的列。16. 將一個字符串劃分成多個列
我們先創建另一個新的示例DataFrame:
如果我們需要將「name」這一列劃分為三個獨立的列,用來表示first, middle, last name呢?我們將會使用str.split()函數,告訴它以空格進行分隔,並將結果擴展成一個DataFrame:
這三列實際上可以通過一行代碼保存至原來的DataFrame:
如果我們想要劃分一個字符串,但是僅保留其中一個結果列呢?比如說,讓我們以", "來劃分location這一列:
如果我們只想保留第0列作為city name,我們僅需要選擇那一列並保存至DataFrame:
17. 將一個由列表組成的Series擴展成DataFrame
讓我們創建一個新的示例DataFrame:
這裡有兩列,第二列包含了Python中的由整數元素組成的列表。
如果我們想要將第二列擴展成DataFrame,我們可以對那一列使用apply()函數並傳遞給Series constructor:
通過使用concat()函數,我們可以將原來的DataFrame和新的DataFrame組合起來:
18. 對多個函數進行聚合
讓我們來看一眼從Chipotle restaurant chain得到的orders這個DataFrame:
每個訂單(order)都有訂單號(order_id),包含一行或者多行。為了找出每個訂單的總價格,你可以將那個訂單號的價格(item_price)加起來。比如,這裡是訂單號為1的總價格:
如果你想要計算每個訂單的總價格,你可以對order_id使用groupby(),再對每個group的item_price進行求和。
但是,事實上你不可能在聚合時僅使用一個函數,比如sum()。為了對多個函數進行聚合,你可以使用agg()函數,傳給它一個函數列表,比如sum()和count():
這將告訴我們沒定訂單的總價格和數量。19. 將聚合結果與DataFrame進行組合
讓我們再看一眼orders這個DataFrame:
如果我們想要增加新的一列,用於展示每個訂單的總價格呢?回憶一下,我們通過使用sum()函數得到了總價格:
sum()是一個聚合函數,這表明它返回輸入數據的精簡版本(reduced version )。
換句話說,sum()函數的輸出:
比這個函數的輸入要小:
解決的辦法是使用transform()函數,它會執行相同的操作但是返回與輸入數據相同的形狀:
我們將這個結果存儲至DataFrame中新的一列:
你可以看到,每個訂單的總價格在每一行中顯示出來了。
這樣我們就能方便地甲酸每個訂單的價格佔該訂單的總價格的百分比:
20. 選取行和列的切片
讓我們看一眼另一個數據集:
這就是著名的Titanic數據集,它保存了Titanic上乘客的信息以及他們是否存活。
如果你想要對這個數據集做一個數值方面的總結,你可以使用describe()函數:
但是,這個DataFrame結果可能比你想要的信息顯示得更多。
如果你想對這個結果進行過濾,只想顯示「五數概括法」(five-number summary)的信息,你可以使用loc函數並傳遞"min"到"max"的切片:
五數概括法的結果
如果你不是對所有列都感興趣,你也可以傳遞列名的切片:
21. 對MultiIndexed Series進行重塑
Titanic數據集的Survived列由1和0組成,因此你可以對這一列計算總的存活率:
如果你想對某個類別,比如「Sex」,計算存活率,你可以使用groupby():
如果你想一次性對兩個類別變量計算存活率,你可以對這些類別變量使用groupby():
該結果展示了由Sex和Passenger Class聯合起來的存活率。它存儲為一個MultiIndexed Series,也就是說它對實際數據有多個索引層級。
這使得該數據難以讀取和交互,因此更為方便的是通過unstack()函數將MultiIndexed Series重塑成一個DataFrame:
該DataFrame包含了與MultiIndexed Series一樣的數據,不同的是,現在你可以用熟悉的DataFrame的函數對它進行操作。22. 創建數據透視表(pivot table)
如果你經常使用上述的方法創建DataFrames,你也許會發現用pivot_table()函數更為便捷:
想要使用數據透視表,你需要指定索引(index), 列名(columns), 值(values)和聚合函數(aggregation function)。
數據透視表的另一個好處是,你可以通過設置margins=True輕鬆地將行和列都加起來:
這個結果既顯示了總的存活率,也顯示了Sex和Passenger Class的存活率。
最後,你可以創建交叉表(cross-tabulation),只需要將聚合函數由"mean"改為"count":
這個結果展示了每一對類別變量組合後的記錄總數。23. 將連續數據轉變成類別數據
讓我們來看一下Titanic數據集中的Age那一列:
它現在是連續性數據,但是如果我們想要將它轉變成類別數據呢?
一個解決辦法是對年齡範圍打標籤,比如"adult", "young adult", "child"。實現該功能的最好方式是使用cut()函數:
這會對每個值打上標籤。0到18歲的打上標籤"child",18-25歲的打上標籤"young adult",25到99歲的打上標籤「adult」。
注意到,該數據類型為類別變量,該類別變量自動排好序了(有序的類別變量)。24. 更改顯示選項
讓我們再來看一眼Titanic 數據集:
注意到,Age列保留到小數點後1位,Fare列保留到小數點後4位。如果你想要標準化,將顯示結果保留到小數點後2位呢?
你可以使用set_option()函數:
set_option()函數中第一個參數為選項的名稱,第二個參數為Python格式化字符。可以看到,Age列和Fare列現在已經保留小數點後兩位。注意,這並沒有修改基礎的數據類型,而只是修改了數據的顯示結果。
你也可以重置任何一個選項為其默認值:
對於其它的選項也是類似的使用方法。25. Style a DataFrame
上一個技巧在你想要修改整個jupyter notebook中的顯示會很有用。但是,一個更靈活和有用的方法是定義特定DataFrame中的格式化(style)。
讓我們回到stocks這個DataFrame:
我們可以創建一個格式化字符串的字典,用於對每一列進行格式化。然後將其傳遞給DataFrame的style.format()函數:
注意到,Date列是month-day-year的格式,Close列包含一個$符號,Volume列包含逗號。
我們可以通過鏈式調用函數來應用更多的格式化:
我們現在隱藏了索引,將Close列中的最小值高亮成紅色,將Close列中的最大值高亮成淺綠色。
這裡有另一個DataFrame格式化的例子:
Volume列現在有一個漸變的背景色,你可以輕鬆地識別出大的和小的數值。
最後一個例子:
現在,Volumn列上有一個條形圖,DataFrame上有一個標題。
請注意,還有許多其他的選項你可以用來格式化DataFrame。額外技巧:Profile a DataFrame
假設你拿到一個新的數據集,你不想要花費太多力氣,只是想快速地探索下。那麼你可以使用pandas-profiling這個模塊。
在你的系統上安裝好該模塊,然後使用ProfileReport()函數,傳遞的參數為任何一個DataFrame。它會返回一個互動的HTML報告:
使用示例如下(只顯示第一部分的報告):
這部分的代碼已經放在Github上,網址為:https://github.com/percent4/panas_usage_25_tricks 。
面試時,最忌諱這樣自我介紹
情人節來了,教你個用 Python 表白的技巧
終於!疫情之下,第一批企業沒能熬住面臨倒閉,員工被遣散,沒能等來春暖花開!