Press "Enter" to skip to content

机器学习-简单的数据预处理和特征工程

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

在实际应用中,样本的不同特征的单位不同,会在求距离时造成很大的影响。比如:在两个样本中肿瘤大小的分别为1cm和5cm,发现时间分别为100天和200天,那幺在求距离时,时间差为100、大小差为4,那幺其结果会被时间所主导,因为肿瘤大小的差距太小了。但是如果我们把时间用年做单位,0.27年与0.55年的差距又远小于肿瘤大小的差距,结果又会被大小主导了。

 

我们发现,在量纲不同的情况下,以上的情况,不能反映样本中每一个特征的重要程度。这就需要数据归一化了。

 

一般来说,我们的解决方案是:把所有的数据都映射到同一个尺度(量纲)上。

 

一般来说,常用的数据归一化有两种:

 

最值归一化(normalization):把所有数据映射到0-1之间。最值归一化的使用范围是特征的分布具有明显边界的(分数0~100分、灰度0~255),受outlier的影响比较大

 

均值方差归一化(standardization):把所有数据归一到均值为0方差为1的分布中。适用于数据中没有明显的边界,有可能存在极端数据值的情况.

 

1.2最值归一化实现

 

import numpy as np
# 创建100个随机数
x = np.random.randint(0,100,size=100)
# 最值归一化(向量)
# 最值归一化公式,映射到0,1之间
(x - np.min(x)) / (np.max(x) -  np.min(x))
# 最值归一化(矩阵)
# 0~100范围内的50*2的矩阵
X = np.random.randint(0,100,(50,2))
# 将矩阵改为浮点型
X = np.array(X, dtype=float)
# 最值归一化公式,对于每一个维度(列方向)进行归一化。
# X[:,0]第一列,第一个特征
X[:,0] = (X[:,0] - np.min(X[:,0])) / (np.max(X[:,0]) - np.min(X[:,0]))
# X[:,1]第二列,第二个特征
X[:,1] = (X[:,1] - np.min(X[:,1])) / (np.max(X[:,1]) - np.min(X[:,1]))
# 如果有n个特征,可以写个循环:
for i in range(0,2):
    X[:,i] = (X[:,i]-np.min(X[:,i])) / (np.max(X[:,i] - np.min(X[:,i])))

下面我们可以简单地绘制样本,并使用np.mean()/np.std()来计算其均值和方差

 

import matplotlib.pyplot as plt
# 简单绘制样本,看横纵坐标
plt.scatter(X[:,0],X[:,1])
plt.show()

1.3 均值方差归一化实现

 

同样地,为了了解均值方差归一化的代码实现,我们可以创建100个随机数,然后对其进行均值方差归一化。

 

X2 = np.array(np.random.randint(0,100,(50,2)),dtype=float)
# 套用公式,对每一列做均值方差归一化
for i in range(0,2):
    X2[:,i]=(X2[:,i]-np.mean(X2[:,i])) / np.std(X2[:,i])

 

下面我们可以简单地绘制样本

 

import matplotlib.pyplot as plt
plt.scatter(X2[:,0],X2[:,1])
plt.show()

计算其均值/

 

np.mean(X2[:,0])
np.std(X2[:,1])

结果为:

 

-3.3306690738754695e-17
1.0

 

1.4 Sklearn中的归一化

 

首先我们来看一个在实际使用归一化时的一个小陷阱。

 

我们在建模时要将数据集划分为训练数据集&测试数据集。

 

训练数据集进行归一化处理,需要计算出训练数据集的均值mean_train和方差std_train。

 

问题是:我们在对测试数据集进行归一化时,要计算测试数据的均值和方差幺?

 

答案是否定的。在对测试数据集进行归一化时,仍然要使用训练数据集的均值train_mean和方差std_train。这是因为测试数据是模拟的真实环境,真实环境中可能无法得到均值和方差,对数据进行归一化。只能够使用公式(x_test – mean_train) / std_train并且,数据归一化也是算法的一部分,针对后面所有的数据,也应该做同样的处理.

 

因此我们要保存训练数据集中得到的均值和方差。

 

在sklearn中专门的用来数据归一化的方法:StandardScaler。

 

下面我们加载鸢尾花数据集

 

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,test_size=0.2,random_state=666)

使用数据归一化的方法:

 

from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
# 归一化的过程跟训练模型一样
standardScaler.fit(X_train)
standardScaler.mean_
standardScaler.scale_   # 表述数据分布范围的变量,替代std_
# 使用transform
X_train_standard = standardScaler.transform(X_train)
X_test_standard = standardScaler.transform(X_test)

