12種用於Python數據分析的Pandas技巧

12種用於Python數據分析的Pandas技巧

來自專欄 論智

編者按:依靠完善的編程語言生態系統和更好的科學計算庫,如今Python幾乎已經成了數據科學家的首選語言。如果你正開始學習Python,而且目標是數據分析,相信NumPy、SciPy、Pandas會是你進階路上的必備法寶。尤其是對數學專業的人來說,Pandas可以作為一個首選的數據分析切入點。

作者:Aarshay Jain

編譯:Bot

本文將介紹12種用於數據分析的Pandas技巧,為了更好地描述它們的效果,這裡我們研究的主題是貸款預測,需要一個數據集輔助操作。

數據集地址:datahack.analyticsvidhya.com/contest/practice-problem-loan-prediction

預備!開始!

首先導入模塊,並將數據集載入到Python環境中:

import pandas as pdimport numpy as npdata = pd.read_csv("train.csv", index_col="Loan_ID")

1. Boolean Indexing

在表格中,如果你想根據另一列的條件篩選當前列的值,你會怎麼做?舉個例子,假設我們想要一份所有未畢業但已經辦理了貸款的女性清單,具體的操作是什麼?在這種情況下,Boolean Indexing,也就是布爾索引能提供相應的功能。我們只需這樣做:

data.loc[(data["Gender"]=="Female") & (data["Education"]=="Not Graduate") & (data["Loan_Status"]=="Y"), ["Gender","Education","Loan_Status"]]

2. Apply Function

Apply函數是使用數據和創建新變數的常用函數之一。在對DataFrame的特定行/列應用一些函數後,它會返回相應的值。這些函數既可以是默認的,也可以是用戶自定義的。如這裡我們就定義了一個查找每行/列中缺失值的函數:

#Create a new function:def num_missing(x): return sum(x.isnull())#Applying per column:print "Missing values per column:"print data.apply(num_missing, axis=0) #axis=0 defines that function is to be applied on each column#Applying per row:print "
Missing values per row:"print data.apply(num_missing, axis=1).head() #axis=1 defines that function is to be applied on each row

最後的輸出滿足預期。需要注意的一點是,這裡head() 函數只作用於第二個輸出,因為它包含多行數據。

3. 替換缺失值

對於替換缺失值,fillna()可以一步到位,它會用目標列的平均值/眾數/中位數更新缺失值。在這個示例中,我們可以用眾數分別更新Gender、Married、Self_Employed這幾列的缺失值:

#First we import a function to determine the modefrom scipy.stats import modemode(data[Gender])

輸出:

ModeResult(mode=array([『Male』], dtype=object), count=array([489]))

眾數是Male,它出現了489次。在實際操作中,很多時候眾數會是一個數組,因為可能數據中存在多個高頻詞,默認情況下,我們會選擇第一個:

mode(data[Gender]).mode[0]

*『Male』

現在我們就能更新缺失值,並檢測自己對Apply函數的掌握情況:

#Impute the values:data[Gender].fillna(mode(data[Gender]).mode[0], inplace=True)data[Married].fillna(mode(data[Married]).mode[0], inplace=True)data[Self_Employed].fillna(mode(data[Self_Employed]).mode[0], inplace=True)#Now check the #missing values again to confirm:print data.apply(num_missing, axis=0)

從結果上看,缺失值的確被補上了,但這只是最原始的形式,為了適應更多變的任務,我們還要掌握更複雜的方法,如分組使用平均值/眾數/中位數、對缺失值進行建模等。

4. Pivot Table

Pandas可以用來創建MS Excel樣式數據透視表(Pivot Table)。在本文的例子中,數據的關鍵列是含有缺失值的「LoanAmount」。為了獲得具體的貸款額度數字,我們可以用Gender、Married、Self_Employed這幾列的貸款情況進行估算:

#Determine pivot tableimpute_grps = data.pivot_table(values=["LoanAmount"], index=["Gender","Married","Self_Employed"], aggfunc=np.mean)print impute_grps

5. Multi-Indexing

如果你仔細觀察了「替換缺失值」那一節的輸出,你可能會發現一個奇怪的現象,就是每個索引都由3個值組合而成。這被稱為多重索引(Multi-Indexing),它有助於操作的快速執行。

