Python sklearn.LabelEncoder具有以前从未见过的值

Python sklearn.LabelEncoder具有以前从未见过的值,python,scikit-learn,Python,Scikit Learn,如果在训练集上安装了sklearn.LabelEncoder,如果在测试集上使用时遇到新值,它可能会中断 对此,我能想到的唯一解决方案是将测试集中的所有新内容(即不属于任何现有类)映射到”,然后将相应的类显式添加到LabelEncoder: 训练和测试是熊猫。数据帧和c是任意列 le=标签编码() le.fit(列车[c]) 测试[c]=测试[c]。映射(lambda s:“”如果s不在le.classes\uElse s中) le.classes=np.append(le.classes) 列

如果在训练集上安装了
sklearn.LabelEncoder
,如果在测试集上使用时遇到新值,它可能会中断

对此,我能想到的唯一解决方案是将测试集中的所有新内容(即不属于任何现有类)映射到
,然后将相应的类显式添加到
LabelEncoder

训练和测试是熊猫。数据帧和c是任意列 le=标签编码() le.fit(列车[c]) 测试[c]=测试[c]。映射(lambda s:“”如果s不在le.classes\uElse s中) le.classes=np.append(le.classes) 列[c]=le.变换(列[c]) test[c]=le.transform(test[c]) 这是可行的,但有更好的解决方案吗

更新

正如@sapo_cosmico在一篇评论中指出的那样,鉴于我假设的是
LabelEncoder.transform
中的一个实现更改,上面的方法似乎不再有效了,它现在似乎使用了
np.searchsorted
(我不知道以前是否是这样)。因此,不需要将
类附加到
LabelEncoder
已提取类的列表中,而是需要按排序顺序插入该类:

导入对分 le_classes=le.classes_u.tolist() 左二等分(le_类,“”) 类=类
然而,总的来说,这感觉相当笨拙,我确信有更好的方法来解决这个问题。

我得到的印象是,当面对这种情况时,你所做的与其他人所做的非常相似

已经有一些努力将对看不见的标签进行编码的能力添加到LabelEncoder中(请特别参见和),但是更改现有行为实际上比乍一看要困难得多


目前看来,处理“词汇表外”标签的工作留给了scikit learn的个人用户。

由于这个看不见数据的问题,我最终选择了Pandas

  • 在训练数据上创建假人
    dummy\u train=pd.get\u dummie(train)
  • 在新的(看不见的数据)中创建假人
    dummy\u new=pd.get\u dummie(新数据)
  • 将新数据重新索引到训练数据的列中,用0填充缺少的值
    dummy\u new.reindex(列=dummy\u train.columns,填充值=0)

实际上,任何分类的新特性都不会进入分类器,但我认为这不会导致问题,因为它不知道如何处理它们

我认识两位开发人员,他们正在变压器和管道周围制作包装。他们有2个健壮的编码器转换器(一个虚拟编码器和一个标签编码器),可以处理看不见的值。搜索
skutil.preprocessing.OneHotCategoricalEncoder
skutil.preprocessing.SafeLabelEncoder
。在他们的
SafeLabelEncoder()
中,未看到的值自动编码为9999999。

我试图解决这个问题,并找到了两种简便的方法来编码使用和不使用LabelEncoder的训练集和测试集的分类数据。 新的类别用一些已知的词组“c”(如“other”或“missing”)填充。第一种方法似乎工作得更快。希望这对你有帮助

import pandas as pd
import time
df=pd.DataFrame()

df["a"]=['a','b', 'c', 'd']
df["b"]=['a','b', 'e', 'd']


#LabelEncoder + map
t=time.clock()
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
suf="_le"
col="a"
df[col+suf] = le.fit_transform(df[col])
dic = dict(zip(le.classes_, le.transform(le.classes_)))
col='b'
df[col+suf]=df[col].map(dic).fillna(dic["c"]).astype(int)
print(time.clock()-t)

#---
#pandas category

t=time.clock()
df["d"] = df["a"].astype('category').cat.codes
dic =df["a"].astype('category').cat.categories.tolist()
df['f']=df['b'].astype('category',categories=dic).fillna("c").cat.codes
df.dtypes
print(time.clock()-t)

如果只是为了训练和测试一个模型,为什么不在整个数据集上加上标签呢。然后使用编码器对象生成的类

encoder = LabelEncoder()
encoder.fit_transform(df["label"])
train_y = encoder.transform(train_y)
test_y = encoder.transform(test_y)

我最近遇到了这个问题,能够很快地找到解决问题的方法。我的回答不仅仅解决了这个问题,它也很容易解决你的问题。(我觉得很酷)

我正在使用pandas数据帧,最初使用sklearns labelencoder()对我的数据进行编码,然后将其pickle以用于程序中的其他模块