如此就能输出归一化后的数据了。

 

1.5 自己实现均值方差归一化

 

同样地,我们仿照sklearn的风格,可以自己实现一下均值方差归一化的方法。

 

我们在之前的工程中创建processing.py:

 

import numpy as np
class StandardScaler:
    def __init__(self):
        self.mean_ = None
        self.scale_ = None
    def fit(self, X):
        """根据训练数据集X获得数据的均值和方差"""
        assert X.ndim == 2, "The dimension of X must be 2"
        # 求出每个列的均值
        self.mean_ = np.array([np.mean(X[:,i] for i in range(X.shape[1]))])
        self.scale_ = np.array([np.std(X[:, i] for i in range(X.shape[1]))])
        return self
    def tranform(self, X):
        """将X根据StandardScaler进行均值方差归一化处理"""
        assert X.ndim == 2, "The dimension of X must be 2"
        assert self.mean_ is not None and self.scale_ is not None, \
        "must fit before transform"
        assert X.shape[1] == len(self.mean_), \
        "the feature number of X must be equal to mean_ and std_"
        # 创建一个空的浮点型矩阵,大小和X相同
        resX = np.empty(shape=X.shape, dtype=float)
        # 对于每一列(维度)都计算
        for col in range(X.shape[1]):
            resX[:,col] = (X[:,col] - self.mean_[col]) / self.scale_[col]
        return resX

 

1.6 kNN优缺点

 

KNN的主要优点有:

 

理论成熟,思想简单,既可以用来做分类也可以用来做回归 天然解决多分类问题,也可用于回归问题 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合 KNN的主要缺点有:

 

计算量大,效率低。即使优化算法,效率也不高。 高度数据相关,样本不平衡的时候,对稀有类别的预测准确率低 相比决策树模型,KNN模型可解释性不强 维度灾难:随着维度的增加,“看似相近”的两个点之间的距离越来越大,而knn非常依赖距离

 

维数	点到点	距离
1维	0到1的距离	1
2维	(0,0)到(1,1)的距离	1.414
3维	(0,0,0)到(1,1,1)的距离	1.73
64维	(0,0,...0)到(1,1,...1)	8
10000维	(0,0,...0)到(1,1,...1)	100

 

二.缺失值处理

 

缺失数据的处理方法主要有三大类:删除元组、数据补齐、不处理 删除元组:也就是将存在遗漏信息属性值的对象(元组,记录)删除,从而得到一个完备的信息表。 优点:简单易行,在对象有多个属性缺失值、被删除的含缺失值的对象与初始数据集的数据量相比非常小的情况下非常有效。

 

不足:当缺失数据所占比例较大,特别当遗漏数据非随机分布时,这种方法可能导致数据发生偏离,从而引出错误的结论。

 

数据补齐:用一定的值去填充空值,从而使信息表完备化。通常基于统计学原理,根据初始数据集中其余对象取值的分布情况来对一个缺失值进行填充。 数据挖掘中常用的有以下几种补齐方法:

 

1.人工填写(filling manually)

 

2.特殊值填充(Treating Missing Attribute values as Special values)

 

将空值作为一种特殊的属性值来处理,它不同于其他的任何属性值。如所有的空值都用“unknown”填充。一般作为临时填充或中间过程。

 

3.平均值填充(Mean/Mode Completer)

 

将初始数据集中的属性分为数值属性和非数值属性来分别进行处理。

 

如果空值是数值型的,就根据该属性在其他所有对象的取值的平均值来填充该缺失的属性值; 如果空值是非数值型的,就根据统计学中的众数原理,用该属性在其他所有对象的取值次数最多的值(即出现频率最高的值)来补齐该缺失的属性值。

 

与其相似的另一种方法叫条件平均值填充法(Conditional Mean Completer)。在该方法中,用于求平均的值并不是从数据集的所有对象中取,而是从与该对象具有相同决策属性值的对象中取得。 例如泰安尼克生存预测数据集里的Embarked变量,缺失这一变量的两个乘客Pclass和Fare相同,所以可以用Pclass和Fare与其相同的乘客这一集合的众数来确定Embarked的值。

 

这两种数据的补齐方法,其基本的出发点都是一样的,以最大概率可能的取值来补充缺失的属性值,只是在具体方法上有一点不同。与其他方法相比,它是用现存数据的多数信息来推测缺失值。

 

4.热卡填充(Hot deck imputation,或就近补齐)

 

