在scikit-learn中,数据的标准化是很多机器学习估计器的常见要求;如果单个特征看起来不符合标准正态分布(平均值为0,方差为1)的话,数据之后可能会有很差的表现。
实际上我们通常忽略分布的具体形态,数据转换仅指,减去每个特征的平均值,再除以他们的标准差。
例如,学习算法中目标函数的很多成分都假设,所有的特征都是围绕着0的,并且拥有相同算数级别的方差(比如SVM中的RBF核,以及线性模型中的l1,l2正则化)。如果一个特征的方差级别高于其他的特征,它会在目标函数中占据主导地位,并使得估计器不能按照预期很好地从其他特征中学习。
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
调整后的数据平均值为0,方差为1:
X_scaled.mean(axis=0)
X_scaled.std(axis=0)
preprocessing模块还提供了一个类"StandardScaler",它能计算训练集的平均值和标准差,以便之后对测试集进行相同的转换。因此,这个类适合用于sklearn.pipeline.Pipeline的前几个步骤:
scaler = preprocessing.StandardScaler().fit(X)
scaler
scaler.mean_
scaler.scale_
scaler.transform(X)
这个scaler之后能对新的数据进行,跟先前对训练集一样的操作:
scaler.transform([[-1., 1., 0.]])
此外,也可以通过在创建StandardScaler时增加with_mean=False或者with_std=False语句,来阻止集中化或缩放比例。
另一个标准化的操作,是把特征缩放到一个最小值与最大值之间(通常是0到1),或者是把每个特征的最大绝对值变到1。这分别可以通过MinMaxScaler或者MaxAbsScaler实现。
使用这种转换方式是为了增加强健性,来解决特征的标准差非常小的问题,以及在稀疏数据中保留0元素。
以下是一个把数据矩阵缩放到[0,1]范围内的一个例子:
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
相同的转换器可以用到新的测试集上:相同的缩放、平移操作会与之前对训练数据的操作保持一致:
X_test = np.array([[ -3., -1., 4.]])
X_test_minmax = min_max_scaler.transform(X_test)
X_test_minmax
我们也可以找出从训练数据中学到的转换的具体特性:
min_max_scaler.scale_
min_max_scaler.min_
如果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]范围内。这对已经围绕着0的数据或者稀疏数据来说是很有意义的。
这里用了这个scaler把之前例子的数据进行了转换:
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^
X_test = np.array([[ -3., -1., 4.]])
X_test_maxabs = max_abs_scaler.transform(X_test)
X_test_maxabs
max_abs_scaler.scale_
与scale一样,这个模块也提供了比较简便的函数minmax_scale以及maxabs_scale,如果你不想创建一个对象。
把稀疏数据集中化会破坏数据中的稀疏性结构,因此不是一个理想的做法。但是,对稀疏的输入转换测度是有道理的,特别是当特征具有不同的测度的时候。
MaxAbsScaler以及maxabs_scale是特别为转换稀疏数据设计的,并且我们建议使用他们。然而,scale和StandardScaler可以接受scipy.sparse矩阵作为输入,只要在创建时说明with_mean=False。否则会产生ValueError,因为默认的集中化会破坏稀疏性,并且会分配过多的内存从而导致运行崩溃。RobustScaler不能用于稀疏输入,但你可以对稀疏输入使用transform方法。
注意,scalers同时接受Compressed Sparse Rows以及Compressed Sparse Columns形式。其他类型的稀疏输入会被转换为Compressed Sparse Rows的形式。为了避免不必要的内存复制,建议选择CSR或者CSC的表达形式。
最后,如果集中化后的数据预期非常小,使用toarray方法把稀疏输入转换为数组是另一个选择。
如果你的数据有很多异常值,使用平均值和方差来进行转换可能表现不会很好。在这些情况下,你可以使用robust_scale以及RobustScaler。他们对数据的中心和范围采用更健壮的估计。
关于集中化和缩放比例重要性更多的讨论:Should I normalize/standardize/resclae the data?.
如果你有一个核矩阵来计算特征空间的点积,KernelCenterer可以转换核矩阵,使其包含特征空间的内积,移去空间中的平均值。
规范化是指,对单个样本缩放比例,使其范数为1。如果你计划使用平方的形式,例如点积或者其他核来量化样本之间的相似性,这个过程是有用的。
这个假设是Vector Space Model的基础,而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
Preprocessing模块还提供了一个类Normalizer来执行相同的操作,它使用了Transformer API(即使fit方法在这个情况下是没有用的:这个类没有状态,因为这个操作对样本独立进行处理)。
因此,这个类适用于sklearn.pipeline.Pipeline的前几个步骤:
normalizer = preprocessing.Normalizer().fit(X) # fit does nothing
normalizer
这个normalizer和其他transformer一样,可以用到样本向量中。
normalizer.transform(X)
normalizer.transform([[-1., 1., 0.]])
normalize和normalizer同时接受scipy.sparse中密集的数组型,以及稀疏的矩阵作为输入。
对于稀疏输入,数据会被转换为Compressed Sparse Rows表示形式。为了避免不必要的内存复制,建议使用CSR表示形式。
X = [[ 1., -1., 2.],
[ 2., 0., 0.],
[ 0., 1., -1.]]
binarizer = preprocessing.Binarizer().fit(X) # fit does nothing
binarizer
binarizer.transform(X)
binarizer的阈值可以进行调整:
binarizer = preprocessing.Binarizer(threshold=1.1)
binarizer.transform(X)
至于StandardScaler以及Normalizer类,预处理模块提供了一个伴随函数binarize,它在transformer API不必要时会被使用。
binarize和Binarizer同时接受scipy.sparse中密集的数组型,以及稀疏的矩阵作为输入。
对于稀疏输入,数据会被转换为Compressed Sparse Rows表示形式。为了避免不必要的内存复制,建议使用CSR表示形式。
经常,特征不是通过连续值表达的,而是通过类型。例如,一个人可以拥有特征["男人", "女人"], ["来自欧洲", "来自美国", "来自亚洲"], ["使用 Firefox", "使用 Chrome", "使用 Safari", "使用 Internet Explorer"]. 这些特征可以被有效编码为整数,例如,["男人", "来自美国", "使用 Internet Explorer"]可以表示为[0, 1, 3],["女人", "来自亚国", "使用 Chrome"]可以表示为[1, 2, 1]。
这样的整数表达方式不能直接被用于scikit-learn估计器中,因为估计器会把它们当做连续的输入,把这些类别解释为有顺序的,而这并不是我们所期望的。
一种把类型特征转换为可以被scikit-learn估计器使用的方式是,使用一种one-of-K,或one-hot编码,它们在OneHotEncoder中被用到。这个估计器把每个,有m个可能值的类型特征转换为m个布尔值,只有一个值为1.
继续之前的例子:
enc = preprocessing.OneHotEncoder()
enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])
enc.transform([[0, 1, 3]]).toarray()
默认地,每个特征能取多少值会自动地从数据集中推测出来。也可以通过n_values明确的说明这个参数。在这个例子中,我们的数据集有两个性别,三个州,以及四个网页浏览器。之后,我们用估计器来学习,转换每个数据点。结果是,前两个数字是性别的编码,之后三个数字是州的编码,最后四个数字是网页浏览器的编码。
注意,如果训练数据有可能丢失了一些类型特征,我们必须明确设置n_values。例如,
enc = preprocessing.OneHotEncoder(n_values=[2, 3, 4])
# 注意第二个和第三个类型值有缺失值
# 特征
enc.fit([[1, 2, 3], [0, 2, 0]])
enc.transform([[1, 0, 0]]).toarray()
用字典表示,而不是用整数表示的类型特征,可以参照Loading features from dicts.
由于各种各样的原因,很多现实生活中的数据集都会有缺失值,经常会被编码为空白,NaNs或其他占位符。然而,这些数据集与scikit-learn估计器并不兼容,因为估计器假设数组中的所有值都是数值型的,并且都有意义。处理不兼容的数据集一个基本的方法是,丢弃包含缺失值的整行或整列。但是,一些有价值的数据可能会因此丢失,尽管它们不兼容。一个更好的办法是,对缺失值进行其他处理,从数据的已有部分来推测缺失值。
Imputer类提供了处理缺失值的一些基本方法,它把缺失值用它所在行或列的平均值,中位数或众数来代替。这个类同时也允许不同的缺失值编码方式。
下面这个例子把缺失值(编码为np.nan)用它所在列的平均值代替:
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))
Imputer也支持稀疏矩阵:
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)
X_test = sp.csc_matrix([[0, 2], [6, 0], [7, 6]])
print(imp.transform(X_test))
注意,在这里,缺失值被编码为0,所以被隐性地储存在了矩阵中。当缺失值个数比观察值多很多的时候,这个形式适用。
Imputer能被用到Pipeline中,用来建立一个支持处理缺失值的复合估计器。参考Imputing missing values before builiding an estimator.
经常,通过考虑输入数据的非线性特征来增加模型的复杂性是有用的。一个简单且常用的方法是使用多项式特征,它能得到特征的高次项以及交互项,通过PolynomialFeatures实现:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(6).reshape(3, 2)
X
poly = PolynomialFeatures(2)
poly.fit_transform(X)
在这里,X的特征从(X1,X2)被转换为了(1, X_1, X_2, X_1^2, X_1X_2, X_2^2).
有时候,只有特征之间的交互项是被要求的,它可以通过设置interaction_only=True来实现:
X = np.arange(9).reshape(3, 3)
X
poly = PolynomialFeatures(degree=3, interaction_only=True)
poly.fit_transform(X)
在这里,X的特征从(X_1, X_2, X_3)被转换为了(1, X_1, X_2, X_3, X_1X_2, X_1X_3, X_2X_3, X_1X_2X_3).
注意,在核方法(如sklearn.svm.SVC,sklearn.decomposition.KernelPCA)中使用多项式核函数时,多项式特征被隐性使用。
经常,你想要把一个已经存在的Python函数转换为一个转换器,用来协助数据清理或处理。你可以通过FunctionTransformer来实现。例如,为了生成一个可以进行log变化的转换器,可以:
import numpy as np
from sklearn.preprocessing import FunctionTransformer
transformer = FunctionTransformer(np.log1p)
X = np.array([[0, 1], [2, 3]])
transformer.transform(X)
使用FunctionTransformer来定制特征选择的完整示例代码,可见Using FunctionTransformer to select columns.