机器学习中的特征工程(三)---- 序数和类别特征处理方法

简介

本文主要说明特征工程中关于序数特征和类别特征的常用处理方法。主要包含LabelEncoder、One-Hot编码、DummyCoding、FeatureHasher以及要重点介绍的WOE编码。

序数特征处理

序数特征指的是有序但无尺度的特征。比如表示‘学历’的特征,'高中'、'本科'、'硕士',这些特征彼此之间是有顺序关系的,但是特征本身无尺度,并且也可能不是数值类型。在实际应用中,一般是字符类型居多,为了将其转换成模型能处理的形式,通常需要先进行编码,比如LabelEncoding。如果序数特征本身就是数值类型变量,则可不进行该步骤。下面依次介绍序数特征相关的处理方式。

Label Encoding

首先介绍Label Encoding编码方式,该方式默认从0开始,按照变量字符顺序排序,例子如下:

from sklearn.preprocessing import LabelEncoder

x = ['a', 'b', 'a', 'c', 'b']

encoder = LabelEncoder()
x1 = encoder.fit_transform(x)
x1
编码结果

可以看到,原始特征一共被分为了3类,'a'、'b'、'c'编码后分别为'0'、'1'、'2'。
如果你的数据是用pandas表示的,pandas中也有类似的处理方式,通过类别编码的方式得到的结果是一样的。

import pandas as pd

x2 = pd.Series(x).astype('category')
x2.cat.codes.values
编码结果

因子化

除此之外,pandas中还有一种编码方式,叫做'因子化'。不过它并不是按照特征的字符顺序来排序的,而是按照特征变量出现的先后顺序来编码的,结果本质上没有区别。对比例子如下:

x = ['a', 'b', 'a', 'c', 'b']
# 调换了第一个a和第四个c的位置
x1 = ['c', 'b', 'a', 'a', 'b']

x2, uniques = pd.factorize(x)
x3, uniques = pd.factorize(x1)
print("x2: ", x2)
print("x3: ", x3)
编码结果

二值化

序数特征还可以进行二值化操作,小于或者大于阈值的时候分别为0和1。实例如下:

x = ['a', 'b', 'a', 'c', 'b']
x4 = pd.Series(x)
x4 = (x4 >= 'b').astype(int) #令大于等于'b'的都为1
x4.values
二值化结果

类别特征处理

类别特征由于没有顺序也没有尺度,因此处理较为麻烦,但是在CTR等领域却是非常常见的特征。比如商品的类型,颜色,用户的职业,兴趣等等。类别变量编码方法中最常使用的就是One-Hot编码,接下来结合具体实例来介绍。

One-Hot编码

One-Hot编码,又称为'独热编码',其变换后的单列特征值只有一位是1。如下例所示,一个特征中包含3个不同的特征值(a,b,c),编码转换后变成3个子特征,其中每个特征值中只有一位是有效位1。

from sklearn.preprocessing import LabelEncoder, OneHotEncoder

one_feature = ['b', 'a', 'c']
label_encoder = LabelEncoder()
feature = label_encoder.fit_transform(one_feature)
onehot_encoder = OneHotEncoder(sparse=False)
onehot_encoder.fit_transform(feature.reshape(-1, 1))
one-hot编码结果

这里要注意的是,因为原始特征是字符串类型,这里使用上一节的LabelEncoder先将其转换成数值类型。但是实际上这一步也不需要,OneHotEncoder可以直接处理字符串类型。

LabelBinarizer

sklearn中的LabelBinarizer也具有同样的作用,代码如下:

from sklearn.preprocessing import LabelBinarizer
feature = np.array(['b', 'a', 'c'])
LabelBinarizer().fit_transform(feature)
LabelBinarizer转换结果

虚拟编码Dummy Coding

同样,pandas中也内置了对应的处理方式,使用起来比Sklearn更加方便。实例如下:

one_feature = ['b', 'a', 'c']
pd.get_dummies(one_feature, prefix='test') # 设置前缀test
get_dummies编码

其实仔细观察One-Hot编码会发现,具有n个类别值的单个特征将会编码成n个子特征。但实际上有一维是冗余的。虚拟编码(dummy coding)可以只产生n-1个特征,再使用一个全零的向量,实例如下:

one_feature = ['b', 'a', 'c']
pd.get_dummies(one_feature, prefix='test', drop_first=True) # 设置前缀test
虚拟编码

可以看到这里只产生了2个子特征,剩余的一个特征由全零向量表示。

特征哈希

