学习sklearn和kagggle时遇到的问题,什么是独热编码?为什么要用独热编码?什么情况下可以用独热编码?以及和其他几种编码方式的区别。
首先了解机器学习中的特征类别:连续型特征和离散型特征
拿到获取的原始特征,必须对每一特征分别进行归一化,比如,特征A的取值范围是[-1000,1000],特征B的取值范围是[-1,1].如果使用logistic回归,w1*x1+w2*x2,因为x1的取值太大了,所以x2基本起不了作用。所以,必须进行特征的归一化,每个特征都单独进行归一化。
对于连续性特征:
对于离散性特征:
独热码,在英文文献中称做 one-hot code, 直观来说就是有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。举例如下:
假如有三种颜色特征&#xff1a;红、黄、蓝。 在利用机器学习的算法时一般需要进行向量化或者数字化。那么你可能想令 红&#61;1&#xff0c;黄&#61;2&#xff0c;蓝&#61;3. 那么这样其实实现了标签编码&#xff0c;即给不同类别以标签。然而这意味着机器可能会学习到“红<黄<蓝”&#xff0c;但这并不是我们的让机器学习的本意&#xff0c;只是想让机器区分它们&#xff0c;并无大小比较之意。所以这时标签编码是不够的&#xff0c;需要进一步转换。因为有三种颜色状态&#xff0c;所以就有3个比特。即红色&#xff1a;1 0 0 &#xff0c;黄色: 0 1 0&#xff0c;蓝色&#xff1a;0 0 1 。如此一来每两个向量之间的距离都是根号2&#xff0c;在向量空间距离都相等&#xff0c;所以这样不会出现偏序性&#xff0c;基本不会影响基于向量空间度量算法的效果。
自然状态码为&#xff1a;000,001,010,011,100,101
独热编码为&#xff1a;000001,000010,000100,001000,010000,100000
来一个sklearn的例子&#xff1a;
from sklearn import preprocessing
enc &#61; preprocessing.OneHotEncoder()
enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) # fit来学习编码
enc.transform([[0, 1, 3]]).toarray() # 进行编码
输出&#xff1a;array([[ 1., 0., 0., 1., 0., 0., 0., 0., 1.]])
数据矩阵是4*3&#xff0c;即4个数据&#xff0c;3个特征维度。
0 0 3 观察左边的数据矩阵&#xff0c;第一列为第一个特征维度&#xff0c;有两种取值0\1. 所以对应编码方式为10 、01
1 1 0 同理&#xff0c;第二列为第二个特征维度&#xff0c;有三种取值0\1\2&#xff0c;所以对应编码方式为100、010、001
0 2 1 同理&#xff0c;第三列为第三个特征维度&#xff0c;有四中取值0\1\2\3&#xff0c;所以对应编码方式为1000、0100、0010、0001
1 0 2
再来看要进行编码的参数[0 , 1, 3]&#xff0c; 0作为第一个特征编码为10, 1作为第二个特征编码为010&#xff0c; 3作为第三个特征编码为0001. 故此编码结果为 1 0 0 1 0 0 0 0 1
二. 为什么要独热编码&#xff1f;正如上文所言&#xff0c;独热编码&#xff08;哑变量 dummy variable&#xff09;是因为大部分算法是基于向量空间中的度量来进行计算的&#xff0c;为了使非偏序关系的变量取值不具有偏序性&#xff0c;并且到圆点是等距的。使用one-hot编码&#xff0c;将离散特征的取值扩展到了欧式空间&#xff0c;离散特征的某个取值就对应欧式空间的某个点。将离散型特征使用one-hot编码&#xff0c;会让特征之间的距离计算更加合理。离散特征进行one-hot编码后&#xff0c;编码后的特征&#xff0c;其实每一维度的特征都可以看做是连续的特征。就可以跟对连续型特征的归一化方法一样&#xff0c;对每一维特征进行归一化。比如归一化到[-1,1]或归一化到均值为0,方差为1。
为什么特征向量要映射到欧式空间&#xff1f;
将离散特征通过one-hot编码映射到欧式空间&#xff0c;是因为&#xff0c;在回归&#xff0c;分类&#xff0c;聚类等机器学习算法中&#xff0c;特征之间距离的计算或相似度的计算是非常重要的&#xff0c;而我们常用的距离或相似度的计算都是在欧式空间的相似度计算&#xff0c;计算余弦相似性&#xff0c;基于的就是欧式空间。
三 .独热编码优缺点总的来说&#xff0c;要是one hot encoding的类别数目不太多&#xff0c;建议优先考虑。
五. 什么情况下(不)需要归一化&#xff1f;作用&#xff1a; 利用LabelEncoder() 将转换成连续的数值型变量。即是对不连续的数字或者文本进行编号例如&#xff1a;
from sklearn.preprocessing import LabelEncoder
le &#61; LabelEncoder()
le.fit([1,5,67,100])
le.transform([1,1,100,67,5])
输出&#xff1a; array([0,0,3,2,1])
>>> le &#61; preprocessing.LabelEncoder()
>>> le.fit(["paris", "paris", "tokyo", "amsterdam"])
LabelEncoder()
>>> list(le.classes_)
[&#39;amsterdam&#39;, &#39;paris&#39;, &#39;tokyo&#39;] # 三个类别分别为0 1 2
>>> le.transform(["tokyo", "tokyo", "paris"])
array([2, 2, 1]...)
>>> list(le.inverse_transform([2, 2, 1])) # 逆过程
[&#39;tokyo&#39;, &#39;tokyo&#39;, &#39;paris&#39;]
限制&#xff1a;上文颜色的例子已经提到标签编码了。Label encoding在某些情况下很有用&#xff0c;但是场景限制很多。再举一例&#xff1a;比如有[dog,cat,dog,mouse,cat]&#xff0c;我们把其转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象&#xff1a;dog和mouse的平均值是cat。所以目前还没有发现标签编码的广泛使用。
附&#xff1a;基本的机器学习过程
Label encoding在某些情况下很有用&#xff0c;但是场景限制很多。比如有一列 [dog,cat,dog,mouse,cat]&#xff0c;我们把其转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象&#xff1a;dog和mouse的平均值是cat。而且像decision tree&#xff0c;random forest和xgboost这种算法能处理好这种转换&#xff0c;而且相比转换前&#xff0c;所需要的内存空间小一点。
One-Hot 编码即独热编码&#xff0c;又称一位有效编码&#xff0c;其方法是使用N位状态寄存器来对N个状态进行编码&#xff0c;每个状态都由他独立的寄存器位&#xff0c;并且在任意时候&#xff0c;其中只有一位有效。这样做的好处主要有&#xff1a;1. 解决了分类器不好处理属性数据的问题&#xff1b; 2. 在一定程度上也起到了扩充特征的作用。
将离散型特征进行one-hot编码的作用&#xff0c;是为了让距离计算更合理&#xff0c;但如果特征是离散的&#xff0c;并且不用one-hot编码就可以很合理的计算出距离&#xff0c;那么就没必要进行one-hot编码。离散特征进行one-hot编码&#xff0c;编码后的特征&#xff0c;其实每一维度的特征都可以看做是连续的特征。就可以跟对连续型特征的归一化方法一样&#xff0c;对每一维特征进行归一化。比如归一化到[-1,1]或归一化到均值为0,方差为1。
基于树的方法是不需要进行特征的归一化&#xff0c;例如随机森林&#xff0c;bagging 和 boosting等。基于参数的模型或基于距离的模型&#xff0c;都是要进行特征的归一化。Tree Model不太需要one-hot编码&#xff1a; 对于决策树来说&#xff0c;one-hot的本质是增加树的深度。
one hot encoding的优点就是它的值只有0和1&#xff0c;不同的类型存储在垂直的空间。缺点就是&#xff0c;当类别的数量很多时&#xff0c;特征空间会变得非常大。在这种情况下&#xff0c;一般可以用PCA来减少维度。而且one hot encoding&#43;PCA这种组合在实际中也非常有用。
总的来说&#xff0c;要是one hot encoding的类别数目不太多&#xff0c;建议优先考虑。
七、Sklearn的LabelEncoder和OneHotEncoder实战LabelEncoder和OneHotEncoder
我们也可以通过sklearn的模块实现对离散变量的one-hot编码&#xff0c;其中LabelEncoder是将离散变量替换为数字&#xff0c;
OneHotEncoder则实现对替换为数字的离散变量进行one-hot编码。
注&#xff1a;get_dummies()可以直接对字符型变量进行one-hot编码&#xff0c;但OneHotEncoder不能直接对字符型变量编码&#xff0c;因此我们需要先将字符型变量转换为数值型变量。这就是为什么在OneHotEncoder之前需要LabelEncoder的原因。
如下代码为将一列数据进行one-hot编码后&#xff0c;然后拼接
all_weekday_cache&#61;{}for e in allday:all_weekday_cache[e]&#61;pd.to_datetime(e).isoweekday()traindata[&#39;weekday&#39;]&#61;traindata[&#39;sampleday&#39;].apply(lambda x:all_weekday_cache[x])if flag&#61;&#61;"train":Enc_ohe.fit(traindata[[&#39;weekday&#39;]])print(Enc_ohe.categories_) print(list(Enc_ohe.categories_[0]))DF_dummies2 &#61; pd.DataFrame(Enc_ohe.transform(traindata[[&#39;weekday&#39;]]).todense(), columns &#61; list(Enc_ohe.categories_[0]))print(DF_dummies2.head(3))#拼接traindata &#61; pd.concat((traindata,DF_dummies2),axis&#61;1) # 1 水平方向拼接
打印如下&#xff1a;
[array([1., 2., 3., 4., 5., 6., 7.])]
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]1.0 2.0 3.0 4.0 5.0 6.0 7.0
0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 1.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 1.0 0.0 0.0
例11
from sklearn.preprocessing import OneHotEncoder
2
ohe &#61; OneHotEncoder()
3
ohe.fit([[1,1],[2,1],[3,2],[4,5]])
4
ohe.transform([[2,1],[3,1],[1,1],[4,5]]).toarray()
/home/bigdevelp_user/anaconda3/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:368: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behaviour and silence this warning, you can specify "categories&#61;&#39;auto&#39;".
In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.warnings.warn(msg, FutureWarning)
array([[0., 1., 0., 0., 1., 0., 0.],[0., 0., 1., 0., 1., 0., 0.],[1., 0., 0., 0., 1., 0., 0.],[0., 0., 0., 1., 0., 0., 1.]])ohe.categories_
1
ohe.categories_
[array([1., 2., 3., 4.]), array([1., 2., 5.])]
例2r
1
from sklearn.preprocessing import OneHotEncoder
2
ohe &#61; OneHotEncoder()
3
ohe.fit([[1],[2],[3],[4]])
4
ohe.transform([[2],[3],[1],[4]]).toarray()
/home/bigdevelp_user/anaconda3/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:368: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behaviour and silence this warning, you can specify "categories&#61;&#39;auto&#39;".
In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.warnings.warn(msg, FutureWarning)
array([[0., 1., 0., 0.],[0., 0., 1., 0.],[1., 0., 0., 0.],[0., 0., 0., 1.]])1
ohe.categories_
[array([1., 2., 3., 4.])]
例31
from sklearn.preprocessing import LabelEncoder
2
le &#61; LabelEncoder()
3
le.fit([1,5,67,100])
4
le.transform([1,1,100,67,5])
array([0, 0, 3, 2, 1])[
1
from sklearn.preprocessing import LabelEncoder
2
le &#61; LabelEncoder()
3
le.fit([[1,5,67,100],[2,3,4,5]])
4
le.transform([[1,100,67,5],[2,3,4,5]])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
----> 3 le.fit([[1,5,67,100],[2,3,4,5]])4 le.transform([[1,100,67,5],[2,3,4,5]])~/anaconda3/lib/python3.6/site-packages/sklearn/preprocessing/label.py in fit(self, y)217 self : returns an instance of self.218 """
--> 219 y &#61; column_or_1d(y, warn&#61;True)220 self.classes_ &#61; _encode(y)221 return self~/anaconda3/lib/python3.6/site-packages/sklearn/utils/validation.py in column_or_1d(y, warn)795 return np.ravel(y)796
--> 797 raise ValueError("bad input shape {0}".format(shape))798 799 ValueError: bad input shape (2, 4)
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
sns.set()%matplotlib inline#Iris Plot
iris &#61; load_iris()
n_samples, m_features &#61; iris.data.shape#Load Data
X, y &#61; iris.data, iris.target
D_target_dummy &#61; dict(zip(np.arange(iris.target_names.shape[0]), iris.target_names))DF_data &#61; pd.DataFrame(X,columns&#61;iris.feature_names)
DF_data["target"] &#61; pd.Series(y).map(D_target_dummy)
#sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \
#0 5.1 3.5 1.4 0.2
#1 4.9 3.0 1.4 0.2
#2 4.7 3.2 1.3 0.2
#3 4.6 3.1 1.5 0.2
#4 5.0 3.6 1.4 0.2
#5 5.4 3.9 1.7 0.4 DF_dummies &#61; pd.get_dummies(DF_data["target"])
#setosa versicolor virginica
#0 1 0 0
#1 1 0 0
#2 1 0 0
#3 1 0 0
#4 1 0 0
#5 1 0 0from sklearn.preprocessing import OneHotEncoder, LabelEncoder
def f1(DF_data):Enc_ohe, Enc_label &#61; OneHotEncoder(), LabelEncoder()DF_data["Dummies"] &#61; Enc_label.fit_transform(DF_data["target"])DF_dummies2 &#61; pd.DataFrame(Enc_ohe.fit_transform(DF_data[["Dummies"]]).todense(), columns &#61; Enc_label.classes_)return(DF_dummies2)%timeit pd.get_dummies(DF_data["target"])
#1000 loops, best of 3: 777 µs per loop%timeit f1(DF_data)
#100 loops, best of 3: 2.91 ms per loop
注&#xff1a;get_dummies返回的为数据框&#xff0c;OneHotEncoder返回的为数组。
>>> from sklearn.preprocessing import OneHotEncoder
>>> enc &#61; OneHotEncoder()>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) >>> enc.n_values_
array([2, 3, 4])>>> enc.feature_indices_
array([0, 2, 5, 9])>>> enc.transform([[0, 1, 1]]).toarray()
array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]])
One-Hot 编码即独热编码&#xff0c;又称一位有效编码&#xff0c;其方法是使用N位状态寄存器来对N个状态进行编码&#xff0c;每个状态都由他独立的寄存器位&#xff0c;并且在任意时候&#xff0c;其中只有一位有效。这样做的好处主要有&#xff1a;1. 解决了分类器不好处理属性数据的问题&#xff1b; 2. 在一定程度上也起到了扩充特征的作用。
将离散型特征进行one-hot编码的作用&#xff0c;是为了让距离计算更合理&#xff0c;但如果特征是离散的&#xff0c;并且不用one-hot编码就可以很合理的计算出距离&#xff0c;那么就没必要进行one-hot编码。离散特征进行one-hot编码&#xff0c;编码后的特征&#xff0c;其实每一维度的特征都可以看做是连续的特征。就可以跟对连续型特征的归一化方法一样&#xff0c;对每一维特征进行归一化。比如归一化到[-1,1]或归一化到均值为0,方差为1。
基于树的方法是不需要进行特征的归一化&#xff0c;例如随机森林&#xff0c;bagging 和 boosting等。基于参数的模型或基于距离的模型&#xff0c;都是要进行特征的归一化。Tree Model不太需要one-hot编码&#xff1a; 对于决策树来说&#xff0c;one-hot的本质是增加树的深度。
one hot encoding的优点就是它的值只有0和1&#xff0c;不同的类型存储在垂直的空间。缺点就是&#xff0c;当类别的数量很多时&#xff0c;特征空间会变得非常大。在这种情况下&#xff0c;一般可以用PCA来减少维度。而且one hot encoding&#43;PCA这种组合在实际中也非常有用。总的来说&#xff0c;要是one hot encoding的类别数目不太多&#xff0c;建议优先考虑。
# 简单来说 LabelEncoder 是对不连续的数字或者文本进行编号
# sklearn.preprocessing.LabelEncoder()&#xff1a;标准化标签&#xff0c;将标签值统一转换成range(标签值个数-1)范围内from sklearn.preprocessing import LabelEncoder
le &#61; LabelEncoder()
le.fit([1,5,67,100])
le.transform([1,1,100,67,5])
out&#xff1a; array([0, 0, 3, 2, 1], dtype&#61;int64)#OneHotEncoder 用于将表示分类的数据扩维&#xff1a;
from sklearn.preprocessing import OneHotEncode
ohe &#61; OneHotEncoder()
ohe.fit([[1],[2],[3],[4]])
ohe.transform([[2],[3],[1],[4]]).toarray()
out&#xff1a;array([[ 0., 1., 0., 0.],[ 0., 0., 1., 0.],[ 1., 0., 0., 0.],[ 0., 0., 0., 1.]])
Examples--------Given a dataset with three features and four samples, we let the encoderfind the maximum value per feature and transform the data to a binaryone-hot encoding.>>> from sklearn.preprocessing import OneHotEncoder>>> enc &#61; OneHotEncoder()>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], \
[1, 0, 2]]) # doctest: &#43;ELLIPSISOneHotEncoder(categorical_features&#61;&#39;all&#39;, dtype&#61;<... &#39;numpy.float64&#39;>,handle_unknown&#61;&#39;error&#39;, n_values&#61;&#39;auto&#39;, sparse&#61;True)>>> enc.n_values_array([2, 3, 4])>>> enc.feature_indices_array([0, 2, 5, 9])>>> enc.transform([[0, 1, 1]]).toarray()array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]])
Examples--------&#96;LabelEncoder&#96; can be used to normalize labels.>>> from sklearn import preprocessing>>> le &#61; preprocessing.LabelEncoder()>>> le.fit([1, 2, 2, 6])LabelEncoder()>>> le.classes_array([1, 2, 6])>>> le.transform([1, 1, 2, 6]) #doctest: &#43;ELLIPSISarray([0, 0, 1, 2]...)>>> le.inverse_transform([0, 0, 1, 2])array([1, 1, 2, 6])It can also be used to transform non-numerical labels (as long as they arehashable and comparable) to numerical labels.>>> le &#61; preprocessing.LabelEncoder()>>> le.fit(["paris", "paris", "tokyo", "amsterdam"])LabelEncoder()>>> list(le.classes_)[&#39;amsterdam&#39;, &#39;paris&#39;, &#39;tokyo&#39;]>>> le.transform(["tokyo", "tokyo", "paris"]) #doctest: &#43;ELLIPSISarray([2, 2, 1]...)>>> list(le.inverse_transform([2, 2, 1]))[&#39;tokyo&#39;, &#39;tokyo&#39;, &#39;paris&#39;]
LabelEncoder和OneHotEncoder 在特征工程中的应用
下面引入scikit learn中的OneHotEncoder的介绍。
http://scikit-learn.org/stable/modules/preprocessing.html#preprocessing
pd.get_dummies(prefix&#61;)
pandas的get_dummies()可以直接对变量进行one-hot编码&#xff0c;其中prefix是为one-hot编码后的变量进行命名。
get_dummies()也可以对某一列数据进行。
DF_dummies &#61; pd.get_dummies(DF_data["target"])
#setosa versicolor virginica
#0 1 0 0
#1 1 0 0
#2 1 0 0
#3 1 0 0
#4 1 0 0
#5 1 0 0
相关参考&#xff1a;
https://www.cnblogs.com/king-lps/p/7846414.html
https://blog.csdn.net/Mr_HHH/article/details/80006971