字符串类别特征的独热编码

39

我正在尝试对一个简单的数据集进行独热编码。

data = [['a', 'dog', 'red']
        ['b', 'cat', 'green']]

如何使用Scikit-Learn对这些数据进行预处理?

最初的想法是使用Scikit-Learn的OneHotEncoder。但是独热编码器不支持字符串作为特征;它只离散化整数。

那么你会使用LabelEncoder,将字符串编码为整数。但是然后你必须将标签编码器应用于每个列,并存储每一个标签编码器(以及它们被应用的列)。这感觉非常笨重。

所以,在Scikit-Learn中做到最好的方法是什么?

请不要建议pandas.get_dummies。那是我现在通常用于一次性编码的方法。但是它有限制,即不能分别编码训练/测试集。


1
同时,pandas.get_dummies二进制编码被决策树分类器视为连续型数据,因此在该场景下不适用。 - vagabond
3个回答

12

如果您使用的是sklearn>0.20.dev0版本

In [11]: from sklearn.preprocessing import OneHotEncoder
    ...: cat = OneHotEncoder()
    ...: X = np.array([['a', 'b', 'a', 'c'], [0, 1, 0, 1]], dtype=object).T
    ...: cat.fit_transform(X).toarray()
    ...: 
Out[11]: array([[1., 0., 0., 1., 0.],
           [0., 1., 0., 0., 1.],
           [1., 0., 0., 1., 0.],
           [0., 0., 1., 0., 1.]])

如果您使用的是sklearn==0.20.dev0版本

In [30]: cat = CategoricalEncoder()

In [31]: X = np.array([['a', 'b', 'a', 'c'], [0, 1, 0, 1]], dtype=object).T

In [32]: cat.fit_transform(X).toarray()
Out[32]:
array([[ 1.,  0., 0.,  1.,  0.],
       [ 0.,  1.,  0.,  0.,  1.],
       [ 1.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  1.,  0.,  1.]])

另一种方法是使用category_encoders。
以下是一个示例:
% pip install category_encoders
import category_encoders as ce
le =  ce.OneHotEncoder(return_df=False, impute_missing=False, handle_unknown="ignore")
X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']])
le.fit_transform(X)
array([[1, 0, 1, 0, 1, 0],
       [0, 1, 0, 1, 0, 1]])

6
CategorialEncoder已与OneHotEncoder合并,因此在当前版本的sklearn==0.20.dev0中,该功能已包含在其中。 - Kay Wittig

5
非常好的问题。
然而,在某种程度上,这是一种经常出现(至少对我来说)的私有情况 - 鉴于适用于X矩阵子集的sklearn阶段,我想应用(可能有几个)给定整个矩阵。例如,在这里,您有一个知道在单个列上运行的阶段,并且您想要将其应用三次 - 每列一次。
这是使用复合设计模式的经典案例。
这是一个(草图式的)可重复使用的阶段,它接受一个将列索引映射到要应用于其中的转换的字典:
class ColumnApplier(object):
    def __init__(self, column_stages):
        self._column_stages = column_stages

    def fit(self, X, y):
        for i, k in self._column_stages.items():
            k.fit(X[:, i])

        return self

    def transform(self, X):
        X = X.copy()
        for i, k in self._column_stages.items():
            X[:, i] = k.transform(X[:, i])

        return X

现在,要在此上下文中使用它,从以下开始
X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']])
y = np.array([1, 2])
X

你只需使用它将每个列索引映射到所需的转换即可:
multi_encoder = \
    ColumnApplier(dict([(i, preprocessing.LabelEncoder()) for i in range(3)]))
multi_encoder.fit(X, None).transform(X)

一旦你开发出这样一个阶段(我不能发布我使用的那个),你可以在各种设置中多次使用它。

2
说实话,我以前已经创建过类似的东西了。但感觉很笨重。Scikit-Learn 应该有一个抽象这个设计模式的类,因为它是一种常见的设计模式。如果没有的话,我认为为此提交一个 PR 是合适的。 - hlin117
你的解决方案在这个数据上出现了问题:X = np.array([['cat'],['dog','cat'],['pet','man'],['cat']]) y = [1,2,3,4] - Stepan Yakovenko
我通常也会做类似的事情,但已知的缺陷是当LabelEncoder#transform看到在训练中没有出现过的字符串时会崩溃。 - ldavid
1
fit_transform 不应该能够纠正这个问题吗? - user2755526

3
我曾多次遇到这个问题,并在此书的第100页找到了解决方案:

我们可以使用LabelBinarizer类一次性应用两种转换(从文本类别到整数类别,然后从整数类别到独热向量):

示例代码如下:

from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(data)
housing_cat_1hot

结果如下: 注意,默认情况下,这将返回一个密集的NumPy数组。您可以通过将sparse_output=True传递给LabelBinarizer构造函数来获得稀疏矩阵。

关于LabelBinarizer,您可以在sklearn官方文档这里找到更多信息。


这在Windows 10上的Python 3.6中失败了。>>> housing_cat_1hot = encoder.fit_transform(data) ----------- Traceback(最近的调用最后):文件“<pyshell#11>”,第1行,in <module>,,,,, - user2120235
这不是一种独热编码,而是虚拟编码。 - zipp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接