十分鐘快速入門 Pandas
本文由 Python 翻譯組 最新翻譯出品,原作者為 Jamal Moir,譯者為 liubj2016,並由編程派作者 EarlGrey 校對。這是使用 Python 進行科學計算的系列文章,上一篇可點此查看:Matplotlib 快速入門。
譯者簡介:liubj2016,中南財經政法大學,金融工程系學生。Python使用方向:數據分析,機器學習和量化投資。
本文獨家發佈於編程派。未經許可,禁止轉載。
Pandas 是我最喜愛的庫之一。通過帶有標籤的列和索引,Pandas 使我們可以以一種所有人都能理解的方式來處理數據。它可以讓我們毫不費力地從諸如 csv 類型的文件中導入數據。我們可以用它快速地對數據進行複雜的轉換和過濾等操作。Pandas 真是超級棒。
我覺得它和 Numpy、Matplotlib 一起構成了一個 Python 數據探索和分析的強大基礎。Scipy (將會在下一篇推文里介紹)當然也是一大主力並且是一個絕對贊的庫,但是我覺得前三者才是 Python 科學計算真正的頂樑柱。
那麼,趕緊看看 python 科學計算系列的第三篇推文,一窺 Pandas 的芳容吧。如果你還沒看其它幾篇文章的話,別忘了去看看。
導入 Pandas
第一件事當然是請出我們的明星 —— Pandas。
import pandas as pd # This is the standardn
這是導入 pandas 的標準方法。我們不想一直寫 pandas 的全名,但是保證代碼的簡潔和避免命名衝突都很重要,所以折中使用 pd 。如果你去看別人使用 pandas 的代碼,就會看到這種導入方式。
Pandas 中的數據類型
Pandas 基於兩種數據類型,series 和 dataframe。
series 是一種一維的數據類型,其中的每個元素都有各自的標籤。如果你之前看過這個系列關於Numpy 的推文,你可以把它當作一個由帶標籤的元素組成的 numpy 數組。標籤可以是數字或者字元。
dataframe 是一個二維的、表格型的數據結構。Pandas 的 dataframe 可以儲存許多不同類型的數據,並且每個軸都有標籤。你可以把它當作一個 series 的字典。
將數據導入 Pandas
在對數據進行修改、探索和分析之前,我們得先導入數據。多虧了 Pandas ,這比在 Numpy 中還要容易。
這裡我鼓勵你去找到自己感興趣的數據並用來練習。你的(或者別的)國家的網站就是不錯的數據源。如果要舉例的話,首推英國政府數據和美國政府數據。Kaggle也是個很好的數據源。
我將使用英國降雨數據,這個數據集可以很容易地從英國政府網站上下載到。此外,我還下載了一些日本降雨量的數據。
英國降雨數據:下載地址 日本的數據實在是沒找到,抱歉。
# Reading a csv into Pandas.ndf = pd.read_csv(uk_rain_2014.csv, header=0)n
譯者註:如果你的數據集中有中文的話,最好在裡面加上 encoding = gbk ,以避免亂碼問題。後面的導出數據的時候也一樣。
這裡我們從 csv 文件里導入了數據,並儲存在 dataframe 中。這一步非常簡單,你只需要調用read_csv 然後將文件的路徑傳進去就行了。header 關鍵字告訴 Pandas 哪些是數據的列名。如果沒有列名的話就將它設定為 None 。Pandas 非常聰明,所以這個經常可以省略。
準備好要進行探索和分析的數據
現在數據已經導入到 Pandas 了,我們也許想看一眼數據來得到一些基本信息,以便在真正開始探索之前找到一些方向。
查看前 x 行的數據:
# Getting first x rows.ndf.head(5)n
我們只需要調用 head() 函數並且將想要查看的行數傳入。
得到的結果如下:
你可能還想看看最後幾行:
# Getting last x rows.ndf.tail(5)n
跟 head 一樣,我們只需要調用 tail 並且傳入想要查看的行數即可。注意,它並不是從最後一行倒著顯示的,而是按照數據原來的順序顯示。
得到的結果如下:
你通常使用列的名字來在 Pandas 中查找列。這一點很好而且易於使用,但是有時列名太長,比如調查問卷的一整個問題。不過你把列名縮短之後一切就好說了。
# Changing column labels.ndf.columns = [water_year,rain_octsep, outflow_octsep,n rain_decfeb, outflow_decfeb, rain_junaug, outflow_junaug]nndf.head(5)n
需要注意的一點是,我故意沒有在每列的標籤中使用空格和破折號。之後你會看到這樣為變數命名可以使我們少打一些字元。
你得到的數據與之前的一樣,只是換了列的名字:
你通常會想知道數據的另一個特徵——它有多少條記錄。在 Pandas 中,一條記錄對應著一行,所以我們可以對數據集調用 len 方法,它將返回數據集的總行數:
# Finding out how many rows dataset has.nlen(df)n
上面的代碼返回一個表示數據行數的整數,在我的數據集中,這個值是 33 。
你可能還想知道數據集的一些基本的統計數據,在 Pandas 中,這個操作簡單到哭:
# Finding out basic statistical information on your dataset.npd.options.display.float_format = {:,.3f}.format # Limit output to 3 decimal places.ndf.describe()n
這將返回一張表,其中有諸如總數、均值、標準差之類的統計數據:
過濾
在探索數據的時候,你可能經常想要抽取數據中特定的樣本,比如你有一個關於工作滿意度的調查表,你可能就想要提取特定行業或者年齡的人的數據。
在 Pandas 中有多種方法可以實現提取我們想要的信息:
有時你想提取一整列,使用列的標籤可以非常簡單地做到:
# Getting a column by labelndf[rain_octsep]n
注意,當我們提取列的時候,會得到一個 series ,而不是 dataframe 。記得我們前面提到過,你可以把 dataframe 看作是一個 series 的字典,所以在抽取列的時候,我們就會得到一個 series。
還記得我在命名列標籤的時候特意指出的嗎?不用空格、破折號之類的符號,這樣我們就可以像訪問對象屬性一樣訪問數據集的列——只用一個點號。
# Getting a column by label using .ndf.rain_octsepn
這句代碼返回的結果與前一個例子完全一樣——是我們選擇的那列數據。
如果你讀過這個系列關於 Numpy 的推文,你可能還記得一個叫做 布爾過濾(boolean masking)的技術,通過在一個數組上運行條件來得到一個布林數組。在 Pandas 里也可以做到。
# Creating a series of booleans based on a conditionalndf.rain_octsep < 1000 # Or df[rain_octsep] < 1000n
上面的代碼將會返回一個由布爾值構成的 dataframe。True 表示在十月-九月降雨量小於 1000 mm,False 表示大於等於 1000 mm。
我們可以用這些條件表達式來過濾現有的 dataframe。
# Using a series of booleans to filterndf[df.rain_octsep < 1000]n
這條代碼只返回十月-九月降雨量小於 1000 mm 的記錄:
也可以通過複合條件表達式來進行過濾:
# Filtering by multiple conditionalsndf[(df.rain_octsep < 1000) & (df.outflow_octsep < 4000)] # Cant use the keyword andn
這條代碼只會返回 rain_octsep 中小於 1000 的和 outflow_octsep 中小於 4000 的記錄:
注意重要的一點:這裡不能用 and 關鍵字,因為會引發操作順序的問題。必須用 & 和圓括弧。
如果你的數據中字元串,好消息,你也可以使用字元串方法來進行過濾:
# Filtering by string methodsndf[df.water_year.str.startswith(199)]n
注意,你必須用 .str.[string method] ,而不能直接在字元串上調用字元方法。上面的代碼返回所有 90 年代的記錄:
索引
之前的部分展示了如何通過列操作來得到數據,但是 Pandas 的行也有標籤。行標籤可以是基於數字的或者是標籤,而且獲取行數據的方法也根據標籤的類型各有不同。
如果你的行標籤是數字型的,你可以通過 iloc 來引用:
# Getting a row via a numerical indexndf.iloc[30]n
iloc 只對數字型的標籤有用。它會返回給定行的 series,行中的每一列都是返回 series 的一個元素。
也許你的數據集中有年份或者年齡的列,你可能想通過這些年份或者年齡來引用行,這個時候我們就可以設置一個(或者多個)新的索引:
# Setting a new index from an existing columnndf = df.set_index([water_year])ndf.head(5)n
上面的代碼將 water_year 列設置為索引。注意,列的名字實際上是一個列表,雖然上面的例子中只有一個元素。如果你想設置多個索引,只需要在列表中加入列的名字即可。
上例中我們設置的索引列中都是字元型數據,這意味著我們不能繼續使用 iloc 來引用,那我們用什麼呢?用 loc 。
# Getting a row via a label-based indexndf.loc[2000/01]n
和 iloc 一樣,loc 會返回你引用的列,唯一一點不同就是此時你使用的是基於字元串的引用,而不是基於數字的。
還有一個引用列的常用常用方法—— ix 。如果 loc 是基於標籤的,而 iloc 是基於數字的,那 ix是基於什麼的?事實上,ix 是基於標籤的查詢方法,但它同時也支持數字型索引作為備選。
# Getting a row via a label-based or numerical indexndf.ix[1999/00] # Label based with numerical index fallback *Not recommendedn
與 iloc、loc 一樣,它也會返回你查詢的行。
如果 ix 可以同時起到 loc 和 iloc 的作用,那為什麼還要用後兩個?一大原因就是 ix 具有輕微的不可預測性。還記得我說過它所支持的數字型索引只是備選嗎?這一特性可能會導致 ix 產生一些奇怪的結果,比如講一個數字解釋為一個位置。而使用 iloc 和 loc 會很安全、可預測並且讓人放心。但是我要指出的是,ix 比 iloc 和 loc 要快一些。
將索引排序通常會很有用,在 Pandas 中,我們可以對 dataframe 調用 sort_index 方法進行排序。
df.sort_index(ascending=False).head(5) #inplace=True to apple the sorting in placen
我的索引本來就是有序的,為了演示,我將參數 ascending 設置為 false,這樣我的數據就會呈降序排列。
當你將一列設置為索引的時候,它就不再是數據的一部分了。如果你想將索引恢復為數據,調用set_index 相反的方法 reset_index 即可:
# Returning an index to datandf = df.reset_index(water_year)ndf.head(5)n
這一語句會將索引恢復成數據形式:
對數據集應用函數
有時你想對數據集中的數據進行改變或者某種操作。比方說,你有一列年份的數據,你需要新的一列來表示這些年份對應的年代。Pandas 中有兩個非常有用的函數,apply 和 applymap。
# Applying a function to a columnndef base_year(year):n base_year = year[:4]n base_year= pd.to_datetime(base_year).yearn return base_yearnndf[year] = df.water_year.apply(base_year)ndf.head(5)n
上面的代碼創建了一個叫做 year 的列,它只將 water_year 列中的年提取了出來。這就是 apply的用法,即對一列數據應用函數。如果你想對整個數據集應用函數,就要使用 applymap 。
操作數據集的結構
另一常見的做法是重新建立數據結構,使得數據集呈現出一種更方便並且(或者)有用的形式。
掌握這些轉換最簡單的方法就是觀察轉換的過程。比起這篇文章的其他部分,接下來的操作需要你跟著練習以便能掌握它們。
首先,是 groupby :
#Manipulating structure (groupby, unstack, pivot)n# Groubyndf.groupby(df.year // 10 *10).max()n
groupby 會按照你選擇的列對數據集進行分組。上例是按照年代分組。不過僅僅這樣做並沒有什麼用,我們必須對其調用函數,比如 max 、 min 、mean 等等。例中,我們可以得到 90 年代的均值。
你也可以按照多列進行分組:
# Grouping by multiple columnsndecade_rain = df.groupby([df.year // 10 * 10, df.rain_octsep // 1000 * 1000])[[outflow_octsep, outflow_decfeb, outflow_junaug]].mean()ndecade_rainn
接下來是 unstack ,最開始可能有一些困惑,它可以將一列數據設置為列標籤。最好還是看看實際的操作:
# Unstackingndecade_rain.unstack(0)n
這條語句將上例中的 dataframe 轉換為下面的形式。它將第 0 列,也就是 year 列設置為列的標籤。
讓我們再操作一次。這次使用第 1 列,也就是 rain_octsep 列:
# More unstackingndecade_rain.unstack(1)n
在進行下次操作之前,我們先創建一個用於演示的 dataframe :
# Create a new dataframe containing entries which n# has rain_octsep values of greater than 1250nhigh_rain = df[df.rain_octsep > 1250]nhigh_rainn
上面的代碼將會產生如下的 dataframe ,我們將會在上面演示軸向旋轉(pivoting)。
軸旋轉其實就是我們之前已經看到的那些操作的一個集合。首先,它會設置一個新的索引(set_index()),然後對索引排序(sort_index()),最後調用 unstack 。以上的步驟合在一起就是pivot 。接下來看看你能不能搞清楚下面的代碼在幹什麼:
#Pivotingn#does set_index, sort_index and unstack in a rownhigh_rain.pivot(year, rain_octsep)[[outflow_octsep, outflow_decfeb, outflow_junaug]].fillna()n
注意,最後有一個 .fillna() 。pivot 產生了很多空的記錄,也就是值為 NaN 的記錄。我個人覺得數據集裡面有很多 NaN 會很煩,所以使用了 fillna() 。你也可以用別的別的東西,比方說 0 。我們也可以使用 dropna(how = any) 來刪除有 NaN 的行,不過這樣就把所有的數據都刪掉了,所以不這樣做。
上面的 dataframe 展示了所有降雨超過 1250 的 outflow 。誠然,這並不是講解 pivot 實際應用最好的例子,但希望你能明白它的意思。看看你能在你的數據集上得到什麼結果。
合併數據集
有時你有兩個相關聯的數據集,你想將它們放在一起比較或者合併它們。好的,沒問題,在 Pandas 里很簡單:
# Merging two datasets togethernrain_jpn = pd.read_csv(jpn_rain.csv)nrain_jpn.columns = [year, jpn_rainfall]nnuk_jpn_rain = df.merge(rain_jpn, on=year)nuk_jpn_rain.head(5)n
首先你需要通過 on 關鍵字來指定需要合併的列。通常你可以省略這個參數,Pandas 將會自動選擇要合併的列。
如下圖所示,兩個數據集在年份這一類上合併了。jpn_rain 數據集只有年份和降雨量兩列,通過年份列合併之後,jpn_rain 中只有降雨量那一列合併到了 UK_rain 數據集中。
使用 Pandas 快速作圖
Matplotlib 很棒,但是想要繪製出還算不錯的圖表卻要寫不少代碼,而有時你只是想粗略的做個圖來探索下數據,搞清楚數據的含義。Pandas 通過 plot 來解決這個問題:
# Using pandas to quickly plot graphsnuk_jpn_rain.plot(x=year, y=[rain_octsep, jpn_rainfall])n
這會調用 Matplotlib 快速輕鬆地繪出了你的數據圖。通過這個圖你就可以在視覺上分析數據,而且它能在探索數據的時候給你一些方向。比如,看到我的數據圖,你會發現在 1995 年的英國好像有一場乾旱。
你會發現英國的降雨明顯少於日本,但人們卻說英國總是下雨。
保存你的數據集
在清洗、重塑、探索完數據之後,你最後的數據集可能會發生很大改變,並且比最開始的時候更有用。你應該保存原始的數據集,但是你同樣應該保存處理之後的數據。
# Saving your data to a csvndf.to_csv(uk_rain.csv)n
上面的代碼將會保存你的數據到 csv 文件以便下次使用。
我們對 Pandas 的介紹就到此為止了。就像我之前所說的, Pandas 非常強大,我們只是領略到了一點皮毛而已,不過你現在知道的應該足夠你開始清洗和探索數據了。
像以前一樣,我建議你用自己感興趣的數據集做一下練習,坐下來,一杯啤酒配數據。這確實是你唯一熟悉 Pandas 以及這個系列其他庫的方式。而且你也許會發現一些有趣的東西。
別忘了分享加關注
記得把這篇文章分享出去好讓其他人也能夠看到。另外請確認你已經訂閱了這個博客的郵件列表,在Twitter上關注我已經加我的Google+,以保證你不會錯過任何有用的推文。
我會閱讀所有的評論,如果你有什麼想說的、想分享的或者什麼問題,直接在下面評論吧!
點此查看原文鏈接。
譯文原文鏈接。
Python 翻譯組是EarlGrey@編程派發起成立的一個專註於 Python 技術內容翻譯的小組,目前已有近 30 名 Python 技術愛好者加入。
翻譯組出品的內容(包括教程、文檔、書籍、視頻)將在編程派微信公眾號首發,歡迎各位 Python 愛好者推薦相關線索。
推薦線索,可直接在編程派微信公眾號推文下留言即可。
推薦閱讀: