特征工程——独热编码

到目前为止,我们一直假设数据是由浮点数组成的二维数组,其中每一列是描述数据点的连续特征。对于许多应用而言,数据的收集方式并不是这样。一种特别常见的特征类型就是分类特征,也叫离散特征。这种特征通常并不是数值。分类特征与连续特征之间的区别类似于分类和回归之间的区别,只是前者在输入端而不是输出端。其实,无论你的数据包含哪种类型的特征,数据表示方式都会对机器学习模型的性能产生巨大影响。譬如说我们之前讲过的数据缩放

对于某个特定的应用来说,如何找到最佳数据表示,这个问题被称为特征工程,它是数据科学家和机器学习从业者在尝试解决现实世界问题时的主要任务之一。用正确的方式表示数据,对监督模型性能的影响比所选择的精确参数还要大。

One-Hot编码(虚拟变量)

到目前为止,表示分类变量最常用的方法就是使用one-hot编码N取一编码,也叫虚拟变量。虚拟变量背后的思想是将一个分类变量替换为一个或多个新特征,新特征取值为0或1.对于线性二分类(以及scikit-learn中其他所有模型)的公式而言,0和1这两个值是有意义的,我们可以像这样对每个类别引入一个新特征,从而表示任意数量的类别。

接下来,我用实际数据来讲解一下独热编码。

数据来源

药品分类:https://www.kaggle.com/prathamtripathi/drug-classification

在这里插入图片描述

该数据集有200条特征记录,包含患者年龄,性别,血压水平 (BP),胆固醇水平,钠钾比共5个特征值,而我们所需要做的就是通过这些特征值,对患者的用药(Drug)进行分类。

数据处理

1.读取数据

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import winreg
###################
real_address = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
file_address=winreg.QueryValueEx(real_address, "Desktop")[0]
file_address+='\\'
file_origin=file_address+"\\源数据-分析\\drug200.csv"###https://www.kaggle.com/prathamtripathi/drug-classification
drug=pd.read_csv(file_origin)
#设立桌面绝对路径,读取源数据文件,这样将数据直接下载到桌面上就可以了,省得还要去找
###################

因为之前每次下载数据之后都要将文件转移到python根目录里面,或者到下载文件夹里面去读取,很麻烦。所以我通过winreg库,来设立绝对桌面路径,这样只要把数据下载到桌面上,或者粘到桌面上的特定文件夹里面去读取就好了,不会跟其它数据搞混。
其实到这一步都是在走流程,基本上每个数据挖掘都要来一遍,没什么好说的。

2.检查分类数据

读取完数据集之后,最好先检查每一列是否包含有意义的分类数据。在处理人工(比如用户调查)输入的数据时,可能没有固定的类别,拼写和大小写也存在差异,因此可能需要预处理。比如,有人可能将性别填为”M“,有人可能填为”m“,虽然他们都代表男性,但是对于电脑来说,他们却是分属两个类别。

检查列的内容有一个好办法,就是使用pandas_Series的value_counts函数,以显示唯一值及其出现次数:

在这里插入图片描述

可以看到,在这个数据集中Drug刚好有5个值,这说明数据格式已经很好,可以用one-hot编码来表示。在实际应用中,你应该查看并检查所有列的值。为简洁起见,这里我们将跳过这一步。

独热编码

用pandas编码数据有一种非常简单的方法,就是使用get_dummies函数。get_dummies函数自动变换所有具有对象类型(比如字符串)的列或所有分类的列:

1
drug_dummies=pd.get_dummies(drug)

结果如下所示:

在这里插入图片描述

可以看到,连续特征Age和Na_to_K没有发生变化,而分类特征的每个可能取值都被扩展为一个新特征。实际上,就是利用多个新特征对一个分类特征进行编码(如Sex_F和Sex_M)。在机器学习中使用此数据时,我们将会删除原有特征,仅保证0-1特征(有点类似于统计学里面的离散变量)。

接下来,我们在变化后的数据集上训练一个Logistic回归分类器。

训练模型

之前,我在LogisticRegression算法中已经讲解过算法原理了,这里就不再赘述了,感兴趣的朋友可以点击链接去查看下。

我们已经知道Logistic回归利用下列公式进行预测,预测值为y:

y=w[0]*x[0]+w[1]x[1]+w[2]x[2]+…+w[p]x[p]+b

其中w[i]和b是从训练集中学到的系数,X[i]是输入特征。显然,只有当x[i]是数字时这个公式才有意义。所以,独热编码的作用就体现出来了。

接下来,我们开始训练模型:

1
2
3
4
5
6
7
8
9
10
11
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
train=pd.get_dummies(drug.drop(["Drug"],axis=1))####去掉目标变量之后,进行独热编码
clusters=drug["Drug"]###只是替换了训练特征,目标特征倒是不用独热编码,不然会替换原有特征,无法训练
###目标变量就和之前监督学习里面的训练一样,作为独立的一列变量存在就好,独热编码只是用来修改训练特征变量,就跟连续变量一样
X_train,X_test,y_train,y_test=train_test_split(train,clusters,random_state=1)
logistic=LogisticRegression(C=20,solver='lbfgs',max_iter=10000)
logistic.fit(X_train,y_train)
print("Logistic训练模型评分:"+str(accuracy_score(y_train,logistic.predict(X_train))))
print("Logistic待测模型评分:"+str(accuracy_score(y_test,logistic.predict(X_test))))

结果如下所示:

在这里插入图片描述

在这个例子中,我们对同时包含训练数据和测试数据的数据框调用get_dummies。这一点很重要,可以确保训练集和测试集中分类变量的表示方式相同。

数字可以编码分类变量

在上面的例子中,分类变量被编码为字符串。一方面,可能会有拼写错误;但另一方面,它明确地将一个变量标记为分类变量。无论是为了便于存储还是因为数据的收集方式,分类变量通常被编码为整数。例如,在性别Sex这一特征中,用1指代男性,用2指代女性等等。现在该列包含数字1和2,而不是像“F”和“M”这样的字符串。如果有人观察表示数据集的表格,很难一眼看出这个变量应该被视为连续变量还是分类变量。但是,如果知道这些数字表示的是性别,那么很明显它们是不同的状态,不应该用单个连续变量来建模。

pandas的get_dummies函数将所有数字看作是连续的,不会为其创建虚拟变量。为了解决这个问题,你也可以使用scikit-learn的OneHotEncoder,指定哪些变量是连续的,哪些变量是离散的,你也可以将数据框转换为字符串。为了说明这一点,我们创建一个两列的DataFrame对象,其中一列包含字符串,另一列包含整数:

在这里插入图片描述

如果使用get_dummies只会编码字符串特征,不会改变整数特征:

在这里插入图片描述

如果你向为“整数特征”这一列创建虚拟变量,可以使用columns参数显式地给出想要编码的列。于是两个特征都会被当作分类特征处理:

在这里插入图片描述