scikit-learn與數據預處理

 

1 引言

預處理操做是機器學習整個週期中必不可少的一個過程,也是最能快速改善模型性能的一個過程,每每稍微轉換一下特徵屬性的形態,就能獲得性能的極大提高。固然,數據預處理絕對也是耗時最長的一個過程,這一過程不只要求洞悉整個數據集結構分佈,還要探查每個特徵屬性細節狀況,並做出應對處理,使數據以最適合的狀態傳輸給模型。
針對預處理操做,sklearn中提供了許多模塊工具,靈活使用工具可讓數據預處理輕鬆不少。
本文簡要介紹數據預處理中的一些主要方法,並結合sklearn中提供的模塊進行實踐。javascript

2 無量綱化

對於大部分機器學習任務而言,對原始數據進行無量綱化是是建模前的必不可少的一個環節。經過無量綱化,能夠消除量綱不一致對模型形成的不良影響。標準化和歸一化是最爲常見的兩種無量綱化方法,下面分別展開介紹這兩種方法。css

2.1 標準化

標準化對數據的分佈的進行轉換,使其符合某種分佈(通常指正態分佈)的一種特徵變換。通常而言,標準化都是指經過z-score的方法將數據轉換爲服從均值爲0,標準差爲1的標準正態分佈數據,經過以下公式進行轉換: $$x' = \frac{{x - \mu }}{\sigma }$$ 式中,$\mu$和$\sigma$是指$x$所在特徵屬性集的均值和標準差。html

(1)sklearn.preprocessing.scale方法實現標準化html5

In [1]:
from sklearn import preprocessing
import numpy as np
In [2]:
X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])
X_scaled = preprocessing.scale(X_train)
 

再次查看X_train各列,咱們會發現,均值和方差都已經標準化:java

In [4]:
X_train.mean(axis=0)
Out[4]:
array([1.        , 0.        , 0.33333333])
In [5]:
X_scaled.std(axis=0)
Out[5]:
array([1., 1., 1.])
 

(2)sklearn.preprocessing.StandardScaler類實現歸一化node

除了scale方法外,在sklearn.preprocessing模塊中還提供有一個專門的類用於實現標準化:StandardScaler,StandardScaler類會自動計算實例化類時傳入的訓練集的均值、標準差,並將這些信息保留,這也就意味着,對訓練集的標準化方式能夠複用,例如對測試集和預測樣本進行一樣的標準化。因此,通常來講,更加建議使用StandardScaler類來實現標準化。python

In [9]:
# 傳入一個訓練集,實例化StandarScaler類
scaler = preprocessing.StandardScaler()
In [34]:
scaler.fit(X_train)  # 收集標準化信息,均值,標準差
Out[34]:
StandardScaler(copy=True, with_mean=True, with_std=True)
In [35]:
scaler.mean_   # 查看均值
Out[35]:
array([1.        , 0.        , 0.33333333])
In [36]:
scaler.scale_  # 查看標準差
Out[36]:
array([0.81649658, 0.81649658, 1.24721913])
 

建立StandarScaler類實例後,須要經過類中的transform方法對X-train進行標準化:jquery

In [16]:
scaler.transform(X_train)
Out[16]:
array([[ 0.        , -1.22474487,  1.33630621],
       [ 1.22474487,  0.        , -0.26726124],
       [-1.22474487,  1.22474487, -1.06904497]])
 

StandardScaler類中還提供有一個fit_transform方法,這個方法合併了fit和transform兩個方法的功能,同時根據傳入的數據集收集標準化信息,並將標準化方案應用於傳入的訓練集:linux

In [26]:
scaler = preprocessing.StandardScaler()
In [29]:
x_train = scaler.fit_transform(X_train)
In [30]:
x_train
Out[30]:
array([[ 0.        , -1.22474487,  1.33630621],
       [ 1.22474487,  0.        , -0.26726124],
       [-1.22474487,  1.22474487, -1.06904497]])
 

假設如今有一個測試樣本,那麼,也能夠經過transform方法將標準化方案應用於測試樣本上:android

In [17]:
X_test = [[-1., 1., 0.]]
scaler.transform(X_test)
Out[17]:
array([[-2.44948974,  1.22474487, -0.26726124]])
 

2.2 歸一化

歸一化是指對數據的數值範圍進行特定縮放,但不改變其數據分佈的一種線性特徵變換。大多數場景下,歸一化都是將數據縮放到[0,1]區間範圍內,計算公式以下: $$x' = \frac{{x - \min }}{{\max - \min }}$$ 式中,$min$和$max$是$x$所屬特徵集合的最小值和最大值。可見,這種歸一化方式的最終結果只受極值的影響。

