概要
在scipy或者scikit-learn中已经具备某些功能的情况下,我们没有注意到而又独立去实现的事,时有发生。这种就是坊间所说的重新发明轮子,这种时间和精力浪费,其实应该尽力避免。下面就汇总一下数据在前处理完成后和被送到分类器之前的阶段中,能够使用,带来便利的各种轮子。
LabelEncoder
在将字符串转换成ID的时候可以使用。
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(['beijing', 'shanghai', 'guangzhou', 'hangzhou', 'nanjing', 'wuhan'])
le.classes_
#=> array(['beijing', 'guangzhou', 'hangzhou', 'nanjing', 'shanghai', 'wuhan'], dtype='<U9')
le.transform(['hangzhou', 'guangzhou', 'beijing', 'hangzhou', 'nanjing', 'guangzhou'])
#=> array([2, 1, 0, 2, 3, 1], dtype=int64)
le.inverse_transform([2, 1, 0, 2, 3, 1])
#=> array(['hangzhou', 'guangzhou', 'beijing', 'hangzhou', 'nanjing', 'guangzhou'], dtype='<U9')
生成的LabelEncoder暂时保存起来,其他地方也想使用的话,StackOverFlow上有一部分保存的解决方法:
训练代码:
encoder = LabelEncoder()
encoder.fit(X)
numpy.save('classes.npy', encoder.classes_)
测试代码:
encoder = LabelEncoder()
encoder.classes_ = numpy.load('classes.npy')
# Now you should be able to use encoder
# as you would do after `fit`
也可以用下面的方式简单的用pickle读写:
import pickle
with open('foo.p', 'wb') as f:
pickle.dump(le, f)
with open('foo.p', 'rb') as f:
le2 = pickle.load(f)
le2.inverse_transform([2, 1, 0, 2, 3, 1])
#=> array(['hangzhou', 'guangzhou', 'beijing', 'hangzhou', 'nanjing', 'guangzhou'], dtype='<U9')
LabelBinarizer
LabelEncoder只是将字符串换成了数值。这些数值可能不能作为一个要素,而是要作为多个要素来处理。
例如输入是[beijing, shanghai, guangzhou]这3个值的话,不要[1,2,3]而是想要[[1, 0, 0], [0, 1, 0]. [0, 0, 1]]。
模式如下:
# 目标不是变成这样
pd.DataFrame([{'prefecture': 1}, {'prefecture': 2}, {'prefecture': 3}])
# 而是要one-hot结果
pd.DataFrame([
{'beijing': 1, 'shanghai': 0, 'guangzhou': 0},
{'beijing': 0, 'shanghai': 1, 'guangzhou': 0},
{'beijing': 0, 'shanghai': 0, 'guangzhou': 1}])
这种情况下可以使用LabelBinarizer来完成。
from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer()
lb.fit(['beijing', 'guangzhou', 'shanghai'])
lb.transform(['beijing', 'guangzhou', 'shanghai'])
#=> array([[1, 0, 0],
#=> [0, 1, 0],
#=> [0, 0, 1]])
对Pandas的DataFrame的列进行运算,运算结果想要放入相同的DataFrame的时候:
df = pd.DataFrame([{'prefecture': 'beijing'}, {'prefecture': 'guangzhou'}, {'prefecture': 'shanghai'}, {'prefecture': 'beijing'}])
lb.fit(df.prefecture)
pd.concat([df, pd.DataFrame(lb.transform(df.prefecture), columns=lb.classes_)], axis=1)
上述只有三个列的情况下,用稠密的DataFrame其实也没有问题。如果值增加的话,就不得不使用稀疏方式了。
如果在LabelBinarizer初始化时设定参数sparse_output,输出结果就变成稀疏结果。
from sklearn.preprocessing import LabelBinarizer
lb = LabelBinarizer(sparse_output=True)
lb.fit(['beijing', 'guangzhou', 'shanghai'])
lb.transform(['beijing', 'guangzhou', 'shanghai'])
#=> <3x3 sparse matrix of type '' with 3 stored elements in Compressed Sparse Row format>
MultiLabelBinarizer
LabelBinarizer只适用于一列一列处理的情况。
比如,处理电影的种类的时候,[action, horror]和[romance, commedy]这样有多个种类关联的场景。这时,将种类数组传递给MultiLabelBinarizer,可以很好的完成变换。
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
mlb.fit_transform([['action', 'adventure'], ['action'], ['action', 'war', 'commedy'], []])
#=> array([[1, 1, 0, 0],
#=> [1, 0, 0, 0],
#=> [1, 0, 1, 1],
#=> [0, 0, 0, 0]])