对于一个包含空值的对象,热卡填充法在完整数据中找到一个与它最相似的对象,然后用这个相似对象的值来进行填充。不同的问题可能会选用不同的标准来对相似进行判定。该方法概念上很简单,且利用了数据间的关系来进行空值估计。这个方法的缺点在于难以定义相似标准,主观因素较多。 例如Fare变量的缺失值处理可以用这一方法,找到与该乘客信息相似度最高的乘客,Pclass船舱级别、Embarked登船港口占更大权重。

 

5.最近距离邻法(K-means clustering)

 

先根据欧式距离或相关分析来确定距离具有缺失数据样本最近的K个样本,将这K个值加权平均来估计该样本的缺失数据。

 

6.使用所有可能的值填充(Assigning All Possible values of the Attribute)

 

用空缺属性值的所有可能的属性取值来填充,能够得到较好的补齐效果。但是,当数据量很大或者遗漏的属性值较多时,其计算的代价很大,可能的测试方案很多。如果没有任何可以借助的变量或可参考变量作用很低时可以采用这个方法,方便简单。

 

7.回归(Regression)

 

基于完整的数据集,建立回归方程。对于包含空值的对象,将已知属性值代入方程来估计未知属性值,以此估计值来进行填充。当变量不是线性相关时会导致有偏差的估计。常用线性回归。

 

8.期望值最大化方法(Expectation maximization,EM)

 

EM算法是一种在不完全数据情况下计算极大似然估计或者后验分布的迭代算法。在每一迭代循环过程中交替执行两个步骤:E步(Excepctaion step,期望步),在给定完全数据和前一次迭代所得到的参数估计的情况下计算完全数据对应的对数似然函数的条件期望;M步(Maximzation step,极大化步),用极大化对数似然函数以确定参数的值,并用于下步的迭代。算法在E步和M步之间不断迭代直至收敛,即两次迭代之间的参数变化小于一个预先给定的阈值时结束。该方法可能会陷入局部极值,收敛速度也不是很快,并且计算很复杂。

 

三 处理分类型特征:编码与哑变量

 

在机器学习的特征处理环节,免不了需要用到类别型特征,这类特征进入模型的方式与一般数值型变量有所不同。

 

通常根据模型的需要,类别型特征需要进行哑变量处理,即按照特征类别进行编码,一般一个类别为k的特征需要编码为一组k-1【避免引起多重共线性】个衍生哑变量,这样就可以表示特征内部所有的类别(将其中基准比较类设为0,当k-1个哑变量都为0时,即为基准类)。

 

这种哑变量的编码过程在R和Python中的有成熟的方案,而无需我们手动进行编码,使用成熟的编码方案可以提升特征处理的过程。

 

R语言哑变量处理:

 

data(iris)

 

这里仍以iris数据集为例,假设这里的Species变量是要进入模型的其中一个自变量,在建模前需要对齐进行哑变量处理。

 

方法一——dummy包:

 

library(“dummy”) dumy <- dummy(x=iris) dummy函数会自动检查你输入数据集对象中的字符型/因子型变量,并全量输出字符型/因子型变量的哑变量编码结果。注意这里编码结果是全量输出,即类别型特征的每一个类别都有一个编码后的特征。为了编码引起多重共线性,我们需要舍弃一个(代表比较基准类的特征),这里Species类别变量一共有三个类别:setosa、versicolor 、virginica,各自都有一个对应编码变量,当原始类别变量取对应类别时,则对应类别哑变量位置取值为1,否则为0.

 

假设这里我们想要对比的基准类是setosa,只需要保留versicolor、virginica对应的编码后变量。那幺当versicolor、virginica都取值为0时,则代表取值为setosa。

 

最终我们要将保留的哑变量与原始数据集合并,以备之后其他特征处理环节需要。

 

iris_data <- cbind(iris,dumy[,-1])

 

此时就可以完美的用Species_versicolor、Species_virginica这两个新生成的哑变量来代表原始分类变量Species了。

 

方法二——model.matrix函数: R语言内置包stat中有一个model.matrix函数(无需单独加载既可用),它可以处理分类变量的哑变量处理过程,语法非常简单。

 

dumy <- model.matrix( ~ Species -1, data = iris) iris_data <- cbind(iris,dumy[,-1])

 

这里需要在表达式中设定消除截距【公式中减一,否则输出的哑变量带有截距项】,选择的时候同上,只取比较基准类之外的所有哑变量。

 

方法三——caret包中的dummyVars函数:

 

library(“caret”) dumy <- dummyVars(~gender,data=customers) trfs <- predict(dumy,newdata=customers)

 

iris_data <- iris %>% dummyVars(~Species,.) %>% predict(iris) %>% .[,-1] %>% cbind(iris,.) 选择规则同上。

 