(1)sklearn.preprocessing.minmax_scale方法實現歸一化。

In [46]:
X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])
X_train = preprocessing.minmax_scale(X_train)
X_train
Out[46]:
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])
 

(2)sklearn.preprocessing.MinMaxScaler類實現歸一化。

In [37]:
X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])

min_max_scaler = preprocessing.MinMaxScaler()
X_train_minmax = min_max_scaler.fit_transform(X_train)
X_train_minmax
Out[37]:
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])
 

使用訓練好的min_max_scaler對新的測試樣本進行歸一化:

In [38]:
X_test = np.array([[-3., -1.,  4.]])
X_test_minmax = min_max_scaler.transform(X_test)
X_test_minmax
Out[38]:
array([[-1.5       ,  0.        ,  1.66666667]])
 

咱們知道,歸一化是將特徵屬性值縮放到[0,1]範圍,但在某些特殊的場景下,咱們須要將特徵屬性縮放到其餘範圍,MinMaxScaler類經過feature_range參數也提供了這一功能,feature_range參數接受一個元組做爲參數,默認值爲(0,1)。

In [40]:
X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])

min_max_scaler = preprocessing.MinMaxScaler(feature_range=(10,20))
X_train_minmax = min_max_scaler.fit_transform(X_train)
X_train_minmax
Out[40]:
array([[15.        , 10.        , 20.        ],
       [20.        , 15.        , 13.33333333],
       [10.        , 20.        , 10.        ]])
 

(3)sklearn.preprocessing.MaxAbsScaler類實現歸一化

MaxAbsScaler是專門爲稀疏數據作歸一化設計的,經過特徵值除以整個特徵集合最大絕對值實現,最終將數據投影到[-1, 1]範圍內,對原來取值爲0的數據並不會作出變換,因此不會影響數據的稀疏性。

 

最後來總結一下標準化和歸一化。
標準化是依照特徵矩陣的列處理數據,其經過求z-score的方法,轉換爲標準正態分佈,和總體樣本分佈相關,每一個樣本點都能對標準化產生影響,而歸一化是將樣本的特徵值轉換到同一量綱下把數據映射到指定區間內,僅由變量的極值決定,因此對異常值較爲敏感。標準化和歸一化都是一種線性變換,都是對向量x按照比例壓縮再進行平移。不管是標準化仍是歸一化,均可以將數據無量綱化,消除不一樣量綱對結果的影響,同時均可以加過模型的收斂速度。 標準化與歸一化之間如何選擇呢?
大多數機器學習算法中,會選擇StandardScaler來進行特徵縮放,由於MinMaxScaler對異常值很是敏感。在PCA,聚類,邏輯迴歸,支持向量機,神經網絡這些算法中,StandardScaler每每是最好的選擇。
MinMaxScaler在不涉及距離度量、梯度、協方差計算以及數據須要被壓縮到特定區間時使用普遍,好比數字圖像處理中量化像素強度時,都會使用MinMaxScaler將數據壓縮於[0,1]區間之中。如果歸一化時須要保留數據的稀疏性,則可使用MaxAbscaler歸一化。 在大多數狀況下,建議先試試看StandardScaler,效果很差換MinMaxScaler。

另外,這裏再提一下正則化(Normalization),不少資料把正則化與歸一化、標準化放到一塊兒討論,雖然正則化也是數據預處理方法的一種,但我並不認爲正則化是無量綱化方法。正則化經過某個特徵值除以整個樣本全部特徵值的範數計算,使得整個樣本範數爲1,一般在文本分類和聚類中使用較多。sklearn中提供preprocessing.normalize方法和preprocessing.Normalizer類實現:

In [49]:
X_train = np.array([[ 1., -2.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])

max_abs_scaler = preprocessing.MaxAbsScaler()
X_train_max_abs = max_abs_scaler.fit_transform(X_train)
X_train_max_abs
Out[49]:
array([[ 0.5, -1. ,  1. ],
       [ 1. ,  0. ,  0. ],
       [ 0. ,  0.5, -0.5]])
In [53]:
X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])

nor = preprocessing.Normalizer()
X_train_nor = nor.fit_transform(X_train)
X_train_nor
Out[53]:
array([[ 0.40824829, -0.40824829,  0.81649658],
       [ 1.        ,  0.        ,  0.        ],
       [ 0.        ,  0.70710678, -0.70710678]])
 

3 缺失值處理

