特征工程思維導圖如下圖。 本文借助sklearn介紹其中的預處理部分
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiIXZ05WZD9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVPNhVWqZ1ViBXOxoVdsdEZwZ1RiZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jMwATO1gTNyIjMwMDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
二 單特征預處理
<1> 标準化 Standardization 或者叫 mean removal and variance scaling(平均值移除、方差縮放)
說明1: 标準化其實就是幹兩件事:“transform the data to center” ,即使資料平均值為0;
“scale it by dividing non-constant features by their standard deviation”,即标準差為1
(1) sklearn 提供了scale做資料标準化:
>>> from sklearn import preprocessing
>>> import numpy as np
>>> X = np.array([[1., -1., 2.],
... [2., 0., 0.],
... [0., 1., -1.]])
>>> X_scaled = preprocessing.scale(X)
>>> X_scaled
array([[ 0. , -1.22474487, 1.33630621],
[ 1.22474487, 0. , -0.26726124],
[-1.22474487, 1.22474487, -1.06904497]])
>>> X_scaled.mean(axis=0)
array([ 0., 0., 0.])
>>> X_scaled.std(axis=0)
array([ 1., 1., 1.])
preprocessing 提供了另一個實用的類StandardScaler,它的fit函數解析訓練資料(包括值和矩陣格式),transform則對
資料執行standardization過程。 在訓練資料上使用的StandardScalar對象可以再使用在測試資料上
>>> scaler = preprocessing.StandardScaler().fit(X)
>>> scaler
StandardScaler(copy=True, with_mean=True, with_std=True)
>>> scaler.mean_
array([ 1. , 0. , 0.33333333])
>>> scaler.scale_
array([ 0.81649658, 0.81649658, 1.24721913])
>>> scaler.transform(X)
array([[ 0. , -1.22474487, 1.33630621],
[ 1.22474487, 0. , -0.26726124],
[-1.22474487, 1.22474487, -1.06904497]])
>>> X_test = np.array([[-1., 1., 0.]])
>>> scaler.transform(X_test)
array([[-2.44948974, 1.22474487, -0.26726124]])
StandardScaler的構造函數中可以關閉with_mean和with_std
(2) 縮放至一定範圍
先介紹MinMaxScaler的使用, 使用max - min作為分母
>>> 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
array([[ 0.5 , 0. , 1. ],
[ 1. , 0.5 , 0.33333333],
[ 0. , 1. , 0. ]])
>>> X_test = np.array([[ -3., -1., 4.]])
>>> X_test_minmax = min_max_scaler.transform(X_test)
>>> X_test_minmax
array([[-1.5 , 0. , 1.66666667]])
在介紹MaxAbsScaler, 将最大的絕對值作為分母
>>> max_abs_scaler = preprocessing.MaxAbsScaler()
>>> X_train_maxabs = max_abs_scaler.fit_transform(X_train)
>>> X_train_maxabs
array([[ 0.5, -1. , 1. ],
[ 1. , 0. , 0. ],
[ 0. , 1. , -0.5]])
>>> X_test = np.array([[ -3., -1., 4.]])
>>> X_test_maxabs = max_abs_scaler.transform(X_test)
>>> X_test_maxabs
array([[-1.5, -1. , 2. ]])
>>> max_abs_scaler.scale_
array([ 2., 1., 2.])
如果不想建立scaler對象、不需要後面的test data上使用,可以使用 minmax_scale和maxabs_scale方法
<2> 正則化 Normalization (跟以前了解的正則化概念不同)
正則化的過程是将每個樣本縮放到機關範數。對于後面要通過點積或其他核方法計算兩個樣本之間相似性的情況,
該正則化十分有用。
Normalization主要思想是對每個樣本計算p-範數(例如l1-範數,l2-範數等),然後樣本的每個元素除以該範數。則
執行正則化的樣本的p-範數變為1。 p-範數的公式: ||X||p = (|x1|^p + |x2|^p + .... + |xn|^p)^1/p
該方法主要用于文本分類和聚類,例如兩個tf-idf向量的l2正則化進行點積就得到兩個向量的餘弦相似性
>>> X_normalized
array([[ 0.40824829, -0.40824829, 0.81649658],
[ 1. , 0. , 0. ],
[ 0. , 0.70710678, -0.70710678]])
同樣提供了類:
>>> normalizer = preprocessing.Normalizer().fit(X)
>>> normalizer
Normalizer(copy=True, norm='l2')
>>> normalizer.transform(X)
array([[ 0.40824829, -0.40824829, 0.81649658],
[ 1. , 0. , 0. ],
[ 0. , 0.70710678, -0.70710678]])
>>> normalizer.transform([[-1., 1., 0.]])
array([[-0.70710678, 0.70710678, 0. ]])
<3> Binarization 二值化
特征二值化是指依據一個門檻值,将大于門檻值的指派為1, 小于等于門檻值的指派為0.
>>> X
array([[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]])
>>> binarizer = preprocessing.Binarizer().fit(X)
>>> binarizer
Binarizer(copy=True, threshold=0.0)
>>> binarizer.transform(X)
array([[ 1., 0., 1.],
[ 1., 0., 0.],
[ 0., 1., 0.]])
>>> binarizer2 = preprocessing.Binarizer(threshold=1.1)
>>> binarizer2.transform(X)
array([[ 0., 0., 1.],
[ 1., 0., 0.],
[ 0., 0., 0.]])
<4> 類别型特征編碼
可以使用oneHot encoding 将類别型特征進行編碼。
>>> enc = preprocessing.OneHotEncoder()
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) # 第一個特征有2個類别,第二個特征有3個類别,第三個特征有4類别
OneHotEncoder(categorical_features='all', dtype=<type 'numpy.float64'>,
handle_unknown='error', n_values='auto', sparse=True)
>>> enc.transform([[0, 0, 0]]).toarray()
array([[ 1., 0., 1., 0., 0., 1., 0., 0., 0.]]) # 第一個特征占據前兩位,第二個特征占據厚3個位置,第三個特征占最後4個
>>> enc.transform([[1, 0, 0]]).toarray()
array([[ 0., 1., 1., 0., 0., 1., 0., 0., 0.]])
>>> enc.transform([[0, 1, 0]]).toarray()
array([[ 1., 0., 0., 1., 0., 1., 0., 0., 0.]])
>>> enc.transform([[0, 2, 0]]).toarray()
array([[ 1., 0., 0., 0., 1., 1., 0., 0., 0.]])
>>> enc.transform([[0, 0, 1]]).toarray()
array([[ 1., 0., 1., 0., 0., 0., 1., 0., 0.]])
>>> enc.transform([[0, 0, 2]]).toarray()
array([[ 1., 0., 1., 0., 0., 0., 0., 1., 0.]])
>>> enc.transform([[0, 0, 3]]).toarray()
array([[ 1., 0., 1., 0., 0., 0., 0., 0., 1.]])
如果訓練資料中的類别不全,則可以指定每個特征的類别數量
>>> enc = preprocessing.OneHotEncoder(n_values=[2,3,4]) #指定
>>> enc.fit([[1, 2, 3], [0, 2, 0]])
OneHotEncoder(categorical_features='all', dtype=<type 'numpy.float64'>,
handle_unknown='error', n_values=[2, 3, 4], sparse=True)
>>> enc.transform([[1, 2, 0]]).toarray()
array([[ 0., 1., 0., 0., 1., 1., 0., 0., 0.]])
>>> enc.transform([[1, 0, 0]]).toarray()
array([[ 0., 1., 1., 0., 0., 1., 0., 0., 0.]])
>>> enc.transform([[1, 1, 0]]).toarray()
array([[ 0., 1., 0., 1., 0., 1., 0., 0., 0.]])
<5> 預設值計算 ---------Imputer
>>> from sklearn.preprocessing import Imputer
>>> imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
>>> imp
Imputer(add_indicator_features=False, axis=0, copy=True, missing_values='NaN',
strategy='mean', verbose=0)
>>> imp.fit([[1, 2], [np.nan, 3], [7, 6]]) #第一維的平均值為4, 第二維的平均值為3.667
Imputer(add_indicator_features=False, axis=0, copy=True, missing_values='NaN',
strategy='mean', verbose=0)
>>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
>>> imp.transform(X)
array([[ 4. , 2. ],
[ 6. , 3.66666667],
[ 7. , 6. ]])
<6> 特征的多項式變換
多項式變換會将每個樣本資料擴充為原本元素的多項式項,例如
(X1, X2) 經過二項式變換,變為(1, X1, X2, X1^2, X1X2, X2^2)
>>> X = np.arange(6).reshape(3,2)
>>> X
array([[0, 1],
[2, 3],
[4, 5]])
>>> poly = preprocessing.PolynomialFeatures(2) #指定二項式變化
>>> poly.fit_transform(X)
array([[ 1., 0., 1., 0., 0., 1.],
[ 1., 2., 3., 4., 6., 9.],
[ 1., 4., 5., 16., 20., 25.]])
還可以指定隻保留互動項:
(X1, X2) 經過二項式變換隻保留互動項,變為(1, X1, X2, X1X2)
>>> X
array([[0, 1],
[2, 3],
[4, 5]])
>>> poly2 = preprocessing.PolynomialFeatures(degree=2, interaction_only=True)
>>> poly2.fit_transform(X)
array([[ 1., 0., 1., 0.],
[ 1., 2., 3., 6.],
[ 1., 4., 5., 20.]])
<7> 自定義變換
可是使用FunctionTransformer指定一個子選擇的變換方法
>>> from numpy import log1p # log1p(x) = log(1 + x)
>>> from sklearn.preprocessing import FunctionTransformer
>>> trans = FunctionTransformer(log1p)
>>> X = np.array([[0,1], [2, 3]])
>>> trans.transform(X)
array([[ 0. , 0.69314718],
[ 1.09861229, 1.38629436]])
三 特征選擇
<1> Filter 過濾法 ---------多使用于線性的模型
1.1 方差選擇法
删除元素方差小于等于門檻值的特征 例如,假設data中每個特征是二項分布(n次伯努利分布),而伯努利分布的方差(variance)Var = p(1-p)
>>> from sklearn.feature_selection import VarianceThreshold
>>> X = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]
>>> sel = VarianceThreshold(threshold=(.8 * (1 - .8))) #
>>> sel.fit_transform(X)
array([[0, 1],
[1, 0],
[0, 0],
[1, 1],
[1, 0],
[1, 1]])
1.2 Univariate feature selection------ 單變量特征選擇
1.2.1 SelectKBest ------保留k個最好的特征。 打分方法,例如卡方驗證
1.2.2 SelectPercentile------- 保留一定比例
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectKBest
>>> from sklearn.feature_selection import chi2
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
>>> X_new.shape
(150, 2)
<2> Wrapper --- 包裹法
把特征選擇看做一個特征子集搜尋問題,篩選各種特征子集,用模型評估效果。 介紹RFE--- recursive feature elemination, 遞歸消除特征法, 每輪訓練,消除權值小的一定比例的特征,在基于新特征訓練,如果滿足 期望要求,繼續消除,以此遞歸,直到模型效果低于預期值。以LR為例,過程如下: 1. 使用全特征跑一個模型 2.根據線性模型的系數(展現相關性),删掉5%-10%的弱特征,觀察準确率/auc變化 3. 逐漸進行,知道準确率/auc出現大的下滑停止 class
sklearn.feature_selection.
RFE
(estimator, n_features_to_select=None, step=1, verbose=0) n_featuers_to_select ------保留的特征個數, 不指定則保留一半 step-----每次要消除的特征。 如果>1, 表示每次删除的個數; 如果[0,1]之間,表示每次删除的比例
>>> from sklearn.feature_selection import RFE
>>> from sklearn.linear_model import LogisticRegression
>>> rfe = RFE(LogisticRegression(), n_features_to_select=1, step=1)
>>> rfe.fit(X, y)
<3> Embedded -----嵌入法, SelectFromModel
class
sklearn.feature_selection.
SelectFromModel
(estimator, threshold=None, prefit=False)
estimator ----- 用來選擇特征的模型 threshold ----- 門檻值。 保留大于門檻值的特征。如果沒有指定,且estimator有penalty=l1或者 estimator為Lasso, 預設門檻值為1e-1, 否則預設值為平均值 prefit --------是否需要先fit的模型
(1) 基于L1正則化的特征選擇方法
先了解L1正則化:http://blog.csdn.net/leiting_imecas/article/details/56351806 L1正則化可以用在維數很大時做特征選擇. SelectFromModel其實就是先用一個模型(通常選擇線性模型)跑一下,把特征選擇出來
>>> from sklearn.svm import LinearSVC
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X,y)
>>> model = SelectFromModel(lsvc, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 3)
sklearn說明文檔上說,選擇特征的模型的選擇問題: linear_model.Lasso --------用于regression; linear_model.LogisticRegression 和svm.LinearSVC ----------用于classification
(2)基于樹的特征選擇 基于樹的模型可以用來計算特征的重要性,這樣也可以用來去除關聯性小的特征
>>> from sklearn.ensemble import ExtraTreesClassifier
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> clf = ExtraTreesClassifier()
>>> clf = clf.fit(X,y)
>>> clf.feature_importances_
array([ 0.05517225, 0.06978394, 0.52250383, 0.35253998]) #特征重要性
>>> model = SelectFromModel(clf, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 2)
[source]