sklearn学习 数据预处理-数据集的转换

数据预处理

sklearn.preprocessing 包提供了一些通用的工具或者transformer类把原始的特征向量转换成更适合于模型进行训练的形式。
通常来说,一些机器学习算法需要对数据进行标准化。如果数据集中出现了一些奇异点,你最好使用更加健壮的scalers或者transformers。这些不同的scalers, transformers, and normalizers在包含奇异点的数据集上的效果,在这里有所展示。

标准化、均值去除和方差缩放

对数据集做标准化(Standardization)操作是很多sklearn库实现的机器学习算法的常见的要求。如果这些特征看起来不太像正态分布(具有零均值和单位方差的高斯分布)的数据的话,这些算法可能效果不佳。
实际上,我们经常忽视数据的分布情况,直接数据转换到向量空间的中央,通过对每个特征减去它的均值实现,然后呢,把特征通过标准差进行缩放。
举个例子,在一些机器学习算法中,比如说支持向量机的RBF核或者线性模型的L1和L2正则化,都是假设所有的特征都是以0为中心分布的,并且取值区间范围应当是相同的。如果一个特征的取值区间比别的特征大,它可能在目标函数中占主导地位,使得学习器(estimator)不能像期望的那样从别的特征中进行学习。

最方便快捷地实现这个操作的方法是调用 scale 函数。

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

>>> X_scaled                                          
array([[ 0.  ..., -1.22...,  1.33...],
       [ 1.22...,  0.  ..., -0.26...],
       [-1.22...,  1.22..., -1.06...]])

scale 操作后的函数都是零均值、单位方差的:

>>> X_scaled.mean(axis=0)
array([ 0.,  0.,  0.])

>>> X_scaled.std(axis=0)
array([ 1.,  1.,  1.])

preprocessing 模块实现了一个工具类叫做 StandardScaler,它可以计算出训练集中特征的均值和方差并且在这之后,在测试集上进行同样的转换。

>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> scaler
StandardScaler(copy=True, with_mean=True, with_std=True)

>>> scaler.mean_                                      
array([ 1. ...,  0. ...,  0.33...])

>>> scaler.scale_                                       
array([ 0.81...,  0.81...,  1.24...])

>>> scaler.transform(X_train)                           
array([[ 0.  ..., -1.22...,  1.33...],
       [ 1.22...,  0.  ..., -0.26...],
       [-1.22...,  1.22..., -1.06...]])

在这之后能,我们可以在测试集上做同样的操作:

>>> X_test = [[-1., 1., 0.]]
>>> scaler.transform(X_test)                
array([[-2.44...,  1.22..., -0.26...]])

把特征缩放到某个范围

标准化的一个可选的操作是把特征缩放到给定最大值和最小值的区间范围,通常是取01区间或者是为了把每个特征的最大绝对值按比例缩放为单位大小。可以通过使用 MinMaxScaler 或 MaxAbsScaler 实现。
这么做的目的是为了给那些标准差非常小的特征增加健壮性,另外也可以使得矩阵中产生更多的零,从而使矩阵更加稀疏,占用更少的存储空间。
下面是一个把矩阵转换到01范围的例子:

>>> 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]])

当然,也可以通过查看scaler对应的属性来了解对训练集做的转换操作的本质:

>>> min_max_scaler.scale_                             
array([ 0.5       ,  0.5       ,  0.33...])

>>> min_max_scaler.min_                               
array([ 0.        ,  0.5       ,  0.33...])

如果向 MinMaxScaler 直接传入了参数 feature_range=(min, max),那么完整的表达式就是下面这样子的:

X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))

X_scaled = X_std * (max - min) + min

MaxAbsScaler 的使用方式类似,但是它是把训练数据缩放到 -1到1 的范围区间,具体的做法是除以每个特征的最大值。一般是作用于以零分分布中心,或者稀疏的数据。

>>> X_train = np.array([[ 1., -1.,  2.],
...                     [ 2.,  0.,  0.],
...                     [ 0.,  1., -1.]])
...
>>> max_abs_scaler = preprocessing.MaxAbsScaler()
>>> X_train_maxabs = max_abs_scaler.fit_transform(X_train)
>>> X_train_maxabs                # doctest +NORMALIZE_WHITESPACE^
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.])

如果你不想直接写类似的功能,你可以用这个模块的 scale,minmax_scale 和 maxabs_scale。

放缩稀疏的数据