因爲各類各樣的緣由,咱們所面對的數據常常是有所缺失的,然而sklearn中實現的各個算法都假設數據沒有缺失爲前提,若是直接用缺失數據跑算法影響最終結果不說,也容易產生各類異常,因此在數據預處理階段,對缺失值進行處理是頗有必要的。對於缺失值處理,直接刪除包含缺失值的特徵屬性或者樣本是最簡單的方法,可是這種方法卻也將其餘部分信息拋棄,在不少狀況下,特別是數據樣本很少、數據價值大時,未省得不償失。在sklearn中,提供了諸多其餘處理缺失值的方案,例如以均值、中位數、衆數亦或者是指定值填充缺失值等,這些方案都在sklearn.impute模塊中提供的SimpleImputer類中實現,SimpleImputer類參數以下: image

In [99]:
import numpy as np
from sklearn.impute import SimpleImputer
imp = SimpleImputer(missing_values=np.nan, strategy='mean')  # 指定缺失值爲nan,以均值填充
imp.fit([[1, 2], [np.nan, 3], [7, 6]])
X = [[np.nan, 2], [6, np.nan], [7, 6]]
imp.transform(X)
Out[99]:
array([[4.        , 2.        ],
       [6.        , 3.66666667],
       [7.        , 6.        ]])
In [107]:
imp = SimpleImputer(missing_values=0, strategy='constant', fill_value=1)  # 指定缺失值爲0,指定以常數1填充
imp.fit([[5, 2], [4, 0], [7, 6]])
X = [[0, 2], [6, 0], [7, 6]]
imp.transform(X)
Out[107]:
array([[1, 2],
       [6, 1],
       [7, 6]])
 

4 離散型特徵屬性處理

不少時候,咱們所要處理的特徵屬性未必是連續型的,也多是離散型,以衣服爲例,款式(男款、女款),大小(X、XL、XXL),顏色(綠色、紅色、白色),都是離散型特徵屬性。對於這類離散型特徵屬性,須要編碼以後才能用來建模。離散型特徵屬性值能夠分爲兩種:

(1)數字編碼
整數編碼是指對離散型屬性以整數來標識,例如色澤這一特徵中,以整數「0」標識「男款」,整數「1」標識「女款」。sklearn中提供了LabelEncoder和OrdinalEncoder兩個類用以實現對數據的不一樣取值以數字標識。LabelEncoder和OrdinalEncoder會自動根據提供的訓練數據進行統計,分別對每一個特徵屬性從0開始編碼,不一樣的是,LabelEncoder類一次只能對一個一維數組(一個特徵屬性)編碼,而OrdinalEncoder能同時對各個特徵屬性編碼:

In [89]:
enc = preprocessing.LabelEncoder()  # 只能接受一個一維數組
X = ['紅色', '白色', '綠色']
enc.fit(X)
X_ = enc.transform(X)
X_
Out[89]:
array([1, 0, 2])
In [83]:
enc = preprocessing.OrdinalEncoder()  # 能夠同時多經過特徵屬性編碼
X = [['女款', 'X', '綠色'], ['女款', 'XL', '紅色'], ['男款', 'XXL', '白色']]
enc.fit(X)
X_ = enc.transform(X)
X_
Out[83]:
array([[0., 0., 2.],
       [0., 1., 1.],
       [1., 2., 0.]])
In [84]:
enc.inverse_transform(X_)  # 可使用inverse_transform逆轉
Out[84]:
array([['女款', 'X', '綠色'],
       ['女款', 'XL', '紅色'],
       ['男款', 'XXL', '白色']], dtype=object)
 

但在不少模型中,使用整數編碼並不合理,特別是在聚類這類須要計算空間距離的算法模型。仔細觀察上面編碼,顏色這一屬性有三種取值(綠色、紅色、白色),分別以(2,1,0)表示,顏色之間是沒有大小意義的,但以三個數字表示後,就賦予了三種屬性值大小上的意義,且在算法計算距離時,綠色(2)到白色(0)的距離比紅色(1)到白色(0)大,這是不合理的。
對於這類取值沒有大小意義的離散型特徵屬性,有一種更加合適的編碼方式:獨熱編碼。

(2)獨熱編碼

獨熱編碼即 One-Hot 編碼,其方法是使用N位狀態寄存器來對N個狀態進行編碼,每一個狀態都由他獨立的寄存器位,而且在任意時候,其中只有一位有效。sklearn中提供了OneHotEncoder類用以實現對數據的獨熱編碼:

In [74]:
enc = preprocessing.OneHotEncoder()
X = [['女款', 'X', '綠色'], ['女款', 'XL', '紅色'], ['男款', 'XXL', '白色']]
enc.fit(X)
enc.transform([['男款', 'XL', '綠色']]).toarray()
Out[74]:
array([[0., 1., 0., 1., 0., 0., 0., 1.]])
 

