在实际工作中获取到的数据往往不那么理想,可能会存在非数值类型的文本数据、重复值、缺失值、异常值及数据分布不均衡等问题,因此,在进行数学建模前还需要对这些问题进行处理,这项工作称为特征工程。特征工程通常分为特征使用方案、特征获取方案、特征处理、特征监控几大部分,其中特征处理是特征工程的核心内容,有时称为数据预处理。
在处理数据前需要理解数据的构成:一行一样本,一列一特征
数据预处理的过程: 输入数据 --> 模型 --> 输出变化后的数据
sklearn库提供了相关的类和方法可以满足绝大多数数据预处理的需求,本文主要结合sklearn库进行理论讲解和代码实现。sklearn把相关的功能封装为转换器(transformer)。使用sklearn转换器能够实现对传入的NumPy数组进行标准化处理,归一化处理,二值化处理,PCA降维等操作。
sklearn库中的preprocessing模块中提供了非常多的类和方法进行各种各样的数据预处理:
# 导包
import sklearn.preprocessing as sp
sklearn部分预处理函数与其作用
sklearn预处理包提供了几个常用的实用函数和转换器类,以将原始特征向量转换为更适合下游估计器的使用。通常,学习算法受益于数据集的标准化。如果数据集中存在一些异常值,则稳健的定标器或变换器更合适。在包含边缘异常值的数据集上,不同缩放器、变换器和归一化器的行为在比较不同缩放器对数据和异常值的影响中突出显示。
函数名称 | 说明 |
---|---|
StandardScaler | 对特征进行标准差标准化。 |
MinMaxScaler | 对特征进行离差标准化。 |
Normalizer | 对特征进行归一化。 |
Binarizer | 对定量特征进行二值化处理。 |
OneHotEncoder | 对定性特征进行独热编码处理。 |
LabelEncoder | 对特征进行标签编码。 |
FunctionTransformer | 对特征进行自定义函数变换。 |
KBinsDiscretizer | 将连续数据放入间隔。 |
LabelBinarizer | 将标签二进制化。 |
MaxAbsScaler | 按最大绝对值缩放每个特征 |
PolynomialFeatures | 生成多项式和交互特征 |
数据集的标准化是scikit学习中实现的许多机器学习估计器的共同要求;如果单个特征或多或少看起来不像标准的正态分布数据,它们可能会表现得很糟糕:具有零均值和单位方差的高斯分布。
在实践中,我们通常忽略分布的形状,只是通过去除每个特征的平均值来变换数据以使其居中,然后通过将非常数特征除以其标准差来缩放数据。
例如,学习算法的目标函数中使用的许多元素(例如支持向量机的RBF核或线性模型的l1和l2正则化器)可以假设所有特征都以零为中心或具有相同顺序的方差。如果一个特征的方差比其他特征的方差大几个数量级,那么它可能会主导目标函数,并使估计器无法按照预期正确地从其他特征中学习。
由于一个样本的不同特征值差异较大,不利于使用现有机器学习算法进行样本处理。
标准差标准化也叫零均值标准化或分数标准化,是当前使用最广泛的数据标准化方法。经过该方法处理的数据均值移除可以让样本矩阵中的每一列的平均值为0,标准差为1。
如何使样本矩阵中的每一列的平均值为0呢?
公式:X* = (X-mean)/ std
标准差标准化不会改变数据的分布情况。
例如有一列特征值表示年龄: 17, 20, 23
mean = (17 + 20 + 23)/3 = 20
a' = -3
b' = 0
c' = 3
完成!
如何使样本矩阵中的每一列的标准差为1呢?
a' = -3
b' = 0
c' = 3
s' = std(a', b', c')
[a'/s', b'/s', c'/s']
API:
class sklearn.preprocessing.StandardScaler(*, copy=True, with_mean=True, with_std=True)
参数:
属性:
示例:
>>> from sklearn.preprocessing import StandardScaler
>>> data = [[0, 0], [0, 0], [1, 1], [1, 1]]
>>> scaler = StandardScaler()
>>> print(scaler.fit(data))
StandardScaler()
>>> print(scaler.mean_)
[0.5 0.5]
>>> print(scaler.transform(data))
[[-1. -1.]
[-1. -1.]
[ 1. 1.]
[ 1. 1.]]
>>> print(scaler.transform([[2, 2]]))
[[3. 3.]]
另一种标准化方法是将特征缩放到给定的最小值和最大值之间,通常介于0和1之间,或者将每个特征的最大绝对值缩放到单位大小。这可以分别使用MinMaxScaler或MaxAbsScaler实现。使用这种缩放的动机包括对非常小的特征标准差的鲁棒性,以及在稀疏数据中保持零条目。
将样本矩阵中的每一列的最小值和最大值设定为相同的区间,统一各列特征值的范围。一般情况下会把特征值缩放至[0, 1]区间。
离差标准化是对原始数据的一种线性变换,结果是将原始数据的数值映射到[0,1]区间之间。
公式:
X ∗ = X − m i n m a x − m i n X^* = \frac{ X - min } { max - min } X∗=max−minX−min
其中max为样本数据的最大值,min 为样本数据的最小值,max-min为极差。离差标准化保留了原始数据值之间的联系,是消除量纲和数据取值范围影响最简单的方法。
离差标准化的特点:
同时,还可以看出离差标准化的缺点:
示例:
例如有一列特征值表示年龄: [17, 20, 23]
每个元素减去特征值数组所有元素的最小值即可:[0, 3, 6]
如何使一组特征值的最大值为1呢?
[0, 3, 6]
把特征值数组的每个元素除以最大值即可:[0, 1/2, 1]
API:
class sklearn.preprocessing.MinMaxScaler(feature_range=(0, 1), *, copy=True, clip=False)
参数:
示例:
>>> from sklearn.preprocessing import MinMaxScaler
>>> data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
>>> scaler = MinMaxScaler()
>>> print(scaler.fit(data)) # 计算用于以后缩放的最小值和最大值。
MinMaxScaler()
>>> print(scaler.data_max_) # 类属性
[ 1. 18.]
>>> print(scaler.transform(data)) # 根据feature_range缩放X的特征。
[[0. 0. ]
[0.25 0.25]
[0.5 0.5 ]
[1. 1. ]]
>>> print(scaler.transform([[2, 2]]))
[[1.5 0. ]]
如果数据中包含了许多异常值,那么使用数据的均值和方差进行缩放可能不会很好地工作。在这些情况下,您可以使用RobustScaler作为替代品。它对数据的中心和范围使用了更可靠的估计。
使用对异常值具有鲁棒性的统计数据来缩放特征。
此缩放器删除中值,并根据分位数范围(默认为IQR:四分位数范围)缩放数据。IQR是第一个四分位数(第25分位数)和第三个四分位(第75分位数)之间的范围。通过计算训练集中样本的相关统计信息,对每个特征进行居中和缩放。然后存储中值和四分位间距,以便使用转换方法用于后续数据。
数据集的标准化是许多机器学习估计器的共同要求。通常,这是通过去除平均值并缩放到单位方差来实现的。然而,异常值通常会以负面方式影响样本均值/方差。在这种情况下,中值和四分位数范围通常会给出更好的结果。
API:
class sklearn.preprocessing.RobustScaler(*, with_centering=True, with_scaling=True, quantile_range=(25.0, 75.0), copy=True, unit_variance=False)
参数说明:
示例:
>>> from sklearn.preprocessing import RobustScaler
>>> X = [[ 1., -2., 2.],
... [ -2., 1., 3.],
... [ 4., 1., -2.]]
>>> transformer = RobustScaler().fit(X)
>>> transformer
RobustScaler()
>>> transformer.transform(X)
array([[ 0. , -2. , 0. ],
[-1. , 0. , 0.4],
[ 1. , 0. , -1.6]])
归一化/标准化可以去除数据单位对计算带来的影响,也就是所谓的去量纲行为,归一化/标准化实质是一种线性变换,线性变换有很多良好的性质,这些性质决定了对数据改变后不会造成“失效”,反而能提高数据的表现,这些性质是归一化/标准化的前提。
归一化/标准化的去量纲作用能够带来以下两个好处:
有些情况每个样本的每个特征值具体的值并不重要,但是每个样本特征值的占比更加重要。
动作 | 爱情 | 科幻 | |
---|---|---|---|
小明 | 20 | 10 | 5 |
小王 | 4 | 2 | 1 |
小李 | 15 | 11 | 13 |
所以归一化即是用每个样本的每个特征值除以该样本各个特征值绝对值的总和。变换后的样本矩阵,每个样本的特征值绝对值之和为1。
将样本单独归一化为单位范数。
API:
class sklearn.preprocessing.Normalizer(norm='l2', *, copy=True)
参数:
示例:
>>> from sklearn.preprocessing import Normalizer
>>> X = [[4, 1, 2, 2],
... [1, 3, 9, 3],
... [5, 7, 5, 1]]
>>> transformer = Normalizer().fit(X) # fit does nothing.
>>> transformer
Normalizer()
>>> transformer.transform(X)
array([[0.8, 0.2, 0.4, 0.4],
[0.1, 0.3, 0.9, 0.3],
[0.5, 0.7, 0.5, 0.1]])
有些业务并不需要分析矩阵的详细完整数据(比如图像边缘识别只需要分析出图像边缘即可),可以根据一个事先给定的阈值,用0和1表示特征值不高于或高于阈值。二值化后的数组中每个元素非0即1,达到简化数学模型的目的。大于阈值的值映射到1,而小于或等于阈值的值则映射到0。默认阈值为0时,只有正值映射到1。
二值化是对文本计数数据的一种常见操作,分析人员可以决定只考虑特征的存在或不存在,而不是量化的出现次数。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,在贝叶斯设置中使用伯努利分布建模)。
API:
class sklearn.preprocessing.Binarizer(*, threshold=0.0, copy=True)
参数:
示例:
>>> from sklearn.preprocessing import Binarizer
>>> X = [[ 1., -1., 2.],
... [ 2., 0., 0.],
... [ 0., 1., -1.]]
>>> transformer = Binarizer().fit(X) # fit does nothing.
>>> transformer
Binarizer()
>>> transformer.transform(X)
array([[1., 0., 1.],
[1., 0., 0.],
[0., 1., 0.]])
将分类特征编码为一个热点数字数组。为样本特征的每个值建立一个由一个1和若干个0组成的序列,用该序列对所有的特征值进行编码。
两个数 三个数 四个数
1 3 2
7 5 4
1 8 6
7 3 9
为每一个数字进行独热编码:
1->10 3->100 2->1000
7->01 5->010 4->0100
8->001 6->0010 9->0001
编码完毕后得到最终经过独热编码后的样本矩阵:
101001000
010100100
100010010
011000001
导演 演员 上映地点 类型
战狼1 吴京 余男 内地 战争
钢铁侠1 钢哥 铁蛋 美国 科幻
战狼2 吴京 吴京 内地 战争
10 100 10 10
01 010 01 01
10 001 10 10
101001010
010100101
100011010
API:
class sklearn.preprocessing.OneHotEncoder(*, categories='auto', drop=None, sparse=True, dtype=<class 'numpy.float64'>, handle_unknown='error', min_frequency=None, max_categories=None)
参数:
示例:
>>> from sklearn.preprocessing import OneHotEncoder
>>> enc = OneHotEncoder(handle_unknown='ignore')
>>> X = [['Male', 1], ['Female', 3], ['Female', 2]]
>>> enc.fit(X)
OneHotEncoder(handle_unknown='ignore')
>>> enc.categories_ # 每个特征的类别
[array(['Female', 'Male'], dtype=object), array([1, 2, 3], dtype=object)]
>>> enc.transform([['Female', 1], ['Male', 4]]).toarray() # 将结果转为数组
array([[1., 0., 1., 0., 0.],
[0., 1., 0., 0., 0.]])
# 将编码反转为原来的形式
>>> enc.inverse_transform([[0, 1, 1, 0, 0], [0, 0, 0, 1, 0]])
array([['Male', 1],
[None, 2]], dtype=object)
>>> enc.get_feature_names_out(['gender', 'group'])
array(['gender_Female', 'gender_Male', 'group_1', 'group_2', 'group_3'], ...)
对值介于0和n_classes-1之间的目标标签进行编码。根据字符串形式的特征值在特征序列中的位置,为其指定一个数字标签,用于提供给基于数值算法的学习模型。该转换器应用于编码目标值,即y,而不是输入X。
API:
class sklearn.preprocessing.LabelEncoder()
无参数
属性:
示例:
>>> import numpy as np
>>> import sklearn.preprocessing as sp
>>> raw_samples = np.array([
... 'audi', 'ford', 'audi', 'toyota',
... 'ford', 'bmw', 'toyota', 'ford',
... 'audi'])
>>> print(raw_samples)
lbe = sp.LabelEncoder()
lbe_samples = lbe.fit_transform(raw_samples)
>>> print(lbe_samples)
inv_samples = lbe.inverse_transform(lbe_samples)
>>> print(inv_samples)
以上内容均用类实现,除了用类创建实例对象外,sklearn还提供了相对应的方法能够实现同样的功能一下是各种类对应的方法,此处只举几个常用的例子:
类名 | 方法名 |
---|---|
sklearn.preprocessing.Binarizer() | sklearn.preprocessing.binarize() |
sklearn.preprocessing.LabelBinarizer() | sklearn.preprocessing.label_binarize() |
sklearn.preprocessing.MaxAbsScaler() | sklearn.preprocessing.maxabs_scale() |
sklearn.preprocessing.MinMaxScaler() | sklearn.preprocessing.minmax_scale() |
sklearn.preprocessing.Normalizer() | sklearn.preprocessing.normalize() |
sklearn.preprocessing.StandardScaler() | sklearn.preprocessing.scale() |
sklearn.preprocessing.RobustScaler() | sklearn.preprocessing.robust_scale() |
在对数据进行预处理时需要结合当下的业务场景选择合适的方法进行处理。以上提供的方法均是由sklearn库提供的,如果有特殊需求,可以根据自己的需要,编写代码实现。