对稀疏的数据做居中处理,可能会破坏掉数据本身稀疏的结构,这通常是不明智的做法。然而,这么处理稀疏的输入是有意义的,特别是特征分布范围各不相同的时候。
minmax_scale 和 maxabs_scale就是为了放缩稀疏数据特别设计的,一般在这种场合下,也推荐你们这么做。然而,如果想要 scale 和 StandardScaler 接受 scipy.sparse 矩阵作为输入,必须要直接设置 with_mean=False。不然在运行的时候会报错 ValueError。RobustScaler 不能 fit 稀疏矩阵, 可以 transform 稀疏矩阵。
注意:scalers 接受 Compressed Sparse Rows 和 Compressed Sparse Columns 格式(参阅scipy.sparse.csr_matrix 和 scipy.sparse.csc_matrix)。任何其他的稀疏输入会被转换成 Compressed Sparse Rows representation。为了避免不必要的内存复制,建议选择CSR 或 CSC representation upstream。
最后,如果居中(centered)的数据足够小的话,直接用sparse matrices的toarray方法把输入转换成array也是一个选择。

放缩包含奇异点的数据

如果数据包含了很多奇异点的话,用数据的均值和方差做放缩的操作可能不太好。在这样的情况下,你可以用 robust_scale 和 RobustScaler。他们使用更加可靠的estimates来做数据居中和放缩。

对核矩阵(Centering kernel matrices)做居中处理

如果你有一个核矩阵,这个核是通过一个叫phi的函数实现的,它主要用来计算特征空间的点积。那么你可以使用 KernelCenterer ,它可以把核矩阵转换成特征空间的点积,然后减去均值,实现数据的居中(centered)。

非线性变换

和scaler类似, QuantileTransformer 也可以把每个特征转换到同一个范围或者是分布。然而,它执行的转换(rank transformation),可以平滑掉非正常的分布,所以受到奇异点的影响比scaler要少。但是,这样做的话,一般会影响特征内或者是特征间的相关性和距离。
QuantileTransformer 和 quantile_transform 提供基于 quantile 函数的非参数转换,把数据映射到[0,1]区间范围内的均匀分布:

>>> 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])

这个特征对应的是用cm表示的长度特征。进行了quantile的转换操作之后:

>>> np.percentile(X_train_trans[:, 0], [0, 25, 50, 75, 100])
... 
array([ 0.00... ,  0.24...,  0.49...,  0.73...,  0.99... ])

在训练集上,进行同样的转换:

>>> 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.01...,  0.25...,  0.46...,  0.60... ,  0.94...])

当然,你也可以把数据映射到标准正态分布,只要设置参数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.31...,  2.02...,  1.01...,  0.1...],
       [ 4.32...,  2.05...,  1.02...,  0.1...],
       ...,
       [ 7.84...,  4.34...,  6.84...,  2.5...],
       [ 7.87...,  4.37...,  6.87...,  2.5...],
       [ 7.9...,   4.4...,   6.9...,   2.5...]])

输入数据的中位数就变成了输出数据的均值,0为分布的中心。因此输出被调整了,输入最大值和最小值得时候,它们分别对应1e-7和1-1e-7分位数,不会出现无穷大的情况。

正则化

正则化是把样本点的特征放缩到01范围的过程。这个操作在你打算做二次型的时候特别有用,比如说在做点积或者是用其他的kernel去衡量任意两个样本点的相似程度的时候。
这个假设是基于向量空间模型(Vector Space Model ),而向量空间模型在文本分类和内容聚类里面经常被用到。
normalize 函数可以方便快捷的实现正则化的操作,你可以选择’L1’或’L2’正则化。

>>> X = [[ 1., -1.,  2.],
...      [ 2.,  0.,  0.],
...      [ 0.,  1., -1.]]
>>> X_normalized = preprocessing.normalize(X, norm='l2')

>>> X_normalized                                      
array([[ 0.40..., -0.40...,  0.81...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 0.  ...,  0.70..., -0.70...]])

当然你也可以用 Normalizer ,它实现了transformer的api借口。尽管fit方法在这里并没有什么卵用。

>>> normalizer = preprocessing.Normalizer().fit(X)  # fit does nothing
>>> normalizer
Normalizer(copy=True, norm='l2')

fit之后你就可以对任何样本点的向量进行这种转换了:

>>> normalizer.transform(X)                            
array([[ 0.40..., -0.40...,  0.81...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 0.  ...,  0.70..., -0.70...]])

>>> normalizer.transform([[-1.,  1., 0.]])             
array([[-0.70...,  0.70...,  0.  ...]])

二值化

特征的二值化

特征的二值化是指 通过阈值处理数值特征,从而把它转换成布尔值。这种操作对于对于一些概率估计的estimators,这些estimators假设输入的数据遵从 多变量的伯努利分布( multi-variate Bernoulli distribution)。具体的例子可以看这里
在文本处理里面,这种二值化很常见。当然,实际上tf-idf模型的效果会更好。
和 Normalizer 的用法类似,fit方法实际上也没什么卵用。

>>> X = [[ 1., -1.,  2.],
...      [ 2.,  0.,  0.],
...      [ 0.,  1., -1.]]

>>> binarizer = preprocessing.Binarizer().fit(X)  # fit does nothing
>>> binarizer
Binarizer(copy=True, threshold=0.0)

