使用非数值类型的特征


类型数据

特征里经常会有非数值类型的,比如某个用户去看电影,他留下的非数值特征可能有:星期几去的,几月份,几点,是不是节假日,当天天气等.这些数据属于类型数据(categorical data),比如:
星期几:一二三四五六七
月份:一到十二月
当天天气:晴,雨,阴,雪.
以前一直以为在构建特征时碰到这种类型数据,用一位数字表示就好,比如某个样本的特征是(2, 12 , 22 , 0, 1)表示星期二,十二月,22点,非节假日,晴天.但最近看书才知道不是这样,应该用一种称作二元编码的方法.

二元编码

首先统计某个类型数据一共有几类,比如星期几共有7种类型.二元编码就是只再对应的位置上为1,其它全是0.比如星期一对应(1,0,0,0,0,0,0).不过不是非要按照顺序来,比如星期三不一定是(0,0,1,0,0,0,0).每种特征按照这种方式转换,最后拼起来.
想了想这么做确实有道理,参数多了,对每个特征的每种情况都训练了一个或者多个参数.如果写成(2, 12 , 22 , 0, 1)参数就太少.

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
##原始数据
#星期四 阴 八月
#星期二 雪 十月
#星期三 阴 六月
#星期一 晴 一月
#...
#...
def get_mapping(dataset, idx):
'''dataset是所有数据,统计各维特征并编号
'''

featset = set()
for data in dataset:
featset.add(data[idx])
return dict(zip(featset, range(0, len(featset))))

# mappings是一个从特征到one-hot点的映射
mappings = [get_mapping(dataset, idx) for idx in range(0, 3)]
# 总的特征维度,这个例子里是7+4+12=23
feature_len = sum(map(len, mappings))

def get_one_hot_feature(data, mappings, feature_len):
'''data是一条数据
'''

feature_vec = np.zeros(feature_len)
idx = 0
step = 0
for feature in data:
feature_vec[step + mappings[idx][feature]] = 1.0
step += len(mappings[idx])
idx += 1
return feature_vec
# 全部转为one-hot
all_one_hot_feature = map(lambda data: get_one_hot_feature(data, mappings, feature_len), dataset)