利用 Scikit Learn的Python數據預處理實戰指南
簡而言之,預處理是指在你將數據“喂給”算法之前進行的一係列轉換操作。在Python中,scikit-learn庫在sklearn.preprocessing下有預裝的功能。有更多的選擇來進行預處理,這將是我們要探索的。
讀完本文,你將具備數據預處理的基本技能並對其有更深入的理解。為了方便起見,我附上了進一步學習機器學習算法的一些資源,並且為更好地掌握這些概念,設計了幾個小練習。
可用數據集
▼
本文中,我使用了部分的貸款預測數據,缺失觀測值的數據已被移除(需要數據的讀者朋友,請在評論區留下電郵地址,我們會把數據發給你——譯者注)。
備注:貸款預測問題中,測試集數據是訓練集的子集。
現在,讓我們從導入重要的包和數據集開始。
# 導入pandas
>> import pandas as pd
# 導入訓練用的數據集
>> X_train=pd.read_csv('X_train.csv')
>> Y_train=pd.read_csv('Y_train.csv')
# 導入測試數據集
>> X_test=pd.read_csv('X_test.csv')
>> Y_test=pd.read_csv('Y_test.csv')
對我們的數據集進行仔細觀察。
>> print (X_train.head())
Loan_ID Gender Married Dependents Education Self_Employed
15 LP001032 Male No 0 Graduate No
248 LP001824 Male Yes 1 Graduate No
590 LP002928 Male Yes 0 Graduate No
246 LP001814 Male Yes 2 Graduate No
388 LP002244 Male Yes 0 Graduate No
ApplicantIncome CoapplicantIncome LoanAmount Loan_Amount_Term
15 4950 0.0 125.0 360.0
248 2882 1843.0 123.0 480.0
590 3000 3416.0 56.0 180.0
246 9703 0.0 112.0 360.0
388 2333 2417.0 136.0 360.0
Credit_History Property_Area
15 1.0 Urban
248 1.0 Semiurban
590 1.0 Semiurban
246 1.0 Urban
388 1.0 Urban
特征縮放
▼
特征縮放是用來限製變量範圍的方法,以讓它們能在相同的尺度上進行比較。這是在連續變量上操作的。讓我們輸出數據集中所有連續變量的分布。
>> import matplotlib.pyplot as plt
>> X_train[X_train.dtypes[(X_train.dtypes=="float64")|(X_train.dtypes=="int64")]
.index.values].hist(figsize=[11,11])
理解以上圖示後,我們推斷ApplicantIncome(申請人收入) 和CoapplicantIncome(共同申請人收入) 有相似的尺度範圍(0-50000$),LoanAmount(貸款額度) 以千為單位,範圍在0 到 600$之間,而Loan_Amount_Term(貸款周期)與其它變量完全不同,因為它的單位是月份,而其它變量單位是美元。
如果我們嚐試應用基於距離的算法,如KNN,在這些特征上,範圍最大的特征會決定最終的輸出結果,那麼我們將得到較低的預測精度。我們可通過特征縮放解決這個問題。讓我們實踐一下。
資料:閱讀這篇關於KNN的文章獲得更好的理解。(https://www.analyticsvidhya.com/blog/2014/10/introduction-k-neighbours-algorithm-clustering/)
讓我們在我們的數據集中試試KNN,看看它表現如何。
# 初始化和擬合一個KNN模型
>> from sklearn.neighbors import KNeighborsClassifier
>> knn=KNeighborsClassifier(n_neighbors=5)
>> knn.fit(X_train[['ApplicantIncome', 'CoapplicantIncome','LoanAmount',
'Loan_Amount_Term', 'Credit_History']],Y_train)
# Checking the performance of our model on the testing data set
# 檢查我們的模型在測試數據集上的性能
>> from sklearn.metrics import accuracy_score
>> accuracy_score(Y_test,knn.predict(X_test[['ApplicantIncome', 'CoapplicantIncome',
'LoanAmount', 'Loan_Amount_Term', 'Credit_History']]))
Out : 0.61458333333333337
我們得到了大約61%的正確預測,這不算糟糕,但在真正實踐中,這是否足夠?我們能否將該模型部署於實際問題中?為回答該問題,讓我們看看在訓練集中關於Loan_Status(貸款狀態) 的分布。
>> Y_train.Target.value_counts()/Y_train.Target.count()
Out : Y 0.705729
N 0.294271
Name: Loan_Status, dtype: float64
大約有70%貸款會被批準,因為有較高的貸款批準率,我們就建立一個所有貸款都通過的預測模型,繼續操作並檢測我們的預測精度。
>> Y_test.Target.value_counts()/Y_test.Target.count()
Out : Y 0.635417
N 0.364583
Name: Loan_Status, dtype: float64
哇!通過猜測,我們獲得63%的精度。這意味著,該模型比我們的預測模型得到更高的精度?
這可能是因為某些具有較大範圍的無關緊要的變量主導了目標函數。我們可以通過縮小所有特征到同樣的範圍來消除該問題。Sklearn提供了MinMaxScaler 工具將所有特征的範圍縮小到0-1之間,MinMaxScaler 的數學表達式如下所示:
讓我們在我們的問題中試試該工具。
# 導入MinMaxScaler並初始化
>> from sklearn.preprocessing import MinMaxScaler
>> min_max=MinMaxScaler()# Scaling down both train and test data set
>> X_train_minmax=min_max.fit_transform(X_train[['ApplicantIncome', 'CoapplicantIncome',
'LoanAmount', 'Loan_Amount_Term', 'Credit_History']])
>> X_test_minmax=min_max.fit_transform(X_test[['ApplicantIncome', 'CoapplicantIncome',
'LoanAmount', 'Loan_Amount_Term', 'Credit_History']])
現在,我們已經完成縮放操作,讓我們在縮放後的數據上應用KNN並檢測其精度。
# 在我們縮小後的數據集上擬合KNN
>> knn=KNeighborsClassifier(n_neighbors=5)
>> knn.fit(X_train_minmax,Y_train)
# 檢查該模型的精度
>> accuracy_score(Y_test,knn.predict(X_test_minmax))
Out : 0.75
太好了!我們的精度從61%提升到了75%。這意味在基於距離的方法中(如:KNN),一些大範圍的特征對預測結果有決定性作用。
應當牢記,當使用基於距離的算法時,我們必須嚐試將數據縮放,這樣較不重要的特征不會因為自身較大的範圍而主導目標函數。此外,具有不同度量單位的特征也應該進行縮放,這樣給每個特征具有相同的初始權重,最終我們會得到更好的預測模型。
嚐試利用邏輯回歸模型做相同的練習(參數: penalty=’l2′,C=0.01), 並請在評論區留下縮放前後的精度。
特征標準化
▼
在進入這部分內容前,我建議你先完成練習1。
在之前的章節,我們在貸款預測數據集之上操作,並在其上擬合出一個KNN學習模型。通過縮小數據,我們得到了75%的精度,這看起來十分不錯。我在邏輯回歸模型上嚐試了同樣的練習, 並得到如下結果:
Before Scaling : 61%
After Scaling : 63%
縮放前:61%
縮放後:63%
縮放後的精度與我們憑猜測得到的預測精度相近,這並不是很了不起的成就。那麼,這是怎麼回事呢?在精度上,為什麼不像用KNN一樣有令人滿意的提升?
資料:閱讀本文(https://www.analyticsvidhya.com/blog/2015/08/comprehensive-guide-regression/)獲得對邏輯回歸更好的理解。
答案在此:
在邏輯回歸中,每個特征都被分配了權重或係數(Wi)。如果某個特征有相對來說比較大的範圍,而且其在目標函數中無關緊要,那麼邏輯回歸模型自己就會分配一個非常小的值給它的係數,從而中和該特定特征的影響優勢,而基於距離的方法,如KNN,沒有這樣的內置策略,因此需要縮放。
我們是否忘了什麼?我們的邏輯模型的預測精度和猜測的幾乎接近。
現在,我將在此介紹一個新概念,叫作標準化。很多Sklearn中的機器學習算法都需要標準化後的數據,這意味數據應具有零均值和單位方差。
標準化(或Z-score正則化)是對特征進行重新調整,讓數據服從基於 μ=0 和 σ=1的標準正態分布,其中μ是均值(平均值)而σ是關於均值的標準偏差。樣本的標準分數(也稱為z-scores)按如下所示的方法計算:
線性模型中因子如l1,l2正則化和學習器的目標函數中的SVM中的RBF核心假設所有的特征都集中在0周圍並且有著相同順序的偏差。
有更大順序的方差的特征將在目標函數上起決定作用,因為前麵的章節中,有著更大範圍的特征產生過此情形。 正如我們在練習1中看到的,沒進行任何預處理的數據之上的精度是61%,讓我們標準化我們的數據,在其上應用邏輯回歸。Sklearn提供了尺度範圍用於標準化數據。
# 標準化訓練和測試數據
>> from sklearn.preprocessing import scale
>> X_train_scale=scale(X_train[['ApplicantIncome', 'CoapplicantIncome',
'LoanAmount', 'Loan_Amount_Term', 'Credit_History']])
>> X_test_scale=scale(X_test[['ApplicantIncome', 'CoapplicantIncome',
'LoanAmount', 'Loan_Amount_Term', 'Credit_History']])
# 在我們的標準化了的數據集上擬合邏輯回歸
>> from sklearn.linear_model import LogisticRegression
>> log=LogisticRegression(penalty='l2',C=.01)
>> log.fit(X_train_scale,Y_train)
# 檢查該模型的精度
>> accuracy_score(Y_test,log.predict(X_test_scale))
Out : 0.75
我們再次達到縮放後利用KNN所能達到的我們最大的精度。這意味著,當使用l1或l2正則化估計時,標準化數據幫助我們提高預測模型的精度。其它學習模型,如有歐幾裏得距離測量的KNN、k-均值、SVM、感知器、神經網絡、線性判別分析、主成分分析對於標準化數據可能會表現更好。
盡管如此,我還是建議你要理解你的數據和對其將要使用的算法類型。過一段時間後,你會有能力判斷出是否要對數據進行標準化操作。
備注:在縮放和標準化中二選一是個令人困惑的選擇,你必須對數據和要使用的學習模型有更深入的理解,才能做出決定。對於初學者,你可以兩種方法都嚐試下並通過交叉驗證精度來做出選擇。
資料:閱讀本文(https://www.analyticsvidhya.com/blog/2015/11/improve-model-performance-cross-validation-in-python-r/)會對交叉驗證有更好的理解
嚐試利用SVM模型做相同的練習,並請在評論區留下標準化前後的精度。
資料:閱讀本文(https://www.analyticsvidhya.com/blog/2015/10/understaing-support-vector-machine-example-code/)會對SVM有更好的理解。
標簽編碼
▼
在前麵的章節裏,我們對連續數字特征做了預處理。但是,我們的數據集還有其它特征,如性別(Gender)、婚否(Married)、供養人(Dependents)、自雇與否(Self-Employed)和教育程度(Education)。所有這些類別特征的值是字符型的。例如,性別(Gender)有兩個層次,或者是男性(Male),或者是女性(Female)。讓我們把這些特征放進我們的邏輯回歸模型中。
#在整個數據集上擬合放邏輯回歸模型
>> log=LogisticRegression(penalty='l2',C=.01)
>> log.fit(X_train,Y_train)
#檢查模型的精度
>> accuracy_score(Y_test,log.predict(X_test))
Out : ValueError: could not convert string to float: Semiurban
我們得到一個錯誤信息:不能把字符型轉換成浮點型。因此,這裏真正在發生的事是像邏輯回歸和基於距離的學習模式,如KNN、SVM、基於樹的方法等等,在Sklearn中需要數字型數組。擁有字符型值的特征不能由這些學習模式來處理。
Sklearn提供了一個非常有效的工具把類別特征層級編碼成數值。LabelEncoder用0到n_classes-1之間的值對標簽進行編碼。
讓我們對所有的類別特征進行編碼。
#導入LabelEncoder並初始化
>> from sklearn.preprocessing import LabelEncoder
>> le=LabelEncoder()
#遍曆在訓練和測試集中所有的公共列
>> for col in X_test.columns.values:
#隻對類別變量編碼
if X_test[col].dtypes=='object':
#利用全部數據形成一個所有層次的詳盡清單
data=X_train[col].append(X_test[col])
le.fit(data.values)
X_train[col]=le.transform(X_train[col])
X_test[col]=le.transform(X_test[col])
我們所有的類別特征都已編碼。用X_train.head()可以查看更新了的數據集。我們將看下性別(Gender)在編碼前後的頻率分布。
Before : Male 318
Female 66
Name: Gender, dtype: int64
After : 1 318
0 66
Name: Gender, dtype: int64
現在我們已經完成了標簽編碼,讓我們在同時有著類別和連續特征的數據集上運行邏輯回歸模型。
#特征標準化
>> X_train_scale=scale(X_train)
>> X_test_scale=scale(X_test)
#擬合邏輯回歸模型
>> log=LogisticRegression(penalty='l2',C=.01)
>> log.fit(X_train_scale,Y_train)
#檢查模型的精度
>> accuracy_score(Y_test,log.predict(X_test_scale))
Out : 0.75
現在可以用了。但是,精度仍然和我們從數字特征標準化之後用邏輯回歸得到的一樣。這意味著我們加入的類別特征在我們的目標函數中不是非常顯著。
練習3
試試用所有的特征作為非獨立變量進行決策樹分類,並評論一下你得到的精度。
資料:瀏覽本文(https://www.analyticsvidhya.com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/)中關於決策樹的內容以更好地理解。
一位有效編碼(One-Hot-Encoding,主要是采用位狀態寄存器來對某個狀態進行編碼,每個狀態都有自己獨立的寄存器位,並且在任意時候隻有一位有效——譯者注)。
一位有效編碼把每個帶有n個可能值的類別特征轉換成n個二進製特征,隻有一個是有效的。
大多數機器學習算法不是為每個特征設置單個權重就是計算樣本之間的距離。如線性模型算法(例如:邏輯回歸)屬於第一類。
讓我們看一看一個來自loan_prediction數據集的例子。特征從屬(Feature Dependents)有4個可能的值:0、1、2和3+,這些是編過碼的,沒有丟掉0、1、2和3的一般性。
在線性分類器中,我們就分配一個權重“W”給這個特征,這將在W*Dependents+K>0或相當於W*Dependents的約束下做出決策。<>
Let f(w)= W*Dependents
讓f(w)=W*Dependents
Possible values that can be attained by the equation are 0, W, 2W and 3W. A problem with this equation is that the weight “W” cannot make decision based on four choices. It can reach to a decision in following ways:
由方程獲得的可能值是0、W、2W和3W。這個方程的一個問題是權重W不能在4個選擇的基礎上得到。它可以用下麵的方法來決定:
• 所有導致同樣的決定(所有的值或反之亦然)<>
• 3:2的層級分配(當f(w)>2W時的決策區間)
• 2:2的層級分配(當f(w)>W時的決策區間)
這裏我們可以看到丟失了許多不同的可能決策,比如:“0”和“2W”應該給予相同的標簽,“3W”和“W”是額外的。
這個問題可以通過一位有效編碼來解決,因為它有效地把特征“從屬”的維度從1變成4,這樣特征“從屬”的每個值將有自己的權重。更新了的決策方程將是f'(w) < K。
這裏,f'(w) = W1*D_0 + W2*D_1 + W3*D_2 + W4*D_3
所有4個新變量有布爾型值(0或1)。
同樣的事發生在基於距離的方法中,如KNN。沒有編碼,“0”和“1”從屬值之間的距離是1,在“0”和“3+”之間的距離是3,這不是所期望的,因為這兩個距離應該類似。在編碼後,值將有新特征(列序列是0,1,2,3+):[1,0,0,0]和[0,0,0,1](最初我們找到的在“0”和“3+”之間的距離),現在這個距離將會是√2。
對於基於樹的方法,同樣的情況(在一個特征中有2個以上的值)可能在一定程度上影響輸出,但是如果像隨機森林的方法,若有足夠深的深度,無需一位有效編碼就能夠處理類別變量。
現在,讓我們看下不同算法中的一位有效編碼的實現。
讓我們創建一個邏輯回歸模型用於分類,而不使用一位有效編碼。
#我們使用的是縮放後的變量,因為我們看到在上一節中縮放會影響L1或L2的正則化算法
>> X_train_scale=scale(X_train)
>> X_test_scale=scale(X_test)
# 擬合邏輯回歸模型
>> log=LogisticRegression(penalty='l2',C=1)
>> log.fit(X_train_scale,Y_train)
# 檢查模型的精度
>> accuracy_score(Y_test,log.predict(X_test_scale))
Out : 0.73958333333333337
現在,我們對數據進行編碼。
>> from sklearn.preprocessing import OneHotEncoder
>> enc=OneHotEncoder(sparse=False)
>> X_train_1=X_train
>> X_test_1=X_test
>> columns=['Gender', 'Married', 'Dependents', 'Education','Self_Employed',
'Credit_History', 'Property_Area']
>> for col in columns:
#創建一個包含所有可能分類值的詳盡列表
data=X_train[[col]].append(X_test[[col]])
enc.fit(data)
#在訓練數據上擬合一位有效編碼
temp = enc.transform(X_train[[col]])
#用新列名把編了碼的特征改為數據幀
temp=pd.DataFrame(temp,columns=[(col+"_"+str(i)) for i in data[col]
.value_counts().index])
#在並排級聯中,檢索值應該是相同的。
#設置與X_train數據幀類似的索引值
temp=temp.set_index(X_train.index.values)
#把新的一位有效編碼了的變量加入訓練數據幀
X_train_1=pd.concat([X_train_1,temp],axis=1)
#在測試數據上擬合一位有效編碼
temp = enc.transform(X_test[[col]])
#把它變成數據幀並加上列名
temp=pd.DataFrame(temp,columns=[(col+"_"+str(i)) for i in data[col]
.value_counts().index])
#設置合適的級聯索引
temp=temp.set_index(X_test.index.values)
#把新的一位有效編碼了的變量加入到測試數據幀
X_test_1=pd.concat([X_test_1,temp],axis=1)
現在,讓我們在一位有效編碼了的數據上應用邏輯回歸模型
#標準化數據集
>> X_train_scale=scale(X_train_1)
>> X_test_scale=scale(X_test_1)
#擬合邏輯回歸模型
>> log=LogisticRegression(penalty='l2',C=1)
>> log.fit(X_train_scale,Y_train)
#檢查模型的精度
>> accuracy_score(Y_test,log.predict(X_test_scale))
Out : 0.75
到此,我們再次得到最大的精度是0.75,這是我們迄今所能得到的。在這個例子中,邏輯回歸正則(C)參數是1,早前我們用的是C=0.01。
原文發布時間為:2017-01-22
本文來自雲棲社區合作夥伴“大數據文摘”,了解相關信息可以關注“BigDataDigest”微信公眾號
最後更新:2017-05-25 09:02:30