机器学习——特征工程之分类变量

机器学习——特征工程之分类变量

    • 前言
    • 分类变量的编码
      • 1、one-hot 编码
      • 2、虚拟编码
      • 3、效果编码
    • 处理大型分类变量
      • 1、特征散列化
      • 2、分箱计数
    • 总结

前言

关于特征工程,已经对空值、数值型和文本数据的处理做了大致方法的说明,这篇对数据类型中的另一大重要部分——分类变量,作处理方法总结。

声明:关于编程语法相关问题不会展开论述,本文只针对方法路线

分类变量的编码

先说明什么样的数据被称为分类变量:分类变量是用来表示类别或标记的,例如西瓜书(周志华的,应该都知道的吧)中西瓜数据集里的色泽(青绿,乌黑,浅白)、纹理(清晰,稍糊)等。更加不好处理的情况是,这些分类变量与数值变量掺杂在一起作为整个数据集,如下图(仅截取一部分):
机器学习——特征工程之分类变量_第1张图片但是,在实际的数据集中,类别的数量总是有限的,为了参与数学算法运算(特别是回归),一定要将分类变量转成数值型,可以用数字表示,但与数值型变量不同,分类变量不能被排序(尽最大可能要避免有顺序)。

1、one-hot 编码

one-hot使用一组比特位,每个比特位表示一种可能的类别。每一个比特位表示一个特征,因此,一个可能有k个类别的分类器就可以编码为一个长度为K的特征向量。
**举例:**使用原数据集中(自己建立)的一个分类变量的列作为例子,该列原始数据如下:

df = pd.DataFrame({"City":["SF","SF","SF","NYC","NYC","NYC","Seattle","Seattle","Seattle"],
                   "Rent":[3999,300,3333,2222,4444,888,88888,44443,3222]})

      City   Rent
0       SF   3999
1       SF    300
2       SF   3333
3      NYC   2222
4      NYC   4444
5      NYC    888
6  Seattle  88888
7  Seattle  44443
8  Seattle   3222

可以看到,该数据集有两个属性,一个为分类变量,一个为数值型变量。使用pandas中的get_dummies(pd,col)一键one-hot。
代码如下:

ont_hot_df = pd.get_dummies(df,["City"])

一键one-hot后,原数据集就变成了:

    Rent  City_NYC  City_SF  City_Seattle
0   3999         0        1             0
1    300         0        1             0
2   3333         0        1             0
3   2222         1        0             0
4   4444         1        0             0
5    888         1        0             0
6  88888         0        0             1
7  44443         0        0             1
8   3222         0        0             1

仅在该属性有值的位置取1,其余为0。
one-hot缺点:

  1. 分类较多时,会造成冗余,使得同一个问题有多个有效模型;
  2. 分类较多时,不可避免地会造成维数灾难;
  3. List item很明显,对于one-hot编码,各行的和一定等于1,即存在线性关系,线性相关的特征会使训练出的模型不唯一。特征的不同线性组合可以做出同样的预测,所以需要做些额外的努力才能理解某个特征对预测结果的作用。

2、虚拟编码

如果一个样本中有k个分类,one-hot编码会产生k个自由度,但是变量本身只需要k-1个自由度,虚拟编码在进行表示时只使用k-1 个特征,除去了额外的自由度。
这么说有点绕,不好理解,下面举个例子:

				e1		e2
San Francisco 	 1		 0
New York		 0		 1
Seattle			 0 		 0

上面有三个分类,使用one-hot编码应该有3(k)列(自由度),但是虚拟编码将其中的一类通过一个全零向量表示,它被称为参照类(上例中的“Seattle”)。
代码:

dummy_df = pd.get_dummies(df, prefix=['city'], drop_first=True)

结果(如果看不明白虚拟编码和one-hot编码的话,可以用下表与上面one-hot后的表作下对比):

	Rent 	city_SF city_Seattle
