一文凑齐四种变量转换方法!

在一份数据集中通常会遇见两类数据——数值型与类别型,数值型变量通常就是int、float类型,类别型变量就是object类型,也就是我们总说的字符型变量。如果更官方地讲,数值型变量被称作定量变量、类别型变量被称作定性变量。

数值型变量主要体现在连续值和离散值:

  • 连续值:体温、房屋面积等
  • 离散值:人数、个数等

我们都知道在大多数机器学习算法中都要与"距离"多多少少都会有些关系,所以只允许传入数值型变量,在不需要做其它处理的前提下,原始数据集中的数值型变量都是可以直接使用的,典型的算法代表有支持向量机、逻辑回归、KNN算法等。

当然也有算法是可以处理字符型变量的,比如朴素贝叶斯、决策树、随机森林等,这类模型不关心变量的取值,而是注重变量间的分布状况和变量之间的条件概率,但如果要通过sklearn调用这些算法的API时,规定也必须要传入数值型变量才合法。所以这个时候通常会面对一个问题,如何将字符型变量转化成数值型变量呢?

LabelEncoder

我自己随意构建了一个数据集,共有五个特征,一个类标签,可以看到只有Age和Heat是数值型变量,其余四个皆为字符型(类别型)变量。如果不调用任何API要将一列字符型数据转化为数值型数据,可以利用自定义函数结合apply或map函数实现,先以标签变量Label举例。

def label(e):
    if e == '猛男':
        return 0
    if e == '美女':
        return 1
    else:
        return 2
Data["Label"] = Data["Label"].apply(label)

这种方式针对类别较少的特征还算可行,但是类别一多,if语句会写吐的,并且可观性极差极差!在sklearn中有专门处理类别标签的API,只需要索引出对应的标签变量并传入即可。

from sklearn.preprocessing import LabelEncoder
LabE = LabelEncoder()#实例化
label = LabE.fit_transform(Data1.iloc[:,-1])
Data1.iloc[:,-1] = label
#一行表示
#Data1.iloc[:,-1] = LabelEncoder().fit_transform(Data1.iloc[:,-1])

一行表示是上面的化简形式,最后的效果都是相同的,实例化之后还可以通过相应的方法获取标签变量中的类别。

LabE.classes_
'''
array(['猛男', '美女', '靓仔'], dtype=object)
'''

最后运行的结果如下:

OrdinalEncoder

处理过标签变量之后,自然也要处理特征中的,方法呢可以选择利用上文提及的自定义函数的方法,但很明显的弊端就是每一列都需要一个自定义函数,导致效率太低了。sklearn中也有处理特征中为类别型变量的API,使用方法与上文大体相同:

扫描二维码关注公众号,回复: 11577791 查看本文章
from sklearn.preprocessing import OrdinalEncoder
Data2 = Data1.copy()
OrE = OrdinalEncoder()#实例化
Data2.iloc[:,2:-1] = OrE.fit_transform(Data2.iloc[:,2:-1])
#Data2.iloc[:,2:-1] = OrdinalEncoder().fit_transform(Data2.iloc[:,2:-1])

与LabelEncoder最大不同之处就是OrdinalEncoder要求传入数据不能为一维,意思就是如果你只想转换一个特征(上面代码为三个),那么你需要通过reshape(-1,1)将一维转换为二维再传入,而LabelEncoder是只针对标签变量的方法,所以自然接受一维数据。

OrdinalEncoder是没有class_方法的,取而替之的是categories_,作用也是获取传入数据每个特征的类别。

OrE.categories_
'''
[array(['女', '男'], dtype=object),
 array(['优', '差', '良'], dtype=object),
 array(['>40kg', '>50kg', '>60kg'], dtype=object)]
'''

最后运行的结果如下:

OneHotEncoder

现在我们要考虑一个问题,就是我们这么转换变量真的合理吗?上面介绍了数值型变量,这里再来简要说一下类别型变量。

类别型变量按照变量间是否存在次序关系,可以分为无序型变量和有序型变量,而有序型变量在可计算的约束下又可以说成有距变量:

  • 无序型变量:数据集中的性别
  • 有序型变量:数据集中的体重
  • 有距变量:数据集中的成绩

先解释一下有距变量,比如上面数据集中的体重有三个类别60kg、50kg、40kg,很明显这是有序的,但是这三者间是存在可计算关系的,比如60kg-10kg=50kg,类别之间可以通过一定计算相互转换。

再看有序型变量,比如上面数据集中的成绩也有三个类别优、良、差,这三个类别并不是毫无关系,因为在某种意义上"优"强于"良"、“良"强于"差”,很明显这也是有序变量,但这可不是有距变量,因为 优-良\neq差 是一定的。而无序变量理解起来就非常简单了,性别就是一个特别典型的例子,类别间的关系就是 男\neq女 ,这是一种"有你就没我的关系"。

前面在通过OrdinalEncoder对这三个类别型变量进行转换时,都会转换成相应的数字,从算法的角度来讲,如果你给我传入的数据为数字,那么我就认为它是可以相互计算的,但是数字是有大小之分的,2和0参与计算起到的作用自然不同。

但我们已经清楚只有有距变量才有这样的相互计算的性质,而无序变量和有序变量经过编码转换后被误解成了有距变量,忽略掉了数字本身的性质,自然会影响后期的建模,而对于这样的变量可以利用独热编码将原本变量转化为哑变量。

from sklearn.preprocessing import OneHotEncoder
OneHot = OneHotEncoder()#实例化
result = OneHot.fit_transform(Data1.iloc[:,2:4]).toarray()
OneHotnames = OneHot.get_feature_names().tolist()#获取新特征名称
OneHotDf = pd.DataFrame(result,columns=OneHotnames)
Data3.drop(columns=['Sex','Grade'],inplace=True)
Data3 = pd.concat([OneHotDf,Data3],axis=1)

最后运行的结果如下:

这个新建的DataFrame中的左半部分是由许多新的特征构成,每个特征只包含0和1两个元素,其中1代表有、0代表没有。比如Sex特征中包含两个元素"男"和"女",而这两个元素就可以通过哑变量转化形成两个新的特征。Sex特征中为"男"的样本在新特征"x0_男"中就为1,在"x0_女"中就为0,反之可推出Sex中的"女"在新特征中的分布。

Dummy Coding

上面利用OneHotEncoder进行哑变量转换略显复杂,在Pandas库中也有相应的方法可以起到同样的效果。

Data4 = pd.get_dummies(Data4)

一行代码显得更加整洁,最后运行结果如下,在get_dummies也有很多参数可以设置,比如新特征的名字等。

这里再介绍一下虚拟变量,虚拟变量和独热编码十分相似,不同之处在于虚拟变量比独热编码少生成一个变量,下图就是虚拟变量的一种形式。

在转换为虚拟变量后,以Grade这一特征为例,可以看到删去了"Grade_良"这一新特征,因为对于一个人的成绩而言,“优"和"差"这两个特征足以表达出所有的信息,如果一个人成绩不是"优"和"差”,那一定就是"良",所以原数据经独热编码后是有冗余的,而虚拟变量则很好地删去冗余,相应的代价就是虚拟变量在表达上没有独热编码直观。

参考链接:
[1].https://blog.csdn.net/weixin_30732825/article/details/101073774
[2].https://www.bilibili.com/video/BV1qt411C7PX

猜你喜欢

转载自blog.csdn.net/weixin_43434202/article/details/106966129