以 sklearn 中的鸢尾花数据集
为例来对数据预处理进行说明。鸢尾花数据集内包含 3 类共 150 条记录,每类各 50 个数据,每条记录都有 4 项特征:花萼长度(sepal length)、花萼宽度(sepal width)、花瓣长度(petal length)、花瓣宽度(petal length)。
导入鸢尾花数据集的代码如下:
from sklearn.datasets import load_iris
#导入IRIS数据集
iris = load_iris()
如果特征的规格不一样,就不能够放在一起比较。
比如:在两个样本中肿瘤大小的分别为 1 cm 和 5 cm,发现时间分别为 100 天和 200 天,那么在求距离时,时间差为 100,大小差为 4,那么其结果会被时间所主导,因为肿瘤大小的差距太小了。但是如果我们把时间用年做单位,0.27 年与 0.55 年的差距又远小于肿瘤大小的差距,结果又会被大小主导了。
这时就需要无量纲化处理,无量纲化使不同规格的数据转换到同一规格。常见的无量纲化方法有归一化和标准化。
常用的数据归一化方法有:
Min-Max Normalization,将数据归一化到 [0, 1]
之间,公式为:
x s c a l e = x − x m i n x m a x − x m i n x_{scale}=\frac{x-x_{min}}{x_{max}-x_{min}} xscale=xmax−xminx−xmin
使用 sklearn.preproccessing
库的 MinMaxScaler
类对数据进行归一化处理,默认归一到 [0, 1] 之间,的代码如下:
from sklearn.preprocessing import MinMaxScaler
#区间缩放,返回值为缩放到[0, 1]区间的数据
MinMaxScaler().fit_transform(iris.data)
使用 MinMaxScaler
的参数 feature_range
实现将数据归一到 [0, 1] 以外的范围:
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
MinMaxScaler(feature_range=[5, 10]).fit_trainsform(data) # 归一到[5, 10]之间
平均归一化,将数据归一化到 [-1, 1]
之间,公式为:
x s c a l e = x − x ˉ x m a x − x m i n x_{scale}=\frac{x-\bar{x}}{x_{max}-x_{min}} xscale=xmax−xminx−xˉ
其中,$ \bar{x} 为 均 值 , 为均值, 为均值,\bar{x}=\frac{\sum_{i=1}^{n}x_i}{n}$
非线性归一化:
经常用在数据分化比较大的场景,有些数值很大,有些很小。通过一些数学函数,将原始值进行映射。
该方法包括 log、指数,正切等。需要根据数据分布的情况,决定非线性函数的曲线,比如 log(V, 2) 还是 log(V, 10) 等。
标准化的前提是特征值服从正态分布,标准化后,其转换成标准正态分布。
Z-score 标准化(Z-score standardization,0-均值标准化),使数据均值为 0, 方差为 1,公式为:
x s c a l e = x − x ˉ s x_{scale}=\frac{x-\bar{x}}{s} xscale=sx−xˉ
其中, x ˉ \bar{x} xˉ 为均值, x ˉ = ∑ i = 1 n x i n \bar{x}=\frac{\sum_{i=1}^{n}x_i}{n} xˉ=n∑i=1nxi
s 为标准差, s = ∑ i = 1 n ( x i − x ˉ ) 2 n s=\sqrt{\frac{\sum_{i=1}^{n}\left(x_{i}-\bar{x}\right)^{2}}{n}} s=n∑i=1n(xi−xˉ)2
使用 sklearn.preproccessing
库的 StandardScaler
类对数据进行标准化的代码如下:
from sklearn.preprocessing import StandardScaler
#标准化,返回值为标准化后的数据
StandardScaler().fit_transform(iris.data)
相同点:
归一化和标准化的目的都是取消由于量纲不同引起的误差;
两者都是一种线性变换,都是把数据按照比例压缩再进行平移。
不同点:
目的不同,归一化是为了将数据压缩到某一限定的区间,比如 [0, 1] 之间;
标准化只是调整特征整体的分布;
归一化与最大,最小值有关,对异常值敏感;
标准化与均值,标准差有关;
归一化输出限定在一定的范围内;
标准化要求数据的特征值符合正态分布;
应用场景:
在分类、聚类算法中,需要使用距离来度量相似性的时候(如 SVM、KNN)、或者使用 PCA 技术进行降维的时候,标准化(Z-score standardization)表现更好;
在不涉及距离度量、协方差计算、数据不符合正态分布的时候,可以使用归一化方法。
例如图像处理中,将 RGB 图像转换为灰度图像后将其值限定在 [0 255] 的范围;
基于树的方法不需要进行特征的归一化。
例如随机森林,bagging 与 boosting 等方法。
如果是基于参数的模型或者基于距离的模型,因为需要对参数或者距离进行计算,都需要进行归一化。
总结来说,建议优先使用标准化。对于输出有要求时再尝试别的方法,如归一化或者更加复杂的方法。很多方法都可以将输出范围调整到 [0,
1],如果我们对于数据的分布有假设的话,更加有效的方法是使用相对应的概率密度函数来转换。
数据中,常常会有重要的字段缺失值很多,但又不能舍弃字段的情况。因此,数据预处理中非常重要的一项就是处理缺失值。
由于鸢尾花数据集没有缺失值,故对数据集新增一个样本,4 个特征均赋值为 NaN,表示数据缺失。
使用 sklearn.preproccessing
库的 Imputer
类对数据进行缺失值计算的代码如下:
from numpy import vstack, array, nan
from sklearn.preprocessing import Imputer
# 缺失值计算,返回值为计算缺失值后的数据
# 参数missing_value为缺失值的表示形式,默认为NaN
# 参数strategy为缺失值填充方式,默认为mean(均值)
Imputer().fit_transform(vstack((array([nan, nan, nan, nan]), iris.data)))
在现实生活中,许多标签和特征在数据收集完毕的时候,都不是以数字来表现的,而是分类数据。比如说,学历的取值可以是 [“小学”,“初中”,“高中”,“大学”],付费方式可能包含 [“支付宝”,“现金”,“微信”] 等等。然而,大部分算法都只能够处理数值型数据,在这种情况下,为了让数据适应算法和库,我们必须将数据进行编码
,即是说,将文字型数据转换为数值型
。
使用 sklearn.preprocessing
库中的 LabelEncoder
类,标签专用,将分类标签转化为分类数值;
使用 sklearn.preprocessing
库中的 OrdinalEncoder
类,标签专用,将分类特征转化为分类数值;
使用 sklearn.preprocessing
库中的 OneHotEncoder
类,独热编码,创建哑变量;
由于鸢尾花数据集的特征皆为定量特征,故使用其目标值进行哑编码(实际上是不需要的),代码如下:
from sklearn.preprocessing import OneHotEncoder
# 哑编码,对IRIS数据集的目标值,返回值为哑编码后的数据
OneHotEncoder().fit_transform(iris.target.reshape((-1,1)))
为什么要使用哑变量呢?
我们考虑三种不同性质的分类数据:
付费方式(支付宝,现金,微信)
三种取值是相互独立的,彼此之间完全没有联系,表达的是支付宝 ≠ 现金 ≠ 微信
的概念。这是名义变量。
学历(小学,初中,高中)
三种取值不是完全独立的,在性质上可以有高中 > 初中 > 小学
这样的联系,学历有高低,但是学历取值之间却不是可以计算的,不能说小学 + 某个取值 = 初中
。这是有序变量。
体重(>45kg,>90kg,>135kg)
各个取值之间有联系,且是可以互相计算的,比如 120kg - 45kg = 90kg
,分类之间可以通过数学计算互相转换。这是有距变量。
在对特征进行编码的时候,如果简单的就把分类数据转换成 [0, 1, 2],这三个数字在算法看来是连续,有大小且可以计算的,算法会把付费方式,学历这样的分类特征,都误会成是体重这样的分类特征。这是说,我们把分类转换成数字的时候,忽略了数字中自带的数学性质,所以给算法传达了一些不准确的信息,而这会影响我们的建模。
所以使用哑变量,也就是独热编码的方式,让算法能够彻底领悟,三个取值是没有可计算性质的(这里正好与自然语言处理中的 one-hot 编码相反,在 NLP 中我们摒弃了传统的 one-hot 编码,因为它无法表示出每个单词之间的相似性)。
连续型特征二值化的核心在于设定一个阈值,大于阈值的赋值为1,小于等于阈值的赋值为0,公式如下:
x ′ = { 1 , x > threshold 0 , x ≤ threshold x^{\prime}=\left\{\begin{array}{l}{1, x>\text { threshold }} \\ {0, x \leq \text { threshold }}\end{array}\right. x′={1,x> threshold 0,x≤ threshold
使用 sklearn.preprocessing
库中的 Binarizer
类对数据进行二值化,代码如下:
from sklearn.preprocessing import Binarizer
# 二值化,阈值设置为3,返回值为二值化后的数据
Binarizer(threshold=3).fit_transform(iris.data)
参考资料:
本系列为参加自公众号「数据科学家联盟」的机器学习小组的系列笔记
【数据科学家学习小组】之机器学习(第一期)第三周:https://mp.weixin.qq.com/s/x00bAoFg2LUa2kihIZo_HA
特征工程系列:特征预处理(上):https://mp.weixin.qq.com/s/qWO9zgKyntvyWfftpGqrHQ
使用 sklearn 做单机特征工程:https://www.cnblogs.com/jasonfreak/p/5448385.html
sklearn 中的特征预处理和特征工程:https://www.cnblogs.com/juanjiang/archive/2019/05/30/10948849.html