編程派微信號:codingpy
本文由 Python 翻譯組 最新翻譯出品,原作者為 Jamal Moir,譯者為 liubj2016,並由編程派作者 EarlGrey 校對。
譯者簡介:liubj2016,中南財經政法大學,金融工程系學生。Python使用方向:數據分析,機器學習和量化投資。
P.S. 彌補一下昨天的錯誤,大家回復「pybook04」即可獲取PDF版分享連結。唉,本來昨天加上這個就不那麼標題黨了。。。回復完了別忘了接著看這篇教程哦
Pandas 是我最喜愛的庫之一。通過帶有標籤的列和索引,Pandas 使我們可以以一種所有人都能理解的方式來處理數據。它可以讓我們毫不費力地從諸如 csv 類型的文件中導入數據。我們可以用它快速地對數據進行複雜的轉換和過濾等操作。Pandas 真是超級棒。
我覺得它和 Numpy、Matplotlib 一起構成了一個 Python 數據探索和分析的強大基礎。Scipy 當然也是一大主力並且是一個絕對贊的庫,但是我覺得前三者才是 Python 科學計算真正的頂梁柱。
那麼,趕緊看看 python 科學計算系列的第三篇推文,一窺 Pandas 的芳容吧。如果你還沒看其它幾篇文章的話,別忘了去看看。
Matplotlib 快速入門
使用Python進行科學計算:NumPy入門
第一件事當然是請出我們的明星 —— Pandas。
import pandas as pd
這是導入 pandas 的標準方法。我們不想一直寫 pandas 的全名,但是保證代碼的簡潔和避免命名衝突都很重要,所以折中使用 pd 。如果你去看別人使用 pandas 的代碼,就會看到這種導入方式。
Pandas 中的數據類型Pandas 基於兩種數據類型,series 和 dataframe。
series 是一種一維的數據類型,其中的每個元素都有各自的標籤。如果你之前看過這個系列關於 Numpy 的推文,你可以把它當作一個由帶標籤的元素組成的 numpy 數組。標籤可以是數字或者字符。
dataframe 是一個二維的、表格型的數據結構。Pandas 的 dataframe 可以儲存許多不同類型的數據,並且每個軸都有標籤。你可以把它當作一個 series 的字典。
將數據導入 Pandas在對數據進行修改、探索和分析之前,我們得先導入數據。多虧了 Pandas ,這比在 Numpy 中還要容易。
這裡我鼓勵你去找到自己感興趣的數據並用來練習。你的(或者別的)國家的網站就是不錯的數據源。如果要舉例的話,首推英國政府數據和美國政府數據。Kaggle也是個很好的數據源。
我將使用英國降雨數據,這個數據集可以很容易地從英國政府網站上下載到。此外,我還下載了一些日本降雨量的數據。
EarlGrey:英國降雨數據:下載地址
日本的數據實在是沒找到,抱歉。
df = pd.read_csv('uk_rain_2014.csv', header=0)
譯者註:如果你的數據集中有中文的話,最好在裡面加上 encoding = 'gbk' ,以避免亂碼問題。後面的導出數據的時候也一樣。
這裡我們從 csv 文件裡導入了數據,並儲存在 dataframe 中。這一步非常簡單,你只需要調用 read_csv 然後將文件的路徑傳進去就行了。header 關鍵字告訴 Pandas 哪些是數據的列名。如果沒有列名的話就將它設定為 None 。Pandas 非常聰明,所以這個經常可以省略。
準備好要進行探索和分析的數據現在數據已經導入到 Pandas 了,我們也許想看一眼數據來得到一些基本信息,以便在真正開始探索之前找到一些方向。
查看前 x 行的數據:
df.head(5)
我們只需要調用 head() 函數並且將想要查看的行數傳入。
得到的結果如下:
你可能還想看看最後幾行:
df.tail(5)
跟 head 一樣,我們只需要調用 tail 並且傳入想要查看的行數即可。注意,它並不是從最後一行倒著顯示的,而是按照數據原來的順序顯示。
得到的結果如下:
你通常使用列的名字來在 Pandas 中查找列。這一點很好而且易於使用,但是有時列名太長,比如調查問卷的一整個問題。不過你把列名縮短之後一切就好說了。
df.columns = ['water_year','rain_octsep', 'outflow_octsep',
'rain_decfeb', 'outflow_decfeb', 'rain_junaug', 'outflow_junaug']df.head(5)
需要注意的一點是,我故意沒有在每列的標籤中使用空格和破折號。之後你會看到這樣為變量命名可以使我們少打一些字符。
你得到的數據與之前的一樣,只是換了列的名字:
你通常會想知道數據的另一個特徵——它有多少條記錄。在 Pandas 中,一條記錄對應著一行,所以我們可以對數據集調用 len 方法,它將返回數據集的總行數:
len(df)
上面的代碼返回一個表示數據行數的整數,在我的數據集中,這個值是 33 。
你可能還想知道數據集的一些基本的統計數據,在 Pandas 中,這個操作簡單到哭:
pd.options.display.float_format = '{:,.3f}'.format
df.describe()
這將返回一張表,其中有諸如總數、均值、標準差之類的統計數據:
過濾在探索數據的時候,你可能經常想要抽取數據中特定的樣本,比如你有一個關於工作滿意度的調查表,你可能就想要提取特定行業或者年齡的人的數據。
在 Pandas 中有多種方法可以實現提取我們想要的信息:
有時你想提取一整列,使用列的標籤可以非常簡單地做到:
df['rain_octsep']
注意,當我們提取列的時候,會得到一個 series ,而不是 dataframe 。記得我們前面提到過,你可以把 dataframe 看作是一個 series 的字典,所以在抽取列的時候,我們就會得到一個 series。
還記得我在命名列標籤的時候特意指出的嗎?不用空格、破折號之類的符號,這樣我們就可以像訪問對象屬性一樣訪問數據集的列——只用一個點號。
df.rain_octsep
這句代碼返回的結果與前一個例子完全一樣——是我們選擇的那列數據。
如果你讀過這個系列關於 Numpy 的推文,你可能還記得一個叫做 布爾過濾(boolean masking)的技術,通過在一個數組上運行條件來得到一個布林數組。在 Pandas 裡也可以做到。
df.rain_octsep < 1000
上面的代碼將會返回一個由布爾值構成的 dataframe。True 表示在十月-九月降雨量小於 1000 mm,False 表示大於等於 1000 mm。
我們可以用這些條件表達式來過濾現有的 dataframe。
df[df.rain_octsep < 1000]
這條代碼只返回十月-九月降雨量小於 1000 mm 的記錄:
也可以通過複合條件表達式來進行過濾:
df[(df.rain_octsep < 1000) & (df.outflow_octsep < 4000)]
這條代碼只會返回 rain_octsep 中小於 1000 的和 outflow_octsep 中小於 4000 的記錄:
注意重要的一點:這裡不能用 and 關鍵字,因為會引發操作順序的問題。必須用 & 和圓括號。
如果你的數據中字符串,好消息,你也可以使用字符串方法來進行過濾:
df[df.water_year.str.startswith('199')]
注意,你必須用 .str.[string method] ,而不能直接在字符串上調用字符方法。上面的代碼返回所有 90 年代的記錄:
索引之前的部分展示了如何通過列操作來得到數據,但是 Pandas 的行也有標籤。行標籤可以是基於數字的或者是標籤,而且獲取行數據的方法也根據標籤的類型各有不同。
如果你的行標籤是數字型的,你可以通過 iloc 來引用:
df.iloc[30]
iloc 只對數字型的標籤有用。它會返回給定行的 series,行中的每一列都是返回 series 的一個元素。
也許你的數據集中有年份或者年齡的列,你可能想通過這些年份或者年齡來引用行,這個時候我們就可以設置一個(或者多個)新的索引:
df = df.set_index(['water_year'])df.head(5)
上面的代碼將 water_year 列設置為索引。注意,列的名字實際上是一個列表,雖然上面的例子中只有一個元素。如果你想設置多個索引,只需要在列表中加入列的名字即可。
上例中我們設置的索引列中都是字符型數據,這意味著我們不能繼續使用 iloc 來引用,那我們用什麼呢?用 loc 。
df.loc['2000/01']
和 iloc 一樣,loc 會返回你引用的列,唯一一點不同就是此時你使用的是基於字符串的引用,而不是基於數字的。
還有一個引用列的常用常用方法—— ix 。如果 loc 是基於標籤的,而 iloc 是基於數字的,那 ix 是基於什麼的?事實上,ix 是基於標籤的查詢方法,但它同時也支持數字型索引作為備選。
df.ix['1999/00']
與 iloc、loc 一樣,它也會返回你查詢的行。
如果 ix 可以同時起到 loc 和 iloc 的作用,那為什麼還要用後兩個?一大原因就是 ix 具有輕微的不可預測性。還記得我說過它所支持的數字型索引只是備選嗎?這一特性可能會導致 ix 產生一些奇怪的結果,比如講一個數字解釋為一個位置。而使用 iloc 和 loc 會很安全、可預測並且讓人放心。但是我要指出的是,ix 比 iloc 和 loc 要快一些。
將索引排序通常會很有用,在 Pandas 中,我們可以對 dataframe 調用 sort_index 方法進行排序。
df.sort_index(ascending=False).head(5)
我的索引本來就是有序的,為了演示,我將參數 ascending 設置為 false,這樣我的數據就會呈降序排列。
當你將一列設置為索引的時候,它就不再是數據的一部分了。如果你想將索引恢復為數據,調用 set_index 相反的方法 reset_index 即可:
df = df.reset_index('water_year')df.head(5)
這一語句會將索引恢復成數據形式:
對數據集應用函數有時你想對數據集中的數據進行改變或者某種操作。比方說,你有一列年份的數據,你需要新的一列來表示這些年份對應的年代。Pandas 中有兩個非常有用的函數,apply 和 applymap。
def base_year(year): base_year = year[:4] base_year= pd.to_datetime(base_year).year
return base_yeardf['year'] = df.water_year.apply(base_year)df.head(5)
上面的代碼創建了一個叫做 year 的列,它只將 water_year 列中的年提取了出來。這就是 apply 的用法,即對一列數據應用函數。如果你想對整個數據集應用函數,就要使用 applymap 。
操作數據集的結構另一常見的做法是重新建立數據結構,使得數據集呈現出一種更方便並且(或者)有用的形式。
掌握這些轉換最簡單的方法就是觀察轉換的過程。比起這篇文章的其他部分,接下來的操作需要你跟著練習以便能掌握它們。
首先,是 groupby :
df.groupby(df.year // 10 *10).max()
groupby 會按照你選擇的列對數據集進行分組。上例是按照年代分組。不過僅僅這樣做並沒有什麼用,我們必須對其調用函數,比如 max 、 min 、mean 等等。例中,我們可以得到 90 年代的均值。
你也可以按照多列進行分組:
decade_rain = df.groupby([df.year // 10 * 10, df.rain_octsep // 1000 * 1000])[['outflow_octsep',
'outflow_decfeb', 'outflow_junaug']].mean()decade_rain
接下來是 unstack ,最開始可能有一些困惑,它可以將一列數據設置為列標籤。最好還是看看實際的操作:
decade_rain.unstack(0)
這條語句將上例中的 dataframe 轉換為下面的形式。它將第 0 列,也就是 year 列設置為列的標籤。
讓我們再操作一次。這次使用第 1 列,也就是 rain_octsep 列:
decade_rain.unstack(1)
在進行下次操作之前,我們先創建一個用於演示的 dataframe :
high_rain = df[df.rain_octsep > 1250]high_rain
上面的代碼將會產生如下的 dataframe ,我們將會在上面演示軸向旋轉(pivoting)。
軸旋轉其實就是我們之前已經看到的那些操作的一個集合。首先,它會設置一個新的索引(set_index()),然後對索引排序(sort_index()),最後調用 unstack 。以上的步驟合在一起就是 pivot 。接下來看看你能不能搞清楚下面的代碼在幹什麼:
high_rain.pivot('year', 'rain_octsep')[['outflow_octsep', 'outflow_decfeb', 'outflow_junaug']].fillna('')
注意,最後有一個 .fillna('') 。pivot 產生了很多空的記錄,也就是值為 NaN 的記錄。我個人覺得數據集裡面有很多 NaN 會很煩,所以使用了 fillna('') 。你也可以用別的別的東西,比方說 0 。我們也可以使用 dropna(how = 'any') 來刪除有 NaN 的行,不過這樣就把所有的數據都刪掉了,所以不這樣做。
上面的 dataframe 展示了所有降雨超過 1250 的 outflow 。誠然,這並不是講解 pivot 實際應用最好的例子,但希望你能明白它的意思。看看你能在你的數據集上得到什麼結果。
合併數據集有時你有兩個相關聯的數據集,你想將它們放在一起比較或者合併它們。好的,沒問題,在 Pandas 裡很簡單:
rain_jpn = pd.read_csv('jpn_rain.csv')rain_jpn.columns = ['year', 'jpn_rainfall']uk_jpn_rain = df.merge(rain_jpn, on='year')uk_jpn_rain.head(5)
首先你需要通過 on 關鍵字來指定需要合併的列。通常你可以省略這個參數,Pandas 將會自動選擇要合併的列。
如下圖所示,兩個數據集在年份這一類上合併了。jpn_rain 數據集只有年份和降雨量兩列,通過年份列合併之後,jpn_rain 中只有降雨量那一列合併到了 UK_rain 數據集中。
使用 Pandas 快速作圖Matplotlib 很棒,但是想要繪製出還算不錯的圖表卻要寫不少代碼,而有時你只是想粗略的做個圖來探索下數據,搞清楚數據的含義。Pandas 通過 plot 來解決這個問題:
uk_jpn_rain.plot(x='year', y=['rain_octsep', 'jpn_rainfall'])
這會調用 Matplotlib 快速輕鬆地繪出了你的數據圖。通過這個圖你就可以在視覺上分析數據,而且它能在探索數據的時候給你一些方向。比如,看到我的數據圖,你會發現在 1995 年的英國好像有一場乾旱。
你會發現英國的降雨明顯少於日本,但人們卻說英國總是下雨。
保存你的數據集在清洗、重塑、探索完數據之後,你最後的數據集可能會發生很大改變,並且比最開始的時候更有用。你應該保存原始的數據集,但是你同樣應該保存處理之後的數據。
df.to_csv('uk_rain.csv')
上面的代碼將會保存你的數據到 csv 文件以便下次使用。
我們對 Pandas 的介紹就到此為止了。就像我之前所說的, Pandas 非常強大,我們只是領略到了一點皮毛而已,不過你現在知道的應該足夠你開始清洗和探索數據了。
像以前一樣,我建議你用自己感興趣的數據集做一下練習,坐下來,一杯啤酒配數據。這確實是你唯一熟悉 Pandas 以及這個系列其他庫的方式。而且你也許會發現一些有趣的東西。
別忘了分享加關注記得把這篇文章分享出去好讓其他人也能夠看到。另外請確認你已經訂閱了這個博客的郵件列表,在Twitter上關注我已經加我的Google+,以保證你不會錯過任何有用的推文。
我會閱讀所有的評論,如果你有什麼想說的、想分享的或者什麼問題,直接在下面評論吧!
Python 翻譯組是EarlGrey@編程派發起成立的一個專注於 Python 技術內容翻譯的小組,目前已有近 30 名 Python 技術愛好者加入。
翻譯組出品的內容(包括教程、文檔、書籍、視頻)將在編程派微信公眾號首發,歡迎各位 Python 愛好者推薦相關線索。
推薦線索,可直接在編程派微信公眾號推文下留言即可。
歡迎轉發至朋友圈。如無特殊註明,本公號所發文章均為原創或編譯,如需轉載,請聯繫「編程派」獲得授權。
加群交流 Python 技術問題,請先閱讀群規(點擊藍色字體),然後加編程派主頁君為好友並說明願意遵守群規。