2. 特征工程

特征工程

​ 在实际工作中获取到的数据往往不那么理想,可能会存在非数值类型的文本数据、重复值、缺失值、异常值及数据分布不均衡等问题,因此,在进行数学建模前还需要对这些问题进行处理,这项工作称为特征工程。特征工程通常分为特征使用方案特征获取方案特征处理特征监控几大部分,其中特征处理是特征工程的核心内容,有时称为数据预处理。

数据预处理概述

在处理数据前需要理解数据的构成:一行一样本,一列一特征

数据预处理的过程: 输入数据 --> 模型 --> 输出变化后的数据

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)

参数:

  • with_mean:bool, default=True:如果为True,则在缩放之前将数据居中。这在操作稀疏矩阵时不起作用(并且会引发一个异常),因为将它们居中需要构建一个密集矩阵,在常见的用例中,密集矩阵可能太大而无法容纳在内存中。
  • with_std:bool, default=True:如果为True,则将数据缩放为单位方差(或等效的单位标准差)。

属性:

  • scale_ :ndarray of shape (n_features,) or None:数据的每个特征相对缩放,以实现零均值和单位方差。通常使用np.sqrt(var)计算。如果方差为零,我们无法实现单位方差,数据保持原样,给出缩放因子1。当with_std=False时,scale_等于None。
  • mean_ :ndarray of shape (n_features,) or None :训练集中每个特征的平均值。当_mean=False时,等于None。
  • var_ :ndarray of shape (n_features,) or None:训练集中每个特征的方差。用于计算scale_ 。当std=False时,等于None。

示例:

>>> 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=maxminXmin

其中max为样本数据的最大值,min 为样本数据的最小值,max-min为极差。离差标准化保留了原始数据值之间的联系,是消除量纲和数据取值范围影响最简单的方法。

离差标准化的特点:

  • 数据的整体分布情况并不会随离差标准化而发生改变,原先取值较大的数据,在做完离差标准化后的值依旧较大。
  • 当数据和最小值相等的时候,通过离差标准化可以发现数据变为0。

​同时,还可以看出离差标准化的缺点:

  • 若数据集中某个数值很大,则离差标准化的值就会接近于0,并且相互之间差别不大。
  • 若将来遇到超过目前属性[min,max]取值范围的时候,会引起系统出错,这时便需要重新确定min和max。

示例:

例如有一列特征值表示年龄: [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)

参数:

  • feature_range:tuple (min, max), default=(0, 1):所需的转换数据范围。

示例:

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

参数说明:

  • with_centering: bool, default=True:如果为True,则在缩放之前将数据居中。这将导致在稀疏矩阵上尝试转换时引发异常,因为将它们居中需要构建密集矩阵,而在常见的使用情况下,密集矩阵可能太大而无法容纳在内存中。
  • with_scaling:bool, default=True:如果为True,则将数据缩放到四分位数范围。
  • quantile_range:tuple (q_min, q_max), 0.0 < q_min < q_max < 100.0, default=(25.0, 75.0):用于计算scale_的分位数范围。默认情况下,这等于IQR,即q_min是第一个分位数,q_max是第三个分位数。
  • copy:bool, default=True:如果为False,请尝试避免复制,改为就地缩放。这不能保证总是在原地工作;
  • unit_variance:bool, default=False:如果为True,则缩放数据,使正态分布特征的方差为1。通常,如果标准正态分布的q_max和q_min的x值之间的差值大于1,则数据集将缩小。如果小于1,将放大数据集。

示例:

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

归一化

​ 归一化/标准化可以去除数据单位对计算带来的影响,也就是所谓的去量纲行为,归一化/标准化实质是一种线性变换,线性变换有很多良好的性质,这些性质决定了对数据改变后不会造成“失效”,反而能提高数据的表现,这些性质是归一化/标准化的前提。

归一化/标准化的去量纲作用能够带来以下两个好处:

  1. 提升模型的精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。
  2. 提高收敛速度。对于线性模型来说,数据归一化/标准化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。

有些情况每个样本的每个特征值具体的值并不重要,但是每个样本特征值的占比更加重要。

动作 爱情 科幻
小明 20 10 5
小王 4 2 1
小李 15 11 13

所以归一化即是用每个样本的每个特征值除以该样本各个特征值绝对值的总和。变换后的样本矩阵,每个样本的特征值绝对值之和为1。

样本单独归一化为单位范数。

API:

class sklearn.preprocessing.Normalizer(norm='l2', *, copy=True)

参数:

  • norm:{‘l1’, ‘l2’, ‘max’}, default=’l2’:用于标准化每个非零样本的范数。如果使用norm=‘max’,则值将按绝对值的最大值重新缩放。
  • copy:bool, default=True:设置为False以执行在位行规范化并避免复制(如果输入已经是numpy数组或scipy.sparse CSR矩阵)。

示例:

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

参数:

  • threshold:float, default=0.0:低于或等于此的特征值由0替换,高于此值由1替换。对于稀疏矩阵上的操作,阈值不能小于0。

示例:

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

独热编码(onehot)

将分类特征编码为一个热点数字数组。为样本特征的每个值建立一个由一个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)

参数:

  • categories:‘auto’ or a list of array-like, default=’auto’:
    • “auto”:根据培训数据自动确定类别。
    • list:categories[i]包含第i列中预期的类别。传递的类别不应在单个功能中混合字符串和数值,并且应在数值的情况下进行排序。
  • sparse:bool, default=True:如果设置为True,将返回稀疏矩阵,否则将返回数组。
  • min_frequency:int or float, default=None:指定最低频率,低于该频率类别将被视为不常见。
  • max_categories:int, default=None:在考虑不常用类别时,为每个输入要素指定输出要素数量的上限。如果存在不常见的类别,max_categories将包括表示不常见类别的类别以及常见类别。如果无,则输出特征的数量没有限制。

示例:

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

无参数

属性:

  • classes_ :ndarray of shape (n_classes,):保存每个类的标签。

示例:

>>> 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库提供的,如果有特殊需求,可以根据自己的需要,编写代码实现。

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