按照上述编码方式,如果某个特征具有100个类别值,那么经过编码后将产生100个或99个新特征,这极大地增加了特征维度和特征的稀疏度,同时还可能会出现内存不足的情况。sklearn中的FeatureHasher接口采用了hash的方法,将不同的值映射到用户指定长度的数组中,使得输出特征的维度是固定的,该方法占用内存少,效率高,可以在多类别变量值中使用,但是由于采用了Hash函数的方式,所以具有冲突的可能,即不同的类别值可能映射到同一个特征变量值中。
下面的例子中,原始特征类别值共有10个不同的值,但是只采用了长度为5的数组进行编码。

from sklearn.feature_extraction import FeatureHasher

h = FeatureHasher(n_features=5, input_type='string')
test_cat = ['a','b','c','d','e','f','g','h','i','j','a','b']
f = h.transform(test_cat)
f.toarray()
FeatureHasher

可以看到编码后的特征值是有重复的,说明多个不同的原始特征类别值编码后,被映射到了同一个类别里。

多类别值处理方式

当类别值过多时,One-Hot 编码或者Dummy Coding都可能导致编码出来的特征过于稀疏,其次也会占用过多内存。如果使用FeatureHasher,n_features的设置不好把握,可能会造成过多冲突,造成信息损失。这里提供一种基于统计的编码方法,包括基于特征值的统计或者基于标签值的统计——基于标签的编码。

下面的代码演示了使用特征值出现频数的分组方法,小于指定阈值的特征将被编码为同一个值。

import seaborn as sns

test = ['a','b','c','d','e','a','a','c']
df = pd.DataFrame(test, columns=['alpha'])
sns.countplot(df['alpha'])
频数划分

首先我们将每个类别值出现的频数计算出来,比如我们设置阈值为1,那么所有小于阈值1的类别值都会被编码为同一类,大于1的类别值会分别编码,如果出现频数一样的类别值,既可以都统一分为一个类,也可以按照某种顺序进行编码,这个可以根据业务需要自行决定。那么根据上图,可以得到其编码值为:


按照频数编码结果

即(a,c)分别编码为一个不同的类别,(e,b,d)编码为同一个类别。

WOE编码

WOE(Weight of Evidence,证据权重)编码利用了标签信息,属于有监督的编码方式。该方式广泛用于金融领域信用风险模型中,是该领域的经验做法。下面先给出WOE的计算公式:
WOE_i = ln\lbrace \frac {P_{y1}}{P_{y0}} \rbrace = ln\lbrace \frac {B_i / B}{G_i/G} \rbrace
WOE_i值可解释为第i类别中好坏样本分布比值的对数。其中各个分量的解释如下:

  • P_{y1}表示该类别中坏样本的分布
  • P_{y0}表示该类别中好样本的分布
  • B_i/B表示该类别中坏样本的数量在总体坏样本中的占比
  • G_i/G表示该类别中好样本的数量在总体好样本中的占比

很明显,如果整个分数的值大于1,那么WOE值为正,否则为负,所以WOE值的取值范围为正负无穷。
WOE值直观上表示的实际上是“当前分组中坏客户占所有坏客户的比例”和“当前分组中好客户占所有坏客户的比例”的差异。转化公式以后,也可以理解为:当前这个组中坏客户和好客户的比值,和所有样本中这个比值的差异。这个差异为这两个比值的比值,再取对数来表示的。
WOE越大,这种差异越大,这个分组里的样本坏样本可能性就越大,WOE越小,差异越小,这个分组里的坏样本可能性就越小。

下面结合一个例子来说明WOE的计算:

np.random.seed(0)
# 随机生成1000行数据
df = pd.DataFrame({
    'x': np.random.choice(['R','G','B'], 1000),
    'y': np.random.randint(2, size=1000)
})
df.head()
随机生成的数据

其中x代表特征,包含3个类别(R,G,B),y表示标签,取值范围为0、1,分别代表好、坏标签。
分别统计该特征中各个类别的占比,代码如下:

pd.crosstab(df['y'], df['x'], margins=True)

各类别占比
以类别R为例,我们来计算一下它的WOE值。在该类别中,好样本占153,坏样本占184,所以其WOE编码值为:
WOE_R = ln \lbrace \frac {184 / 519}{153/481} = 0.108461
同理可得WOE_G = -0.046184, WOE_B = -0.063841
由WOE值的定义推断,相邻类别的绝对值差异越大,说明组之间差异越大,越具备区分性,预测性能越好。

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容