讓我們接著這個例子,假設現在我們有各列的值,但還沒有進行缺失值估算。這時就要用到之前的各種技巧:

#iterate only through rows with missing LoanAmountfor i,row in data.loc[data[LoanAmount].isnull(),:].iterrows(): ind = tuple([row[Gender],row[Married],row[Self_Employed]]) data.loc[i,LoanAmount] = impute_grps.loc[ind].values[0]#Now check the #missing values again to confirm:print data.apply(num_missing, axis=0)

註:

  • 多索引需要元組來定義loc語句中的索引組。這是一個在函數中要用到的元組。
  • values [0]的後綴是必需的,因為默認情況下返回的值與DataFrame的值不匹配。在這種情況下,直接分配會出現錯誤。

6. Crosstab

這個函數可以被用來塑造對數據的初始「感覺(概覽)」,通俗地講,就是我們可以驗證一些基本假設。如在貸款案例中,「Credit_History」是否會影響個人貸款成功?這可以用交叉表(Crosstab)測試,如下所示:

pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True)

如果說數值還不夠直觀,我們可以用apply函數把它轉換成百分比:

def percConvert(ser): return ser/float(ser[-1]) pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True).apply(percConvert, axis=1)

顯然,有「CreditHistory」的人獲得貸款的機會更大,有80%以上的概率,而沒有「CreditHistory」的人獲得貸款的概率只有可憐的9%。

但是這就是個簡單的預測結果嗎?不是的,這裡包含著一個有趣的故事。已知有CreditHistory」的人獲得貸款的概率更高,那我們大可以設他們的「LoanStatus」為Y,其他人的為N,這樣一個模型的預測結果會是什麼樣的?我們進行了614次測試,而它的正確預測次數是82+378=460,75%的準確率!

也許你會吐槽這麼個問題為什麼要扯到統計模型。我不否認,但我只想說明一點,就是如果你能把這個模型的準確率再提升哪怕0.001%,這都是個巨大的突破。

註:這裡的75%是個大概的值,具體數字在訓練集和測試集上有所不同。我希望這能直觀地解釋為什麼在Kaggle這樣的比賽中,0.05%的準確率提升能帶來500名以上的排名提升。

7. 合併DataFrame

當我們需要將來自不同來源的信息進行整合時,合併DataFrame(或者你們愛說數據框)就變得很重要了。現在房價很熱,炒房團也很熱,所以我們先用數據集的數據假設一份各地區房屋均價(1平)不同的表:

prop_rates = pd.DataFrame([1000,5000,12000],index = [Rural,Semiurban,Urban],columns = [rates])prop_rates

現在我們可以將這些信息與原始DataFrame合併為:

data_merged = data.merge(right=prop_rates, how=inner,left_on=Property_Area,right_index=True, sort=False)data_merged.pivot_table(values=Credit_History,index=[Property_Area,rates], aggfunc=len)

反正買不起,好了,數據合併成功了。請注意,values參數在這裡是沒什麼用的,因為我們只是做計數。

8. DataFrame排序

Pandas可以輕鬆基於多列進行排序,如下所示

data_sorted = data.sort_values([ApplicantIncome,CoapplicantIncome], ascending=False)data_sorted[[ApplicantIncome,CoapplicantIncome]].head(10)

註:Pandas的sort函數已經不能用了,現在排序要調用sort_value

9. 繪圖(Boxplot和直方圖)

很多人可能不知道自己能直接在Pandas里繪製盒形圖和直方圖,無需單獨調用matplotlib,一行命令就能搞定。例如,如果我們想比較Loan_Status的ApplicantIncome的分布情況:

import matplotlib.pyplot as plt%matplotlib inlinedata.boxplot(column="ApplicantIncome",by="Loan_Status")

data.hist(column="ApplicantIncome",by="Loan_Status",bins=30)

這兩幅圖表明收入在貸款過程中所佔的比重並沒有我們想像中那麼高,無論是被拒的還是收到貸款的,他們的收入沒有非常明顯的區別。

10. Cut function for binning