在上述輸出結果中,特徵屬性有多少種取值通過獨熱編碼後就擴展爲多少個維度,以款式爲例,通過獨熱編碼後,擴展爲兩個維度,第一維中1表示是女款,0表示非女款。
在實例化OneHotEncoder類時,能夠經過categories參數指定各特徵屬性的全部類別,這樣即便存在訓練數據中沒有出現的類別,在後續出現時也能正確編碼:

In [76]:
style = ['女款', '男款']
size = [ 'X','XL','XXL']
color = ['綠色','紅色','白色']
enc = preprocessing.OneHotEncoder(categories=[style, size, color])
X = [['女款', 'X', '綠色'], ['女款', 'XL', '紅色']]
enc.fit(X)
enc.transform(X).toarray()
Out[76]:
array([[1., 0., 1., 0., 0., 1., 0., 0.],
       [1., 0., 0., 1., 0., 0., 1., 0.]])
In [78]:
enc.transform([['男款', 'XXL', '白色']]).toarray()  # 男款,XXL,白色三個屬性值均爲在X中出現,可是能夠正確編碼
Out[78]:
array([[0., 1., 0., 0., 1., 0., 0., 1.]])
 

建立好OneHotEncoder類實例並經過訓練數據後,就能夠對後續的數據進行獨熱編碼,可是,有時候卻不可避免地出現categories和訓練數據集中都未出現過的取值,這時候繼續編碼就會拋出異常。爲了防止這一狀況發生,咱們能夠在建立OneHotEncoder實例時,傳入參數handle_unknown='ignore',這樣的話,若是出現某一特徵屬性值未在categories和訓練數據集中出現過,經過熱獨編碼時,該特徵屬性多對應的維度都會以0來填充。

In [82]:
style = ['女款', '男款']
size = [ 'X','XL']
color = ['綠色','紅色']
enc = preprocessing.OneHotEncoder(categories=[style, size, color],handle_unknown='ignore')
X = [['女款', 'X', '綠色'], ['女款', 'XL', '紅色']]
enc.fit(X)
enc.transform([['男款', 'XXL', '白色']]).toarray()  # XXL, 白色在categories和X中都爲出現過
Out[82]:
array([[0., 1., 0., 0., 0., 0.]])
 

獨熱編碼解決了離散型屬性難以有效刻畫的問,在必定程度上也起到了擴充特徵的做用,它的值只有0和1,不一樣的類型存儲在垂直的空間。當類別的數量不少時,特徵空間會變得很是大。在這種狀況下,通常能夠用PCA來減小維度。並且one hot encoding+PCA這種組合在實際中也很是有用。

5 連續型特徵屬性離散化

有時候,將連續型特徵屬性離散化可以顯著提升模型的表現力。連續型特徵屬性離散化包括二值化和分段等方法。

(1)二值化

二值化是指經過一個閾值對屬性值進行劃分,當小於這個閾值時,將值映射爲0,大於閾值時映射爲1。二值化是對文本計數數據的常見操做,分析人員能夠決定僅考慮某種現象的存在與否。它還能夠用做考慮布爾隨機變量的估計器的預處理步驟(例如,使用貝葉斯設置中的伯努利分佈建模)。
sklearn中提供了Binarizer實現二值化,默認閾值爲0,也就是將非正數映射爲0,將正數映射爲1。也能夠在實例化時經過參數threshold,設置其餘閾值。

In [90]:
X = [[ 1., -1.,  2.],
     [ 2.,  -4.,  0.],
     [ 3.,  2., -1.]]
binarizer = preprocessing.Binarizer().fit(X)
binarizer
Out[90]:
Binarizer(copy=True, threshold=0.0)
In [91]:
binarizer.transform(X)
Out[91]:
array([[1., 0., 1.],
       [1., 0., 0.],
       [1., 1., 0.]])
In [92]:
binarizer = preprocessing.Binarizer(threshold=1.5).fit(X)
binarizer.transform(X)
Out[92]:
array([[0., 0., 1.],
       [1., 0., 0.],
       [1., 1., 0.]])
 

(2)分段

二值化只能將數據映射爲兩個值,分段能夠對數據進行排序後分爲多個部分而後進行編碼。在sklearn中,分段操做經過KBinsDiscretizer類進行。KBinsDiscretizer類有三個重要參數,必須瞭解一下:

image

In [97]:
X = [[-2, 1, -4,   -1],
     [-1, 2, -3, -0.5],
     [ 0, 3, -2,  0.5],
     [ 1, 4, -1,    2]]
est = preprocessing.KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform').fit(X)
est.transform(X)
Out[97]:
array([[0., 0., 0., 0.],
       [1., 1., 1., 0.],
       [2., 2., 2., 1.],
       [2., 2., 2., 2.]])
相關文章
相關標籤/搜索