Python机器学习:使用scikit-learn进行数据预处理

数据预处理

sklearn.preprocessing包提供了几个数据预处理中常用的功能和变换器,用于将原始特征向量更改为更适合进行机器学习模型的形式。一般来说,数据的标准化使得机器学习算法更加显著,如果数据集中存在一些离散值,显然对数据进行稳定的缩放或转换显然是很有必要的。

标准化

数据集的标准化对scikit-learn中实现的大多数机器学习算法来说是一个必备的前提,如果个别特征或多或少看起来与标准正态分布(具有零均值和单位方差)不是十分类似,那么这些数据的在算号发中的表现会显得差强人意。在实际应用中,我们经常忽略特征的分布情况,直接使用均值对某个特征进行中心化,再通过除以非常量特征的标准差进行缩放。

sklearn.preprocessing中使用函数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.])

sklearn.preprocessing模块还提供了一个实用类StandarScaler,它实现了转化器的API来计算训练集上的平均值和标准差,以便以后能够在测试集上重新应用相同的变换。

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 = [[-1.,1.,0.]]
scaler.transform(x_test)
array([[-2.44948974,  1.22474487, -0.26726124]])

也可以通过在构造函数:class:StandarScaler中传入参数with_mean=False或者with_std=False来取消中心化或缩放操作。

将特征缩放至特定范围内

一种标准化的实现是将特征缩放到给定的最小值和最大值之间(通常在之间),或者也可以将每个特征的最大绝对值转换为单位大小,分别使用MinMaxScalerMaxAbsScaler实现。进行此类缩放的目的是实现特征极小方差的鲁棒性以及在稀疏矩阵中保留零元素。(鲁棒性:表征控制系统对特性或参数扰动的不敏感性)

min_max_scaler = preprocessing.MinMaxScaler()
x_min_max = min_max_scaler.fit_transform(x)
x_min_max
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])

同样的转换可以被用在在训练过程中不可见的测试数据,实现和训练数据一致的缩放和移位操作

x_t = np.array([[-3.,-1.,4.]])
x_t_min_max = min_max_scaler.transform(x_t)
print(x_t_min_max)
[[-1.5         0.          1.66666667]]

通过检查缩放器(scaler)的属性,来观察在训练集中学习到的转换操作的基本性质:

min_max_scaler.scale_
array([0.5       , 0.5       , 0.33333333])
min_max_scaler.min_
array([0.        , 0.5       , 0.33333333])

MaxAbsScaler的工作原理与MinMAXScaler非常相似,但是他只是通过除以每个特征的最大值将训练数据特征缩放至之间,这意味着,训练数据应该是已经是零中心化或者是稀疏数据。

max_abs_scaler = preprocessing.MaxAbsScaler()
x_maxabs = max_abs_scaler.fit_transform(x)
x_maxabs
array([[ 0.5, -1. ,  1. ],
       [ 1. ,  0. ,  0. ],
       [ 0. ,  1. , -0.5]])
x_test_maxabs = max_abs_scaler.transform(x_t)
x_test_maxabs
array([[-1.5, -1. ,  2. ]])
max_abs_scaler.scale_
array([2., 1., 2.])

缩放稀疏矩阵

中心化稀疏矩阵会破坏数据的稀疏结构,因此一般不会使用。但是缩放稀疏矩阵是极具意义的,尤其是当几个特征在不同的量级范围时。MaxAbsScaler以及maxabs_scale是专为缩放数据而设计的,并且也是缩放数据的推荐方法。

缩放有离群值的数据

当数据包含许多异常值时,使用均值和方差缩放可能并不是一个很好的选择。这种情况下,可以使用 robust_scale 以及 RobustScaler进行缩放。它们对你的数据的中心和范围使用更有鲁棒性的估计。

非线性转换

