【问题标题】:sklearn.preprocessing.OneHotEncoder: using drop and handle_unknown='ignore'sklearn.preprocessing.OneHotEncoder:使用 drop 和 handle_unknown='ignore'
【发布时间】:2020-05-17 09:27:21
【问题描述】:

我有一些pandas.Series - s,下面 - 我想一次性编码。我通过研究发现'b' 级别对于我的预测建模任务并不重要。我可以像这样从我的分析中排除它:

import pandas as pd
from sklearn.preprocessing import OneHotEncoder

s = pd.Series(['a', 'b', 'c']).values.reshape(-1, 1)

enc = OneHotEncoder(drop=['b'], sparse=False, handle_unknown='error')
enc.fit_transform(s)
# array([[1., 0.],
#        [0., 0.],
#        [0., 1.]])
enc.get_feature_names()
# array(['x0_a', 'x0_c'], dtype=object)

但是当我去转换一个新系列时,一个同时包含'b' 和一个新关卡'd',我得到一个错误:

new_s = pd.Series(['a', 'b', 'c', 'd']).values.reshape(-1, 1)
enc.transform(new_s)

Traceback(最近一次调用最后一次): 文件“”,第 1 行,在 文件“/Users/user/Documents/assets/envs/data-science/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py”,第 390 行,在转换中 X_int, X_mask = self._transform(X, handle_unknown=self.handle_unknown) 文件“/Users/user/Documents/assets/envs/data-science/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py”,第 124 行,在 _transform 引发 ValueError(味精) ValueError:在转换期间在第 0 列中发现未知类别 ['d']

这是意料之中的,因为我在上面设置了handle_unknown='error'。但是,我想在拟合和后续转换步骤中完全忽略除['a', 'c'] 之外的所有类。我试过这个:

enc = OneHotEncoder(drop=['b'], sparse=False, handle_unknown='ignore')
enc.fit_transform(s)
enc.transform(new_s)

Traceback(最近一次调用最后一次): 文件“”,第 1 行,在 文件“/Users/user/Documents/assets/envs/data-science/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py”,第 371 行,在 fit_transform self._validate_keywords() _validate_keywords 中的文件“/Users/user/Documents/assets/envs/data-science/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py”,第 289 行 "handle_unknown 在 drop 参数为 " 时必须为 'error' ValueError: handle_unknown 在指定 drop 参数时必须为 'error',因为两者都会创建全为零的类别。

scikit-learn 似乎不支持这种模式。有谁知道完成这项任务的 scikit-learn 兼容模式?

【问题讨论】:

    标签: python machine-learning scikit-learn


    【解决方案1】:

    看起来sklearn.preprocessing.LabelBinarizer 可以用于这个用例,因为它没有任何参数来指定是错误输出还是忽略新类:

    >>> import pandas as pd
    >>> from sklearn.preprocessing import LabelBinarizer
    >>> s = pd.Series(['a', 'b', 'c']).values.reshape(-1, 1)
    >>> enc = LabelBinarizer()
    >>> enc.fit_transform(s)
    array([[1, 0, 0],
           [0, 1, 0],
           [0, 0, 1]])
    >>> enc.classes_
    array(['a', 'b', 'c'], dtype='<U1')
    >>> new_s = pd.Series(['a', 'b', 'c', 'd']).values.reshape(-1, 1)
    >>> enc.transform(new_s)
    array([[1, 0, 0],
           [0, 1, 0],
           [0, 0, 1],
           [0, 0, 0]])
    

    【讨论】:

    • 而且,是的,我在提供赏金后立即意识到这一点......
    • 但在第一种情况下,从pd.Series(['a', 'b', 'c']) 中删除功能bpd.Series(['a', 'c'])LabelBinarizer 返回array([[0], [1]]),而不是您在问题中想要的array([[1., 0.], [0., 0.], [0., 1.]])。在LabelBinarizer 的其他方式中,如果您在pd.Series(['a', 'b', 'c'])transform 上的fit_transformpd.Series(['a', 'c']),它返回array([[1, 0, 0],, [0, 0, 1]])。它仍然不是您想要的输出,因为使用 OneHotEncoderdrop 参数
    • 好点。我想我可以在LabelBinarizer 上编写一个类,添加一个像这样的categories 参数,然后做一些类似的事情,就像我对IgnorantOneHotEncoder 所做的那样。
    【解决方案2】:

    您也可以使用以下方法来解决此问题:

    class IgnorantOneHotEncoder(OneHotEncoder):
        def transform(self, X, y=None):
            try:
                return super().transform(X)
            except ValueError as e:
                if 'Found unknown categories' in str(e):
                    X = np.copy(X)
                    # Keep track of indices corresponding to unknown categories
                    unknown_categories_mask = ~np.isin(X, self.categories_[0]).ravel()
                    # Overwrite the unknown categories in the input matrix, X, with the first known category
                    X[unknown_categories_mask] = self.categories_[0][0]
                    # Transform X, whose categories are all known now
                    X = super().transform(X)
                    # Overwrite originally unknown-category records with 0 to indicate
                    # absence of any value for any category for that feature
                    X[unknown_categories_mask, 0] = 0
                    return X
                else:
                    raise
    

    试试看:

    >>> ienc = IgnorantOneHotEncoder(sparse=False)
    >>> ienc.fit(s)
    IgnorantOneHotEncoder(sparse=False)
    >>> ienc.transform(s)
    array([[1., 0., 0.],
           [0., 1., 0.],
           [0., 0., 1.]])
    >>> ienc.transform(new_s)
    array([[1., 0., 0.],
           [0., 1., 0.],
           [0., 0., 1.],
           [0., 0., 0.]])
    

    【讨论】:

      猜你喜欢
      • 2019-10-29
      • 2020-02-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-31
      • 2020-05-31
      • 2018-05-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多