0 	3999 		1.0		 0.0
1 	4000	 	1.0 	 0.0
2 	4001	 	1.0 	 0.0
3 	3499	 	0.0 	 0.0
4 	3500	 	0.0		 0.0
5 	3501	 	0.0 	 0.0
6 	2499 		0.0 	 1.0
7 	2500	 	0.0 	 1.0
8 	2501	 	0.0 	 1.0

使用虚拟编码的好处: 使用虚拟编码的模型结果比使用one-hot 编码的模型结果更具解释性**,没有冗余。(具体实际效果只能使用交叉验证来选择则编码方式)

缺点: 不直观,全零向量不太容易处理缺失数据。

3、效果编码

效果编码与虚拟编码非常相似,简单地说,区别就在于参照类由全零向量改成了全-1向量,如下:

				e1		e2
San Francisco 	 1		 0
New York		 0		 1
Seattle			-1 		-1

效果编码需要在虚拟编码的基础上,将参照类的全零向量改成全-1向量:
代码:

ffect_df = dummy_df.copy()	#dummy_df为上面虚拟编码后的数据集
effect_df.ix[3:5, ['city_SF', 'city_Seattle']] = -1.0

效果如下(注意对比):

	Rent 	city_SF city_Seattle
0 	3999 		1.0		 0.0
1 	4000	 	1.0 	 0.0
2 	4001	 	1.0 	 0.0
3 	3499	   -1.0     -1.0 
4 	3500	   -1.0 	-1.0 
5 	3501	   -1.0  	-1.0 
6 	2499 		0.0 	 1.0
7 	2500	 	0.0 	 1.0
8 	2501	 	0.0 	 1.0

效果编码使用另外一种编码表示参照类,从而避免了虚拟编码存在的问题,但是全由-1 组成的向量是个密集向量,计算和存储的成本都比较高。正是因为这个原因,像Pandas 和scikit-learn这样的常用机器学习软件包更喜欢使用虚拟编码或one-hot 编码,而不是效果编码。

处理大型分类变量

当类别的数量变得非常大时,上面介绍的3种编码方式都会出现问题,所以需要另外的策略来处理超大型分类变量。
处理大型分类变量的目标:既要内存高效,又能生成精确的、训练速度很快的模型。
现有的解决方案可以分类如下。
(1) 不在编码问题上搞什么花样,使用一个简单、容易训练的模型,在很多机器上使用one-hot 编码训练线性模型(逻辑回归或线性支持向量机)。
(2) 压缩特征,有两种方式。
a) 特征散列化,通常用于线性模型。
b) 分箱计数,常用于线性模型和树模型。
one-hot编码上面已经说完了,下面谁基于特征的处理方法。

1、特征散列化

先放一张图:
机器学习——特征工程之分类变量_第2张图片
图中的散列函数 (有点类似于哈希化)是一种确定性函数,他可以将一个可能无界的整数映射到一个有限的整数范围[1,m]中。

举例: 图中的每一个单词可以看成一个分类,散列函数经过算法,将每一分类映射成一个散列值

因为输入域可能大于输出范围,所以可能有多个值被映射为同样的输出,这称为碰撞。均匀散列函数可以确保将大致相同数量的数值映射到m 个分箱中。
代码:

from sklearn.feature_extraction import FeatureHasher
 h = FeatureHasher(n_features=m, input_type='string')

举例:

from sklearn.feature_extraction import FeatureHasher
h = FeatureHasher(n_features = 3,input_type='string')
f = h.transform(df["City"])
print(f.toarray())
结果:
 [[-1.  0. -1.]
 [-1.  0. -1.]
 [-1.  0. -1.]
 [-2.  0. -1.]
 [-2.  0. -1.]
 [-2.  0. -1.]
 [-3.  2.  2.]
 [-3.  2.  2.]
 [-3.  2.  2.]]

优点:特征散列化对计算能力大有裨益
缺点:牺牲了直观的用户可解释性