类似于缩放,QuantileTransformer类将每个特征缩放在同样的范围或分布下。通过执行一个秩转换能够使异常的分布平滑化,并且能够比缩放更少的收到离群值的影响。但是,它会使特征间及特征内的关联和距离失真。QuantileTransformer 类以及 quantile_transform 函数提供了一个基于分位数函数的无参数转换,将数据映射到了零到一的均匀分布上

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
x,y = iris.data,iris.target
# 将数据集划分为训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,random_state = 0)
quantile_transformer = preprocessing.QuantileTransformer(random_state=0)
x_train_trans = quantile_transformer.fit_transform(x_train)
x_test_trans = quantile_transformer.transform(x_test)
np.percentile(x_train[:, 0], [0, 25, 50, 75, 100]) 
array([4.3, 5.1, 5.8, 6.5, 7.9])

这个特征是萼片的长度,一旦应用分位数转换,这些元素就接近于之前定义的百分数。

np.percentile(x_train_trans[:,0],[0,25,50,75,100])
array([9.99999998e-08, 2.38738739e-01, 5.09009009e-01, 7.43243243e-01,
       9.99999900e-01])

还可以在具有类似形式的独立测试集上确认:

np.percentile(x_test[:,0],[0,25,50,75,100])
array([4.4  , 5.125, 5.75 , 6.175, 7.3  ])
np.percentile(x_test_trans[:, 0], [0, 25, 50, 75, 100])
array([0.01351351, 0.25012513, 0.47972973, 0.6021021 , 0.94144144])

也可以通过设置output_distribution='normal'将转换后的数据映射到正态分布。

quantile_transformer = preprocessing.QuantileTransformer(output_distribution='normal',random_state=0)
x_trans = quantile_transformer.fit_transform(x)
quantile_transformer.quantiles_
array([[4.3       , 2.        , 1.        , 0.1       ],
       [4.31491491, 2.02982983, 1.01491491, 0.1       ],
       [4.32982983, 2.05965966, 1.02982983, 0.1       ],
       ...,
       [7.84034034, 4.34034034, 6.84034034, 2.5       ],
       [7.87017017, 4.37017017, 6.87017017, 2.5       ],
       [7.9       , 4.4       , 6.9       , 2.5       ]])

归一化

归一化缩放单个样本以具有单位范数的过程,如果使用二次行驶(如点积或任何其他核函数)来量化任何样本的相似度,归一化将显得及其出色。使用normalize对数据集进行归一化处理。Normalization主要思想是对每个样本计算其p-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后样本的p-范数(l1-norm,l2-norm)等于1。范数是泛函分析中定义的一个基本概念,定义在赋范线性空间中,且满足非负性,齐次性,三角不等式。常被用于度量某个向量空间中每个向量的长度和大小。最常用的范数是范数:若,那么我们将形如的距离称为范数。

x_normalize = preprocessing.normalize(x,norm='l1')
x_normalize[:5]
array([[0.5       , 0.34313725, 0.1372549 , 0.01960784],
       [0.51578947, 0.31578947, 0.14736842, 0.02105263],
       [0.5       , 0.34042553, 0.13829787, 0.0212766 ],
       [0.4893617 , 0.32978723, 0.15957447, 0.0212766 ],
       [0.49019608, 0.35294118, 0.1372549 , 0.01960784]])

二值化

特征二值化是将数值特征用阈值过滤得到布尔值的过程。在文字处理中即使归一化计数(术语频率)和TD-IDF值特征在实践中表现稍好一些,处于简化概率估计的考虑,常常会选择使用二值化特征值。

binarizer = preprocessing.Binarizer().fit(x)
binarizer.transform(x)
array([[1., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.]])

也可以为二值化器赋一个阈值

binarizer = preprocessing.Binarizer(threshold=1.1)
binarizer.transform(x)
array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 0., 0.]])

分类特征编码

在机器学习中,特征经常不是数据型而是分类型,虽然可以将这些分类型特征编码成整数,但是这些整数特征并不能在scikit-learn的估计器中使用,因为输入的是整数特征,估计器会认为类别之间是有序的,但实际却是无序的。故一般使用one-of-K或ono-hot编码进行分类特征转换。sklearn使用preprocessing.OneHotEncoder()实现,该函数使用m个可能值转换为m值化特征,将分类特征的每个元素转化为一个值。