有時候聚類後的數據會更有意義。以今天最近車禍頻發的自動駕駛汽車為例,如果我們要用它捕獲的數據重現某條路上的交通情況,比起一整天的流暢數據,或是把一天均勻分割為24個小時,「早上」「下午」「晚上」「夜晚」「深夜」這幾個關鍵時段的數據包含的信息量更多,也更有效。如果我們用這些數據建模,它的成果會更直觀,而且可以避免過擬合。

這裡我們定義一個簡單的函數,它可以高效binning:

#Binning:def binning(col, cut_points, labels=None): #Define min and max values: minval = col.min() maxval = col.max() #create list by adding min and max to cut_points break_points = [minval] + cut_points + [maxval] #if no labels provided, use default labels 0 ... (n-1) if not labels: labels = range(len(cut_points)+1) #Binning using cut function of pandas colBin = pd.cut(col,bins=break_points,labels=labels,include_lowest=True) return colBin#Binning age:cut_points = [90,140,190]labels = ["low","medium","high","very high"]data["LoanAmount_Bin"] = binning(data["LoanAmount"], cut_points, labels)print pd.value_counts(data["LoanAmount_Bin"], sort=False)

11. 為nominal數據編碼

有時候我們需要對稱名數據(nominal數據)重新分類,這可能是由於各種原因造成的:

  • 一些演算法(如Logistic回歸)要求所有輸入都是數字,所以我們要把稱名變數重新編碼為0,1 ...(n-1)。
  • 有時一個類別可能包含多種表達,如「溫度」可以被記錄為「High」「Medium」「Low」「H」「low」,其中「High」和「H」是一碼事,「Low」和「low」也是一碼事,但Python會認為它們是不同的。
  • 有些類別的頻數非常低,所以我們應該把它們合併起來。

為了解決這個問題,這裡我們定義了一個簡單的函數,它把輸入作為「字典」,然後調用Pandas的replace函數重新編碼:

#Define a generic function using Pandas replace functiondef coding(col, codeDict): colCoded = pd.Series(col, copy=True) for key, value in codeDict.items(): colCoded.replace(key, value, inplace=True) return colCoded#Coding LoanStatus as Y=1, N=0:print Before Coding:print pd.value_counts(data["Loan_Status"])data["Loan_Status_Coded"] = coding(data["Loan_Status"], {N:0,Y:1})print
After Coding:print pd.value_counts(data["Loan_Status_Coded"])

12. 迭代dataframe的行

這不是一個常用的技巧,但如果遇到這種問題,相信沒人想到時候再絞盡腦汁想辦法,或者直接自暴自棄用for循環遍歷所有行。這裡我們舉兩個要用到這種方法的場景:

  • 當帶有數字的nominal variable被當成數字。
  • 當某一行帶有字元(因為數據錯誤)的Numeric variable被當成分類。

這時我們就要手動定義列的類別。雖然很麻煩,但這之後如果我們再檢查數據類別:

#Check current type:data.dtypes

它的輸出會是:

這裡我們看到Credit_History是一個稱名變數,但是它卻顯示為float。解決這些問題的一個好方法是創建一個包含列名和類型的csv文件,有了它,我們就可以創建一個函數來讀取文件並分配列數據類型。

#Load the file:colTypes = pd.read_csv(datatypes.csv)print colTypes

載入這個文件後,我們可以遍歷每一行,並用type列將數據類型賦值給feature列中定義的變數名稱。

#Iterate through each row and assign variable type.#Note: astype is used to assign typesfor i, row in colTypes.iterrows(): #i: dataframe index; row: each row in series format if row[type]=="categorical": data[row[feature]]=data[row[feature]].astype(np.object) elif row[type]=="continuous": data[row[feature]]=data[row[feature]].astype(np.float)print data.dtypes

希望本文對你有用!


推薦閱讀:

基於TF-IDF,word2vec,LDA等方法對股票概念及概念對應關鍵詞挖掘(一)
惠眾在線行業情報|互聯網改變下的傳統節日
不識廬山真面目,只緣身在此山中
給妹子講python-S02E16多級索引Pandas的取值、分片與運算
小白python之路的開啟

TAG:Python | 數據挖掘 | 數據分析 |