>>> 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.]])

对类别特征进行编码

一些特征它并不是一个连续的值,用来表示类别。举个例子,一个人可以有这些特征 [“male”, “female”], [“from Europe”, “from US”, “from Asia”], [“uses Firefox”, “uses Chrome”, “uses Safari”, “uses Internet Explorer”]。这些特征可以用整数值代替,例如,[“male”, “from US”, “uses Internet Explorer”]可以表示成 [0, 1, 3],[“female”, “from Asia”, “uses Chrome”] 表示成 [1, 2, 1]。
这种整数表示的方法并不能直接用在scikit-learn的estimators,这些 estimators 把它看做连续的值,认为这些类别之间是有顺序的,这通常不是我们想要的。
一个可行的办法是对这些类别特征进行onehot编码。这个时候,可以用OneHotEncoder。它把某个类别特征的m个取值,映射到一个m维的01向量中,当然对应每个取值,向量中只会出现一个1。
继续使用上面这个例子:

>>> enc = preprocessing.OneHotEncoder()
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])  
OneHotEncoder(categorical_features='all', dtype=<... 'numpy.float64'>,
       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就可以了。
注意,有可能训练集的数据中某个类别特征的某些取值没有出现,这种情况下,你需要认为的设置参数n_values。

>>> enc = preprocessing.OneHotEncoder(n_values=[2, 3, 4])
>>> # Note that there are missing categorical values for the 2nd and 3rd
>>> # features
>>> enc.fit([[1, 2, 3], [0, 2, 0]])  
OneHotEncoder(categorical_features='all', dtype=<... 'numpy.float64'>,
       handle_unknown='error', n_values=[2, 3, 4], sparse=True)
>>> enc.transform([[1, 0, 0]]).toarray()
array([[ 0.,  1.,  1.,  0.,  0.,  1.,  0.,  0.,  0.]])

有时候类别特征不一定是整数表示的,你可以先对特征进行字典话(dic),然后在进一步处理,具体例子看这里

丢失值处理

因为各种各样的原因,现实世界中的数据集可能包含了缺失值,一般用空格、nan或者是其他的占位符表示。这样的数据集是不能直接拿来训练的。最基本的策略是丢弃那些包含缺失值的样本。然而,这么做可能会丢失一些有价值的数据,虽然这些数据有些项是缺失的。更好一点的策略是,根据已知的数据,对这些数据的缺失项进行填充。
Imputer 这个类提供了填充缺失值的基本策略,包括均值填充,中位数填充,还有众数填充。
下面的例子展示了均值填充:

>>> import numpy as np
>>> from sklearn.preprocessing import Imputer
>>> imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
>>> imp.fit([[1, 2], [np.nan, 3], [7, 6]])
Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)
>>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
>>> print(imp.transform(X))                           
[[ 4.          2.        ]
 [ 6.          3.666...]
 [ 7.          6.        ]]

当然,稀疏矩阵也可以填充。

>>> import scipy.sparse as sp
>>> X = sp.csc_matrix([[1, 2], [0, 3], [7, 6]])
>>> imp = Imputer(missing_values=0, strategy='mean', axis=0)
>>> imp.fit(X)
Imputer(axis=0, copy=True, missing_values=0, strategy='mean', verbose=0)
>>> X_test = sp.csc_matrix([[0, 2], [6, 0], [7, 6]])
>>> print(imp.transform(X_test))                      
[[ 4.          2.        ]
 [ 6.          3.666...]
 [ 7.          6.        ]]

注意看,这里的缺失值是用0表示的。这种形式非常适合缺失项比未缺失项多的情况。这里是一个填充的例子。

生成多项式特征

考虑输入数据的非线性特征来增加模型的复杂度经常是有用的尝试。简单常用的做法是使用多项式特征,它常常会获得这些特征的高维的或者是交互的信息。可以尝试使用 PolynomialFeatures :

>>> import numpy as np
>>> from sklearn.preprocessing import PolynomialFeatures
>>> X = np.arange(6).reshape(3, 2)
>>> X                                                 
array([[0, 1],
       [2, 3],
       [4, 5]])
>>> poly = 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.]])

向量X就从 8d36cb3394e3d439143d8860f763ec33.png 转换成 a875b9016d6a3412e915d1d18496c094.png
其实多项式特征在核方法例如 sklearn.svm.SVC, sklearn.decomposition.KernelPCA (当你使用多项式核函数的时候)间接使用到。这里是一个使用多项式特征进行岭回归的例子。

惯用的转换操作

你有时候会想把一个现成的python的方法转换成一个transformer,在数据清洗或者预处理的时候使用。你确实可以这么做,只要你使用 FunctionTransformer 的话。比如说,对数据做一个取对数操作:

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

这里有一个更加完整的例子,你可以参考。

学习或交流 联系qq378600275

你可能感兴趣的:(机器学习,sklearn)