scaling-data-to-the-standard-normal

把数据调整为标准正态分布

经常需要将数据标准化调整(scaling)为标准正态分布(standard normal)。标准正态分布算得上是统计学中最重要的分布了。如果你学过统计,Z值表(z-scores)应该不陌生。实际上,Z值表的作用就是把服从某种分布的特征转换成标准正态分布的Z值。

Getting ready

数据标准化调整是非常有用的。许多机器学习算法在具有不同范围特征的数据中呈现不同的学习效果。例如,SVM(Support Vector Machine,支持向量机)在没有标准化调整过的数据中表现很差,因为可能一个变量的范围是0-10000,而另一个变量的范围是0-1。preprocessing模块提供了一些函数可以将特征调整为标准形:

In [1]:
from sklearn import preprocessing
import numpy as np

How to do it...

还用boston数据集运行下面的代码:

In [2]:
from sklearn import datasets
boston = datasets.load_boston()
X, y = boston.data, boston.target
In [5]:
X[:, :3].mean(axis=0) #前三个特征的均值
Out[5]:
array([  3.59376071,  11.36363636,  11.13677866])
In [6]:
X[:, :3].std(axis=0) #前三个特征的标准差
Out[6]:
array([  8.58828355,  23.29939569,   6.85357058])

这里看出很多信息。首先,第一个特征的均值是三个特征中最小的,而其标准差却比第三个特征的标准差大。第二个特征的均值和标准差都是最大的——说明它的值很分散,我们通过preprocessing对它们标准化:

In [7]:
X_2 = preprocessing.scale(X[:, :3])
In [8]:
X_2.mean(axis=0)
Out[8]:
array([  6.34099712e-17,  -6.34319123e-16,  -2.68291099e-15])
In [9]:
X_2.std(axis=0)
Out[9]:
array([ 1.,  1.,  1.])

How it works...

中心化与标准化函数很简单,就是减去均值后除以标准差,公式如下所示:

$$x= \frac {x- \bar x} \sigma$$

除了这个函数,还有一个中心化与标准化类,与管线命令(Pipeline)联合处理大数据集时很有用。单独使用一个中心化与标准化类实例也是有用处的:

In [10]:
my_scaler = preprocessing.StandardScaler()
my_scaler.fit(X[:, :3])
my_scaler.transform(X[:, :3]).mean(axis=0)
Out[10]:
array([  6.34099712e-17,  -6.34319123e-16,  -2.68291099e-15])

把特征的样本均值变成0,标准差变成1,这种标准化处理并不是唯一的方法。preprocessing还有MinMaxScaler类,将样本数据根据最大值和最小值调整到一个区间内:

In [14]:
my_minmax_scaler = preprocessing.MinMaxScaler()
my_minmax_scaler.fit(X[:, :3])
my_minmax_scaler.transform(X[:, :3]).max(axis=0)
Out[14]:
array([ 1.,  1.,  1.])

通过MinMaxScaler类可以很容易将默认的区间01修改为需要的区间:

In [19]:
my_odd_scaler = preprocessing.MinMaxScaler(feature_range=(-3.14, 3.14))
my_odd_scaler.fit(X[:, :3])
my_odd_scaler.transform(X[:, :3]).max(axis=0)
Out[19]:
array([ 3.14,  3.14,  3.14])

还有一种方法是正态化(normalization)。它会将每个样本长度标准化为1。这种方法和前面介绍的不同,它的特征值是标量。正态化代码如下:

In [27]:
normalized_X = preprocessing.normalize(X[:, :3])

乍看好像没什么用,但是在求欧式距离(相似度度量指标)时就很必要了。例如三个样本分别是向量(1,1,0)(3,3,0)(1,-1,0)。样本1与样本3的距离比样本1与样本2的距离短,尽管样本1与样本3是轴对称,而样本1与样本2只是比例不同而已。由于距离常用于相似度检测,因此建模之前如果不对数据进行正态化很可能会造成失误。

There's more...

数据填补(data imputation)是一个内涵丰富的主题,在使用scikit-learn的数据填补功能时需要注意以下两点。

创建幂等标准化(idempotent scaler)对象

有时可能需要标准化StandardScaler实例的均值和/或方差。例如,可能(尽管没用)会经过一系列变化创建出一个与原来完全相同的StandardScaler

In [37]:
my_useless_scaler = preprocessing.StandardScaler(with_mean=False, with_std=False)
transformed_sd = my_useless_scaler.fit_transform(X[:, :3]).std(axis=0)
original_sd = X[:, :3].std(axis=0)
np.array_equal(transformed_sd, original_sd)
Out[37]:
True

处理稀疏数据填补

在标准化处理时,稀疏矩阵的处理方式与正常矩阵没太大不同。这是因为数据经过中心化处理后,原来的0值会变成非0值,这样稀疏矩阵经过处理就不再稀疏了:

In [45]:
import scipy
matrix = scipy.sparse.eye(1000)
preprocessing.scale(matrix)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-45-466df6030461> in <module>()
      1 import scipy
      2 matrix = scipy.sparse.eye(1000)
----> 3 preprocessing.scale(matrix)

d:\programfiles\Miniconda3\lib\site-packages\sklearn\preprocessing\data.py in scale(X, axis, with_mean, with_std, copy)
    120         if with_mean:
    121             raise ValueError(
--> 122                 "Cannot center sparse matrices: pass `with_mean=False` instead"
    123                 " See docstring for motivation and alternatives.")
    124         if axis != 0:

ValueError: Cannot center sparse matrices: pass `with_mean=False` instead See docstring for motivation and alternatives.

这个错误表面,标准化一个稀疏矩阵不能带with_mean,只要with_std

In [58]:
preprocessing.scale(matrix, with_mean=False)
Out[58]:
<1000x1000 sparse matrix of type '<class 'numpy.float64'>'
	with 1000 stored elements in Compressed Sparse Row format>

另一个方法是直接处理matrix.todense()。但是,这个方法很危险,因为矩阵已经是稀疏的了,这么做可能出现内存异常。