Python中的哑变量处理工具:

 

from sklearn.preprocessing import Imputer,LabelEncoder,OneHotEncoder from sklearn import preprocessing from sklearn.model_selection import train_test_split from sklearn.datasets import load_iris import pandas as pd import numpy as np 方案一——:sk-learn中的OneHotEncoder方法:

 

iris = load_iris() data = iris[‘data’] iris_data = pd.DataFrame( data = data, columns = [‘sepal_length’,’sepal_width’,’petal_length’,’petal_width’] ) iris_data[“Species”] = iris[ ‘target’] iris_data[“Species”] = iris_data[“Species”].map({0:”setosa”,1:”versicolor”,2:”virginica”})

 

labelencoder_X = LabelEncoder() iris_data[“Species_code”] = labelencoder_X.fit_transform(iris_data.iloc[:,4])

 

onehotencoder = OneHotEncoder(categorical_features = [0]) X = onehotencoder.fit_transform(iris_data[[“Species_code”]]).toarray()

 

iris_data = pd.DataFrame( data = np.hstack((iris_data.values,X[:,0:2])), columns = iris_data.columns.tolist() + [‘Species_versicolor’,’Species_virginica’] )

 

方案二——pandas中的get_dummies方法:

 

可以看到sk-learn中的OneHotEncoder方法必须保证处理的输入值是array,而且只能处理数值型(也就是数字编码之后的类别变量),无法直接处理仔字符型变量。

 

其实如果能够直接在数据框中处理完这一切就方便很多。

 

dummy = pd.get_dummies(iris_data.iloc[:,4],prefix = “Species”) iris_data = pd.concat([iris_data,dummy.iloc[:,0:2]], axis= 1)

 

pandas中的get_dummies方法提供了非常简单高效的哑变量处理方案,只有短短的一句代码即可。

 

回顾一下今天分享的哑变量处理知识点:

 

R语言:

 

方案一——:dummy包的dummy函数 方法二——:model.matrix函数 方法三——:caret包中的dummyVars函数 Python:

 

方法一——:caret包中的dummyVars函数 方案二——:pandas中的get_dummies方法

 

四 处理连续型特征:二值化与分段

 

6.1 二值化:preprocession.Binarizer(Threshold)

 

#导二值化包(特征专用) from sklearn.preprocessing import Binarizer ##提取年龄,要.reshape(-1,1)变为一列 data_2 = data.copy() data_2.iloc[:,0] X = data_2.iloc[:,0].values.reshape(-1,1) X[:,5] #将年龄二值化 transformer = Binarizer(threshold=30).fit_transform(X) #threshold = 30是阈值,大于30为1,小于30为0 transformer[:5] 6.2 分段:preprocessing.KBinsDiscretizer

 

n_bins :每个特征中分箱的个数,默认5,一次会被运用到所有导入的特征 encode:编码的方式,默认“onehot”,”encode=ordinal”:每个特征的每个箱都被编码为一个整数,返回每一列是一个特征,每个特征下含有不同整数编码的箱的矩阵”encode=onehot-dense”:做哑变量,之后返回一个密集数组 strategy:用来定义箱宽的方式,默认”quantile”,”uniform”:表示等宽分箱;”quantile”:表示等位分箱,即每个特征中的每个箱内的样本数量都相同;”kmeans”:表示按聚类分箱,每个箱中的值到最近的一维k均值聚类的簇心得距离都相同 #导入分箱包 from sklearn.preprocessing import KBinsDiscretizer #提取年龄并变为二维 X = data.iloc[:,0].values.reshape(-1,1)

 

#分箱(正常分箱) est = KBinsDiscretizer(n_bins=3, encode=’ordinal’, strategy=’uniform’) est.fit_transform(X)[:5]

 

set(est.fit_transform(X).ravel()) #检验是否分为三箱 set(est.fit_transform(X).flatten()) #ravel与flatten作用相同,均为降维(降至一维)

 

#分箱(独热编码) (每个类别数量不一定相等) est = KBinsDiscretizer(n_bins=3, encode=’onehot’, strategy=’uniform’) #查看转换后分的箱:变成了哑变量 est.fit_transform(X).toarray()

 

#分箱(独热编码)(每个类别数量基本相等) est = KBinsDiscretizer(n_bins=3, encode=’onehot’, strategy=’quantile’) #查看转换后分的箱:变成了哑变量,且每个类别数量基本相等 est.fit_transform(X).toarray()

 

est.fit_transform(X).toarray().sum(0) #查看每个类别是否数量一样 data[‘Age’].value_counts() #查看年龄分布

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注