数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限。而特征工程就是使机器学习算法达到最佳性能的特征处理的过程,一般包含了Data数据清洗、数据预处理、特征提取、特征构造、特征选择等内容,本篇主要讨论数据预处理的方法及实现。
而数据预处理主要包含数据标准化、数据特征编码、统计变换等内容,那数据预处理的意义是什么呢?
1. 标准化处理,消除量纲对数据的影响
在实际应用中,样本不同特征的单位不同,会在求距离时造成很大的影响。比如:在两个样本中年龄大小分别为20和25,发现时间分别为180天和360天,那么在求距离时,时间差为180、大小差为5,那么其结果会被时间所主导,因为年龄大小的差距太小了。但是如果我们把时间用年做单位,0.5年与1年的差距又远小于年龄大小的差距,结果又会被年龄大小所主导。
为了避免上述问题对结果造成影响,我们需要对数据做无量纲化处理(标准化)。无量纲化不是说把天数变成年数,而是说,无论是天数还是年数,最后都会变成1,也就是没有了单位。常见的无量纲化方法有标准化和归一化两种。
2. 数据分布更稳定
现实中很多数据的分布是倾斜的,数据分布与均值关系密切,方差不稳定,通过统计变换使原本密集的区间的值尽可能的分散,原本分散的区间的值尽量的聚合,帮助稳定方差等
3.模型算法的需要
大部分算法是基于向量空间中的度量来进行计算的,所以需要数值变量进行距离的计算,所以需要对对不连续的数字或者文本进行编码
1.1 Z-Score标准化
基于原始数据的均值(mean)和标准差(standarddeviation)进行数据标准化,标准化后,其转换成均值为0,方差为1的标准正态分布。
# z-score标准化方法
# 1.多列同时处理
def scale_encoder(col_x):
x = preprocessing.scale(col_x)
print(x, '\n', np.mean(x), np.std(x))
return x
scale_encoder(i_x)
# 2.单列处理
def zscore_encoder(col_x):
average = float(sum(col_x)) / len(col_x)
new_x = [(x - average) / np.std(col_x) for x in col_x]
print(new_x, '\n', np.mean(new_x), np.std(new_x))
return new_x
zscore_encoder(i_x[:, 0])
优点:
- 需要消除样本不同属性具有不同量级(大小)时的影响,特别是依赖于样本距离的算法对于数据的数量级非常敏感。
- 标准化有可能提高精度。数量级的差异将导致量级较大的属性占据主导地位,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。
- 数量级的差异将导致迭代收敛速度减慢。当使用梯度下降法寻求最优解时,很有可能走“之字型”路线(垂直等高线走),从而导致需要迭代很多次才能收敛。
- 适应某些算法的要求,他们要求样本具有零均值和单位方差。
缺点:
- 对于数据分布有一定要求,近似正态分布的数据是最好的。
- 标准化处理需要总体的平均值与方差,但是在真实的分析与挖掘中很难获取,多用样本的均值与标准差替代。
- 消除了数据的实际意义,如年龄和收入与他们各自的标准化后分数不再有关系,因此Z-Score的结果只能用于比较数据间的结果,数据的真实意义还需要还原到原值。
1.2 MinMax归一化
也叫离差标准化或极差标准化,利用了边界值信息,将属性缩放到[0,1]区间
# min-max标准化(Min-Max Normalization)
def min_max_norm(col_x):
print(col_x)
preprocessing_min_max = preprocessing.MinMaxScaler()
x = preprocessing_min_max.fit_transform(col_x)
return x
min_max_norm(i_x)
def min_max_encoder(col_x):
print(col_x)
new_x = [(x - min(col_x))/(max(col_x) - min(col_x)) for x in col_x]
return new_x
min_max_encoder(i_x[:, 0])
优点:
- 归一化消除了量纲对数据的影响,有可能提高精度
- 归一化后加快梯度下降的速度,算法收敛速度更快
缺点:
- 当有新数据加入时,可能导致max和min的变化,需要重新定义
- 对异常值的存在非常敏感。
标准化与归一化却别对比
相同点:
它们的相同点在于都能取消由于量纲不同引起的误差,都是一种线性变换,都是对向量X按照比例压缩再进行平移。
不同点:
- 归一化是为了消除纲量,数据被压缩到[0,1]区间。 标准化只是调整特征整体的分布,调整成均值为0方差为1的标准正态分布
- 归一化与最大,最小值有关。 标准化与均值,标准差有关。
- 归一化输出在[0,1]之间。 标准化无限制。
适用场景:
- 如果数据存在异常值和较多噪音,用标准化,可以间接通过中心化避免异常值和极端值的影响。在SVM、KNN等分类、聚类算法中,需要使用距离来度量相似性的时候,也是用标准化Z-score表现更好。
- 如果对输出结果范围有要求、数据较为稳定,不存在极端的最大最小值、数据不符合正太分布或不涉及距离计算时,用归一化。
- 如基于参数的模型或者基于距离的模型,因为需要对参数或者距离进行计算,都需要进行归一化。
- 而决策树、随机森林,bagging与boosting等基于树的方法不需要进行特征的归一化。
1.3 正态分布化(Normalization)
# 规范化(Normalization)
def normalize_encoder(col_x):
print(col_x)
x1 = preprocessing.normalize(col_x, norm='l1')
x2 = preprocessing.normalize(col_x, norm='l2')
return x1, x2
normalize_encoder(i_x)
- 正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1)。Normalization主要思想是对每个样本计算其p-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后样本的p-范数(l1-norm,l2-norm)等于1。
- 如果要使用如二次型(点积)或者其它核方法计算两个样本之间的相似性这个方法会很有用。该方法是文本分类和聚类分析中经常使用的向量空间模型(VectorSpace Model)的基础。
2 统计变换
- 如利用Log变换来减轻数据分布倾斜的影响,使原本密集的区间的值尽可能的分散,原本分散的区间的值尽量的聚合,稳定方差,始终保持分布接近于正态分布并使得数据与分布的平均值无关。
# 对数变换
def log_encoder(col, logn):
print(col)
if logn == 2:
new_x = [math.log2(x) for x in col]
elif logn == 10:
new_x = [math.log10(x) for x in col]
else:
print('暂不支持')
return new_x
log_encoder(i_x[:, 0], logn=10)
3.1 标签编码(LabelEncode)
- 对不连续的数字或者文本进行编号,编码值介于0和n_classes-1之间的标签。但也隐含了一个不同的类别之间的默认顺序关系
def label_encoder(data):
print(data)
le = preprocessing.LabelEncoder()
le.fit(data)
t = le.transform(data)
return t
label_encoder(i_x[:, 0])
3.2 标签二值化(LabelBinarizer)
- 与OneHotEncoder一样,但是OneHotEncode只能对数值型变量二值化,无法直接对字符串型的类别变量编码,而LabelBinarizer可以直接对字符型变量二值化。
# 标签二值化(Label binarization)
def label_encoder(col_y):
lb = preprocessing.LabelBinarizer()
y = lb.fit_transform(col_y)
return y
label_encoder(['a','b','c'])
3.3 独热编码(OneHotEncode)
- 用于将表示分类的数据扩维,设置一个个数与类型数量相同的全0数组,每一位对应一个类型,如该位为1,该数字表示该类型。OneHotEncode只能对数值型变量二值化,无法直接对字符串型的类别变量编码。
# 独热编码
def onehot_encoder(data):
print(data)
enc = preprocessing.OneHotEncoder(sparse=False, categories='auto')
t = enc.fit_transform(data)
return t
onehot_encoder(i_x)
优点:
- 将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理,解决了分类器不好处理离散数据的问题,它的值只有0和1,不同的类型存储在垂直的空间。
缺点:
- 当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hotencoding+PCA这种组合在实际中也非常有用。
3.4 二值化(Binarization)
- 二值化可以将数值型(numerical)的特征进行阀值化得到boolean型数据。这对于下游的概率估计来说可能很有用(比如:数据分布为Bernoulli分布时)。
- 定量特征二值化的核心在于设定一个阈值,大于阈值的赋值为1,小于等于阈值的赋值为0。
# 特征二值化(Binarization)
def binary_encoder(data):
print(data)
binarizer = preprocessing.Binarizer(threshold=4)
x = binarizer.transform(data)
return x
binary_encoder(i_x)