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