enc = preprocessing.OneHotEncoder()
l = [[0,0,3],[1,1,0],[0,2,1],[1,0,2]]
enc.fit(l)
OneHotEncoder(categorical_features='all', dtype=,
       handle_unknown='error', n_values='auto', sparse=True)
# 使用相同模型训练给出的数据集
enc.transform([[0,1,3]]).toarray()
array([[1., 0., 0., 1., 0., 0., 0., 0., 1.]])

默认情况下,每个特征使用几维的数值由数据集自动推断。当然,也可以通过参数n_values来直接指定。

缺失值补插

因为诸多因素,有时候我们得到的数据往往包含缺失,这些缺失值被编码成空格、NANs,或者其它占位符。由于这些数据集不能被scikit-learn算法所兼容,因为大多数的机器学习算法默认数组中的元素都是数值,每个元素都有自己的意义。对于一些没有价值的数据,处理的基本策略是直接舍弃整行或整列包含缺失值的数据。当然,最好的办法还是从已有的数据中推断出缺失的值。scikit-learn使用Imputer类估算缺失值,使用缺失值所在的行、列中的平均值、中位数或者众数来填充,这种处理方式显然是有点粗糙的,如果需要更高的要求可以使用scipyinterpolate中的插值函数进行估计,也可以使用回归分析进行预测。这里不再详细说明,可以参考数据预处理

from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN',strategy='mean',axis=0)
h = [[1,2],[np.nan,3],[7,6]]
imp.fit(h)
Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)
t = [[np.nan,2],[6,np.nan],[7,6]]
imp.transform(t)
array([[4.        , 2.        ],
       [6.        , 3.66666667],
       [7.        , 6.        ]])

Imputer也支持稀疏矩阵

import scipy.sparse as sp
i = sp.csc_matrix([[1,2],[0,3],[7,6]])
imp = Imputer(missing_values=0,strategy='mean',axis=0)
imp.fit(i)
Imputer(axis=0, copy=True, missing_values=0, strategy='mean', verbose=0)
i_test = sp.csc_matrix([[0,2],[6,0],[7,6]])
imp.transform(i_test)
array([[4.        , 2.        ],
       [6.        , 3.66666667],
       [7.        , 6.        ]])

需要注意的是,在第二个例子中缺失值被编码为0,隐式的存储在矩阵中。

生成多项式特征

在机器学习中,通过增加一些输入数据的非线性特征来增加模型的复杂度通常是十分有效的,一个比较简单的实现就是使用多项式特征。

from sklearn.preprocessing import PolynomialFeatures
q = np.arange(6).reshape(3,2)
poly = PolynomialFeatures(2)
print(q)
poly.fit_transform(q)
[[0 1]
 [2 3]
 [4 5]]





array([[ 1.,  0.,  1.,  0.,  0.,  1.],
       [ 1.,  2.,  3.,  4.,  6.,  9.],
       [ 1.,  4.,  5., 16., 20., 25.]])

显然,特征已从转换为

在一些情况下,只需要特征间的交互项,此时可以通过设置interaction_only=True得到:

w = np.arange(9).reshape(3,3)
print(w)
poly = PolynomialFeatures(degree=3,interaction_only=True)
poly.fit_transform(w)
[[0 1 2]
 [3 4 5]
 [6 7 8]]





array([[  1.,   0.,   1.,   2.,   0.,   0.,   2.,   0.],
       [  1.,   3.,   4.,   5.,  12.,  15.,  20.,  60.],
       [  1.,   6.,   7.,   8.,  42.,  48.,  56., 336.]])

特征由转换为

自定义转换器

在机器学习中,如果想要将一个函数转化为一个转换器来协助数据清理或处理,可以使用FunctionTransformer将任意函数实现为转换器。如,构建一个实现日志转换的转换器。

from sklearn.preprocessing import FunctionTransformer
transformer = FunctionTransformer(np.log1p)
s = np.array([[0,1],[2,3]])
transformer.transform(s)
array([[0.        , 0.69314718],
       [1.09861229, 1.38629436]])

本文是作者学习sklearn时的笔记,更多详细内容,请参考scitik-learn。

你可能感兴趣的:(Python机器学习:使用scikit-learn进行数据预处理)