1、数据预处理概述¶
数据预处理是构建特征工程的基本环境,并且是提高算法精确度的有效手段。数据预处理需要根据数据本身的特性进行,不同的格式有不同的处理要求,比如无效的要剔除,缺失值要回填和冗余维度的选择等。大致主要分为三个步骤:数据的准备、转换和输出。本文主要利用sklearn讲解转换数据的常用方法。
2、无量纲化¶
在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布的需求,这种需求统称为将数据无量纲化。数据的无量纲化可以是线性的,也可以是非线性的。线性的无量纲化包括中心化(Zero-centered或者Mean-subtraction)处理和缩放处理(Scale)。中心化的本质是让所有记录减去一个固定值,即让数据样本数据平移到某个位置。缩放的本质是通过除以一个固定值,将数据固定在某个范围之中,取对数也算是一种缩放处理。
(1)preprocessing.MinMaxScaler类
在scikit-learn中,可以通过数据MinMaxScaler来调整数据尺度,将属性缩放到一个指定的范围,或者将数据标准化并将数据都聚集到0附近,方差为1。即先将数据移动了$min(x)$个单位,按极差$max(x)-min(x)$缩放,则数据会被收敛到[0,1]之间。而这个过程,就叫做数据归一化。归一化之后的数据服从正态分布,公式如下:
from sklearn.preprocessing import MinMaxScaler #导入类
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]] #测试数据准备
scaler = MinMaxScaler() #实例化
scaler = scaler.fit(data) #fit,在这里本质是生成min(x)和max(x)
result = scaler.transform(data) #通过接口导出结果
result
array([[0. , 0. ], [0.25, 0.25], [0.5 , 0.5 ], [1. , 1. ]])
result_ = scaler.fit_transform(data) #训练和导出结果一步达成
result
array([[0. , 0. ], [0.25, 0.25], [0.5 , 0.5 ], [1. , 1. ]])
scaler.inverse_transform(result) #将归一化后的结果逆转
array([[-1. , 2. ], [-0.5, 6. ], [ 0. , 10. ], [ 1. , 18. ]])
#使用MinMaxScaler的参数feature_range实现将数据归一化到[0,1]以外的范围中
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
scaler = MinMaxScaler(feature_range=[5,10]) #依然实例化
result = scaler.fit_transform(data) #fit_transform一步导出结果
result
array([[ 5. , 5. ], [ 6.25, 6.25], [ 7.5 , 7.5 ], [10. , 10. ]])
(2)preprocessing.StandardScaler类
正态化数据是有效的处理符合高斯分布的数据的手段,输出结果以0为中位数,方差为1,并作为假定数据符合高斯分布的算法的输入。当数据$x$按均值$\mu$中心化后,再按标准差$\sigma$缩放,数据就会服从为均值为0,方差为1的正态分布(即标准正态分布),而这个过程,就叫做数据标准化(Standardization,又称Z-score normalization),公式如下:
from sklearn.preprocessing import StandardScaler #导入包
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]] #测试数据准备
scaler = StandardScaler() #实例化
scaler.fit(data) #fit,本质是生成均值和方差
StandardScaler(copy=True, with_mean=True, with_std=True)
scaler.mean_ #查看均值的属性mean_
array([-0.12, 9. ])
scaler.var_ #查看方差的属性var_
array([ 0.55, 35. ])
x_std = scaler.transform(data) #通过接口导出结果
x_std
array([[-1.18, -1.18], [-0.51, -0.51], [ 0.17, 0.17], [ 1.52, 1.52]])
x_std.mean() #导出的结果是一个数组,用mean()查看均值
0.0
x_std.std() #用std()查看方差
1.0
scaler.fit_transform(data) #使用fit_transform(data)一步达成结果
array([[-1.18, -1.18], [-0.51, -0.51], [ 0.17, 0.17], [ 1.52, 1.52]])
scaler.inverse_transform(x_std) #使用inverse_transform逆转标准化
array([[-1. , 2. ], [-0.5, 6. ], [ 0. , 10. ], [ 1. , 18. ]])
注意:
1.对于StandardScaler和MinMaxScaler来说,空值NaN会被当做是缺失值,在fit的时候忽略,在transform的时候保持缺失NaN的状态显示。
2.fit接口中,依然只允许导入至少二维数组,一维数组导入会报错。
3、缺失值处理¶
数据处理过程中,不可避免的会出现很多空值,这时就需要我们通过业务经验选择性的进行数值填充,尽量保证模型的准确性。
参数说明:
class sklearn.impute.SimpleImputer(missing_values=nan, strategy=’mean’, fill_value=None, verbose=0, copy=True)
1.missing_values:告诉SimpleImputer,数据中的缺失值长什么样,默认空值np.nan
2.strategy:我们填补缺失值的策略,默认均值。
-
输入“mean”使用均值填补(仅对数值型特征可用)
-
输入“median"用中值填补(仅对数值型特征可用)
-
输入"most_frequent”用众数填补(对数值型和字符型特征都可用)
-
输入“constant"表示请参考参数“fill_value"中的值(对数值型和字符型特征都可用)
3.fill_value:当参数startegy为”constant"的时候可用,可输入字符串或数字表示要填充的值,常用0。
4.copy:默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中去。
#缺失值处理代码
from sklearn.impute import SimpleImputer
import pandas as pd
#导入数据
filename = 'pima_indian.csv'
names = ['preg','plas','pres','skin','test','mass','pedi','age','class']
data = pd.read_csv(filename,names=names)
data.head()
#以填补年龄为例,sklearn当中特征矩阵必须是二维
Age = data.loc[:,"age"].values.reshape(-1,1)
Age[0:5]
array([[50], [31], [32], [21], [33]], dtype=int64)
#实例化,默认均值填补
imp_mean = SimpleImputer()
imp_mean = imp_mean.fit_transform(Age)
data.loc[:,"age"] = imp_median
#用中位数填补
imp_median = SimpleImputer(strategy="median")
imp_median = imp_median.fit_transform(Age)
data.loc[:,"age"] = imp_median
#用众数填补
imp_median = SimpleImputer(strategy="most_frequent")
imp_median = imp_median.fit_transform(Age)
data.loc[:,"age"] = imp_median
#用0填补
imp_0 = SimpleImputer(strategy="constant",fill_value=0)
imp_0 = imp_0.fit_transform(Age)
data.loc[:,"age"] = imp_0
当然,此处均已年龄这个特征为例进行填充,实际应用的时候要根据业务经验来进行选择填充缺失值的方案。
4、分类型特征处理¶
由于很多的机器学习算法是计算数值型数据的,但现实情况下特征数据中包含很多文本数据,又不能忽略掉。比如说学历的取值可以是["小学",“初中”,“高中”,"大学"]。在这种情况下,为了让数据适应算法和库,我们必须将数据进行编码,即是说,将文字型数据转换为数值型。
首先介绍下编码变量类型:
名义变量:变量取值是相互独立的,彼此之间完全没有联系。比如性别男和性别女。
有序变量:变量取值不完全独立的,彼此之间有一些联系,但没有计算关系。比如本科、硕士、博士。
有距变量:各个取值之间是联系的,且是可以互相计算的。比如重量10kg、20kg、30kg。
(1)preprocessing.LabelEncoder类
标签专用,能够将分类转换为分类数值,是对不连续的数字或者文本进行编号(连续的会是同一个编号)。
from sklearn.preprocessing import LabelEncoder
import pandas as pd
#导入数据
filename = 'info.csv'
names = ['segment','weight','building','class']
data = pd.read_csv(filename,names=names,index_col=0)
data.head()
#LabelEncoder编码标签
y = data.iloc[:,-1] #要输入的是标签,不是特征矩阵,所以允许一维
le = LabelEncoder() #实例化
le = le.fit(y) #导入数据
label = le.transform(y) #transform接口调取结果
label
array([0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0])
le.classes_ #属性.classes_查看标签中究竟有多少类别
array(['否', '是'], dtype=object)
data.iloc[:,-1] = label #让标签等于我们运行出来的结果
(2)preprocessing.OrdinalEncoder类
特征专用,能够将分类特征转换为分类数值,其主要处理有序变量。
#OrdinalEncoder编码有序变量{青年,中年,老年}
Ordinal = OrdinalEncoder()
Ordinal = Ordinal.fit(data.iloc[:,0:1])
Ordinal.categories_ #接口categories_对应LabelEncoder的接口classes_,一模一样的功能
[array(['中年', '老年', '青年'], dtype=object)]
data.iloc[:,0:1] = OrdinalEncoder().fit_transform(data.iloc[:,0:1]) #让特征数据等于我们运行出来的结果
(3)preprocessing.OneHotEncoder类
独热编码,创建哑变量,其处理名义变量
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(categories='auto')
enc.fit(data.iloc[:,1:3])
ans = enc.transform(data.iloc[:,1:3]).toarray()
ans[0:5] #显示编码后的前5行
array([[1., 0., 0., 1., 0.], [0., 1., 0., 1., 0.], [0., 0., 1., 1., 0.], [0., 0., 1., 0., 1.], [0., 1., 0., 1., 0.]])
参数说明:
OneHotEncoder(n_values=’auto’, categorical_features=’all’, dtype=<class ‘numpy.float64’>, sparse=True, handle_unknown=’error’)
1.n_values=’auto’,表示每个特征使用几维的数值由数据集自动推断,即几种类别就使用几位来表示。可以设定维度比正常维度多,将未编码到的数据分类到一起。
2.categorical_features = 'all',这个参数指定了对哪些特征进行编码,默认对所有类别都进行编码。
3.dtype=<class ‘numpy.float64’> 表示编码数值格式,默认是浮点型。
4.sparse=True 表示编码的格式,默认为True,即为稀疏的格式,指定False 则就不用 toarray() 了。
5.handle_unknown=’error’,其值可以指定为 "error" 或者 "ignore",即如果碰到未知的类别,是返回一个错误还是忽略它。
5、连续型特征处理¶
(1)sklearn.preprocessing.Binarizer类
根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量。大于阈值的值映射为1,而小于或等于阈值的值映射为0。默认阈值为0时,特征中所有的正值都映射到1。二值化是对文本计数数据的常见操作,分析人员可以决定仅考虑某种现象的存在与否。它还可以用作考虑布尔随机变量的估计器的预处理步骤(例如,使用贝叶斯设置中的伯努利分布建模)。
from sklearn import preprocessing
import numpy as np
# 创建一组特征数据,每一行表示一个样本,每一列表示一个特征
x = np.array([[1., -1., 2.],
[2., 0., 0.],
[0., 1., -1.]])
binarizer = preprocessing.Binarizer().fit(x)
binarizer.transform(x)
array([[1., 0., 1.], [1., 0., 0.], [0., 1., 0.]])
#当然也可以自己设置这个阀值,只需传出参数threshold即可
binarizer = preprocessing.Binarizer(threshold=1.5)
binarizer.fit_transform(x)
array([[0., 0., 1.], [1., 0., 0.], [0., 0., 0.]])
(2)preprocessing.KBinsDiscretizer
这是将连续型变量划分为分类变量的类,能够将连续型变量排序后按顺序分箱后编码。总共包含三个重要参数:
1.n_bins:每个特征中分箱的个数,默认5,一次会被运用到所有导入的特征
2.encode:编码的方式,默认onehot。
-
onehot:做哑变量,之后返回一个稀疏矩阵,每一列是一个特征中的一个类别,含有该 类别的样本表示为1,不含的表示为0
-
ordinal:每个特征的每个箱都被编码为一个整数,返回每一列是一个特征,每个特征下含 有不同整数编码的箱的矩阵
-
onehot-dense:做哑变量,之后返回一个密集数组。
3.strategy:用来定义箱宽的方式,默认quantile。
-
uniform:表示等宽分箱,即每个特征中的每个箱的最大值之间的差为 (特征.max() - 特征.min())/(n_bins)
-
quantile:表示等位分箱,即每个特征中的每个箱内的样本数量都相同
-
kmeans:表示按聚类分箱,每个箱中的值到最近的一维k均值聚类的簇心得距离都相同
from sklearn.preprocessing import KBinsDiscretizer
import numpy as np
# 创建一组特征数据,每一行表示一个样本,每一列表示一个特征
X = np.array([[1., -1., 2.],
[2., 0., 0.],
[0., 1., -1.]])
est = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')
est.fit_transform(X)
array([[1., 0., 2.], [2., 1., 1.], [0., 2., 0.]])
#查看转换后分的箱:变成了一列中的三箱
set(est.fit_transform(X).ravel())
{0.0, 1.0, 2.0}
est = KBinsDiscretizer(n_bins=3, encode='onehot', strategy='uniform')
#查看转换后分的箱:变成了哑变量
est.fit_transform(X).toarray()
array([[0., 1., 0., 1., 0., 0., 0., 0., 1.], [0., 0., 1., 0., 1., 0., 0., 1., 0.], [1., 0., 0., 0., 0., 1., 1., 0., 0.]])