但是,sklearn预处理中的标签编码器无法向编码算法添加新值。我解决了对多个值进行编码、保存映射值以及向编码器添加新值的问题(以下是我所做工作的大致概述):

然后,您只需将字典保存到JSON文件中,就可以通过添加新值和相应的整数值来提取字典并添加任何您想要的值

我将解释使用map()而不是replace()背后的一些原因。我发现使用pandas replace()函数迭代大约117000行代码需要一分钟的时间。使用map使时间略超过100毫秒


TLDR:不使用预处理,只需制作一个映射字典并自己映射出值来处理数据帧。

LabelEncoder基本上是一个字典。您可以将其提取并用于将来的编码:

from sklearn.preprocessing import LabelEncoder

le = preprocessing.LabelEncoder()
le.fit(X)

le_dict = dict(zip(le.classes_, le.transform(le.classes_)))
检索单个新项目的标签,如果项目缺失,则将值设置为未知

le_dict.get(new_item, '<Unknown>')
le_dict.get(新项“”)
检索Dataframe列的标签:

df[your_col] = df[your_col].apply(lambda x: le_dict.get(x, <unknown_value>))
df[your_col]=df[your_col].apply(lambda x:le_dict.get(x,))

我创建了一个类来支持这一点。如果您有一个新标签,这将分配它作为未知类

from sklearn.preprocessing import LabelEncoder
import numpy as np


class LabelEncoderExt(object):
    def __init__(self):
        """
        It differs from LabelEncoder by handling new classes and providing a value for it [Unknown]
        Unknown will be added in fit and transform will take care of new item. It gives unknown class id
        """
        self.label_encoder = LabelEncoder()
        # self.classes_ = self.label_encoder.classes_

    def fit(self, data_list):
        """
        This will fit the encoder for all the unique values and introduce unknown value
        :param data_list: A list of string
        :return: self
        """
        self.label_encoder = self.label_encoder.fit(list(data_list) + ['Unknown'])
        self.classes_ = self.label_encoder.classes_

        return self

    def transform(self, data_list):
        """
        This will transform the data_list to id list where the new values get assigned to Unknown class
        :param data_list:
        :return:
        """
        new_data_list = list(data_list)
        for unique_item in np.unique(data_list):
            if unique_item not in self.label_encoder.classes_:
                new_data_list = ['Unknown' if x==unique_item else x for x in new_data_list]

        return self.label_encoder.transform(new_data_list)
示例用法:

country_list = ['Argentina', 'Australia', 'Canada', 'France', 'Italy', 'Spain', 'US', 'Canada', 'Argentina, ''US']

label_encoder = LabelEncoderExt()

label_encoder.fit(country_list)
print(label_encoder.classes_) # you can see new class called Unknown
print(label_encoder.transform(country_list))


new_country_list = ['Canada', 'France', 'Italy', 'Spain', 'US', 'India', 'Pakistan', 'South Africa']
print(label_encoder.transform(new_country_list))

我面临着同样的问题,并意识到我的编码器不知何故在我的列数据帧中混合了值。假设您为多个列运行编码器,当为标签分配数字时,编码器会自动向其中写入数字,有时会发现您有两个具有相似值的不同列。我解决这个问题的方法是为pandas DataFrame中的每一列创建一个LabelEncoder()实例,我得到了一个很好的结果

encoder1 = LabelEncoder()
encoder2 = LabelEncoder()
encoder3 = LabelEncoder()

df['col1'] = encoder1.fit_transform(list(df['col1'].values))
df['col2'] = encoder2.fit_transform(list(df['col2'].values))
df['col3'] = encoder3.fit_transform(list(df['col3'].values))

问候

以下是使用熊猫的相对较新功能。主要动机是像“lightgbm”这样的机器学习包可以接受熊猫类别作为特征列,并且在某些情况下比使用onehotencoding更好。在这个例子中,变压器
encoder1 = LabelEncoder()
encoder2 = LabelEncoder()
encoder3 = LabelEncoder()

df['col1'] = encoder1.fit_transform(list(df['col1'].values))
df['col2'] = encoder2.fit_transform(list(df['col2'].values))
df['col3'] = encoder3.fit_transform(list(df['col3'].values))
One Two
0   0   0
1   1   1
2   2   1
3   2   2
    One Two
0   0   2
1   1   -1
2   2   0
3   -1  1
4   -1  -1
One Two
0   A   d
1   B   NaN
2   C   b
3   NaN c
4   NaN NaN
for l in enc_list:  

    old_list = enc_map[l].classes_
    new_list = df[l].unique()
    na = [j for j in new_list if j not in old_list]
    df[l] = df[l].replace(na,'NA')