Amateur Hour

简易数据预处理与特征工程

2018-10-14

Intro

使用鸢尾花数据集:

1
2
3
4
5
6
from sklearn.datasets import load_iris

data = load_iris()

features = data.data
labels = data.target

其中features是特征矩阵,labels是真实分类。

1. 预处理(preprocessing)

1.1 预处理的目的

数据预处理的目的在于:使得特征数据在处理后,能让学习模型得到更好的效果。预处理通常会解决原始数据存在的如下问题:

  • 量纲不同:比如可能存在两个特征,都是“长度”,但其中一个以千米为单位,一个以毫米为单位。直接让模型去学习这两个特征,明显是不科学的。
  • 信息冗余:经常会有好几个特征表达同一个含义;或者某个特征过于具体或连续,但其实我们只需要一个区段标记或者“是”与“否”的标记。
  • 定性(Categorical)的特征:比如国籍、XX状态等,需要处理成数值编码才可以被学习。
  • 缺失值。

下面具体地介绍几种预处理的手段:

1.2 无量纲化

1.2.1 标准化(standardization)

常用的z-score标准化计算公式:

$$x’ = \frac{x - \bar{X}}{S} $$

在sklearn库中,实现标准化的类是:StandardScaler。

1
2
3
from sklearn.preprocessing import StandardScaler

StandardScaler().fit_transform(features)

可以对比下转换前和转换后,第一行数据的形态:

1
2
3
4
5
# 转换前
[5.1, 3.5, 1.4, 0.2]

# 转换后
[-9.00681170e-01, 1.03205722e+00, -1.34127240e+00, -1.31297673e+00]

1.2.2 最大-最小值缩放(min-max scaler)

其实区间缩放的方式不止最大-最小值这一种,但这是最常用的一种,也是归一化方式的一种。公式如下:

$$x’ = \frac{x - Min}{Max - Min}$$

sklearn中实现的类为MinMaxScaler:

1
2
3
from sklearn.preprocessing import MinMaxScaler

MinMaxScaler().fit_transform(features)

转换后特征变成 [0, 1] 区间内的数值:

1
2
# 转换后
[0.22222222, 0.625, 0.06779661, 0.04166667]

1.2.2 L2归一化(L2 Normalization)

其实上面的最大-最小值缩放也是归一化的一种,但通常说归一化,指的还是L2的归一化方式。公式如下:

$$x’ = \frac{x}{\sqrt{\sum{x[j]^2}}}$$

特征转换后得到的也是 [0, 1] 区间内的数值。

代码如下:

1
2
3
from sklearn.preprocessing import Normalizer

Normalizer().fit_transform(features)
1
2
# 转换后
[0.80377277, 0.55160877, 0.22064351, 0.0315205 ]

标准化与归一化的区别在于:标准化是对一个特征的所有值求z-score,它并不会改变整个特征的分布。而归一化是对一个数据点做处理,得到的是一个更好的新的分布。

1.3 哑编码/独热编码

定性(categorical)的特征无法直接被学习器学习,必需先转换成数值型的编码。哑编码是最常用的方式,在sklearn中有OneHotEncoder类:

1
2
3
from sklearn.preprocessing import OneHotEncoder

OneHotEncoder().fit_transform(labels.reshape(-1, 1))

1.4 基于多项式的变换

数据变换,总体来说就是根据一定的规律造指标。这里介绍的是多项式,其实还可以基于指数函数或者log函数。多项式变换的核心在于“度”(degree)的选择,选择正确的度对于运算复杂度和最终预测结果都有很大的影响。

sklearn用于实现变换的类为:PolynomialFeatures。

1
2
3
4
from sklearn.preprocessing import PolynomialFeatures

# 其实可定义参数degree,默认值为2
PolynomialFeatures().fit_transform(features)

2. 特征选择

对特征数据的预处理只是第一步,在实际输入到学习器之前,还需要对特征的必要性进行排查。不必要的特征需要删除。

特征的选择通常从两方面来考虑:

  1. 特征方差取值:方差大的特征更具有选择性。
  2. 特征相关性:与label相关性越强的特征越好。

下面介绍三种常用的特征选择操作。

2.1 过滤(Filter)

2.1.1 方差过滤

单独计算每个特征的方差,设定阈值并只保留大于阈值的特征。在sklearn中是使用feature_selection库的VarianceThreshold类来实现。

1
2
3
4
from sklearn.feature_selection import VarianceThreshold

# threshold就是阈值
VarianceThreshold(threshold=3).fit_transform(features)

2.1.2 相关系数过滤

计算各个特征对目标值的相关系数以及相关系数的P值。用feature_selection库的SelectKBest类结合相关系数来选择特征的代码如下:

1
2
3
4
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr

SelectKBest(lambda X, Y: array(map(lambda x: pearsonr(x, Y), X.T)).T, k=2).fit_transform(features, labels)

2.1.3 卡方检验过滤

经典的卡方检验是检验定性自变量对定性因变量的相关性。假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距。

用feature_selection库的SelectKBest类结合卡方检验来选择特征的代码如下:

1
2
3
4
5
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

# 选择K个最好的特征,返回选择特征后的数据
SelectKBest(chi2, k=2).fit_transform(features, labels)

2.2 包裹(Wrapper)

2.2.1 递归特征消除

递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。使用feature_selection库的RFE类来选择特征的代码如下:

1
2
3
4
5
6
7
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression

#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(features, labels)

3. 特征降维

当特征选择完成后,可以直接训练模型了,但是可能由于特征矩阵过大,导致计算量大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。常见的降维方法除了以上提到的基于L1惩罚项的模型以外,另外还有主成分分析法(PCA)和线性判别分析(LDA),线性判别分析本身也是一个分类模型。PCA和LDA有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但是PCA和LDA的映射目标不一样:PCA是为了让映射后的样本具有最大的发散性;而LDA是为了让映射后的样本有最好的分类性能。所以说PCA是一种无监督的降维方法,而LDA是一种有监督的降维方法。

3.1 主成分分析(PCA)

1
2
3
4
5
from sklearn.decomposition import PCA

# 主成分分析法,返回降维后的数据
# 参数n_components为主成分数目
PCA(n_components=2).fit_transform(features)

3.2 线性判别(LDA)

1
2
3
4
5
from sklearn.lda import LDA

# 线性判别分析法,返回降维后的数据
# 参数n_components为降维后的维数
LDA(n_components=2).fit_transform(features, labels)