2、分箱计数

思想:分箱计数不使用分类变量的值作为特征,而是使用目标变量取这个值的概率。换个说法,不对分类变量的值进行编码,而是计算分类变量值与要预测的目标变量之间的相关统计量。

假设有10000个分类变量,one-hot 编码会生成一个长度为10 000 的稀疏向量,只在对应当前数据点的列上有一个1。分箱计数会将所有10 000 个二值列编码为一个单独的特征,是0和1 之间的一个实数值。简而言之,分箱计数将一个分类变量转换为与其值相关的统计量,它可以将一个大型的、稀疏的、二值的分类变量表示(如one-hot 编码生成的结果)转换为一个小巧的、密集的、实数型的数值表示。
举例:

import pandas as pd
# 使用这个超过6GB的数据集的前10 000行作为训练集
df = pd.read_csv('data/train_subset.csv')
# 看看训练集中有多少个唯一的特征
len(df['device_id'].unique())
7201
# 对每个类别,我们要计算:
# Theta = [counts, p(click), p(no click), p(click)/p(no click)]
def click_counting(x, bin_column):
	clicks = pd.Series(x[x['click'] > 0][bin_column].value_counts(),name='clicks')
	no_clicks = pd.Series(x[x['click'] < 1][bin_column].value_counts(),name='no_clicks')
    counts = pd.DataFrame([clicks,no_clicks]).T.fillna('0')
	counts['total_clicks'] = counts['clicks'].astype('int64') +counts['no_clicks'].astype('int64')
	return counts
def bin_counting(counts):
    counts['N+'] = counts['clicks'].astype('int64').divide(counts['total_clicks'].astype('int64'))
    counts['N-'] = counts['no_clicks'].astype('int64').divide(counts['total_clicks'].astype('int64'))
    counts['log_N+'] = counts['N+'].divide(counts['N-'])
    # 如果只想返回分箱属性就进行过滤
    bin_counts = counts.filter(items= ['N+', 'N-', 'log_N+'])
    return counts, bin_counts
 # 分箱计数示例:device_id
bin_column = 'device_id'
device_clicks = click_counting(df.filter(items=[bin_column, 'click']),bin_column)
device_all, device_bin_counts = bin_counting(device_clicks)
# 检查一下,确定我们处理了所有设备
print(len(device_bin_counts))
7201
print(device_all.sort_values(by = 'total_clicks', ascending=False).head(4))
#结果
			clicks 	no_clicks 	total 		N+ 		N- 		log_N+
a99f214a 	15729 	71206 		86935 	0.180928 0.819072 0.220894
c357dbff 	33 		134 		167 	0.197605 0.802395 0.246269
31da1bd0 	0 		62 			62 		0.000000 1.000000 0.000000
936e92fb 	5 		54 			59 		0.084746 0.915254 0.092593

后面还有关于如何处理稀有类、防止数据泄露、无界计数等情况的处理方法,以后如果遇到了再详细的说吧!

总结

对分类变量说了这么方法,下面总结下各自的优缺点:

one-hot编码
|优点:  容易实现、可能是最精确的、可用于在线学习
|缺点:	计算效率不高、不能适应可增长的类别、只适用于线性模型、对于大数据集,需要大规模的分布式优化

特征散列化
|优点:	• 容易实现
		• 模型训练成本更低
		• 容易适应新类别
		• 容易处理稀有类
		• 可用于在线学习
|缺点 :• 只适合线性模型或核方法
		• 散列后的特征无法解释
		• 精确度难以保证
分箱计数
优点: • 训练阶段的计算负担最小
	   • 可用于基于树的模型
	   • 比较容易适应新类别
       • 可使用 back-off 方法或最小计数图处理稀有类
       • 可解释
缺点: • 需要历史数据
	  • 需要延迟更新,不完全

你可能感兴趣的:(特征工程,机器学习,特征工程)