【问题标题】:Sklearn Labelencoder keep encoded values when encoding new dataframeSklearn Labelencoder 在编码新数据帧时保留编码值
【发布时间】:2020-03-04 09:24:22
【问题描述】:

我正在编写一个脚本,该脚本使用“局部异常因子”算法进行“新奇检测”。 在这种情况下,我们需要在进行预测之前“拟合”一个“干净/训练”的数据框。 为了使算法正常工作,我们需要对数据帧中的值进行编码,例如将“vrrp”编码为“0”,将“udp”编码为“2”,依此类推。 为此,我使用了 sklearn 的 LabelEncoder(),它使我能够将编码的数据帧传递给算法。

encoder = LabelEncoder()
dataEnc = dataEnc.apply(encoder.fit_transform)

...

dataframeEnc = dataframeEnc.apply(encoder.fit_transform)

其中“dataEnc”是训练数据集,“dataframeEnc”是用于进行预测的数据集。

当我尝试使用新数据帧进行预测时出现问题:对于相同的原始值,“训练”的编码值与“预测”数据帧的编码值不同。

我的目标是在编码新数据帧时保持生成的编码值参考原始值。

在对“Training”数据帧进行编码时,例如,在对值“10.67.21.254”进行编码时,它总是编码为“23”。 然而,当编码一个新的数据帧(验证数据帧)时,相同的值会产生不同的编码值,在我的例子中是'1'。

作为我所期望的一个例子是:

10.67.21.254       234.1.2.88      0      0     udp  3.472 KB       62

编码为:

23     153      0      0         4  1254       61          0

预计,对于相同的原始值,它会编码 到相同的编码值,但是,我再次编码后得到的是:

1       1      0      0         1     2        2          0

我认为它所做的是基于同一数据集的其他值为数据集上的每一行赋予新值。

然后我的问题是:如何确保在对新数据集(预测)的值进行编码时,我得到与前一个(训练)数据集相同的编码值?

【问题讨论】:

  • 你应该只对测试数据使用 .tranform(),而不是 .fit_transform()。
  • @KRKirov 在测试数据上使用dataframeEnc = dataframeEnc.apply(encoder.transform) 时,它可能是一个新的、非种子值,然后会导致ValueError: ("y contains previously unseen labels: '11.31.77.119'", :(
  • 是的,这是通常的痛苦。在你的训练中引入一个“unk”类别并将数据中任何以前看不见的值设置为“unk”可以帮助解决这个问题。或者,您可以使用 scikit-learn 的 OneHotEncoder 和 handle_unknown='ignore' 来自动处理这个问题。发布十行训练数据和几个验证数据也会有所帮助。
  • @KRKirov 它会解决它引发错误是的。但是,它会破坏我的目标:检测新奇事物。我想要发生的是:有 200 个不同的 IP,每个都从 0 编码到 199。当看到一个新 IP 时,它应该编码为 200 而不是 0 或无:使用 handle_unknown='ignore' 会发生什么是每个非-known 值变为 0 并将其反转为无(null),这对我的用例无用:\ TLDR:应该将新的未知值“添加”到编码中,而不是全部变为 0 (忽略)。

标签: python pandas scikit-learn sklearn-pandas


【解决方案1】:

自定义转换器应该会有所帮助。如果要转换整个数据帧,则必须创建一个循环并创建一个编码器字典。

import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator
from sklearn.base import TransformerMixin


class TTLabelEncoder(BaseEstimator, TransformerMixin):
    """Transform data frame columns with different categorical values
    in training and test data. TT stands for Train-Test

    Pass individual data frame columns to the class instance"""

    def __init__(self):
        self.code_dic = None
        self.max_code = None
        self.fitted = False

    def fit(self, df):
        self.code_dict = dict(zip(df.unique(),
                                  np.arange(len(df.unique()))))
        self.__max_code__()
        self.fitted = True
        return self

    def transform(self, df):
        assert self.fitted == True, 'Fit the data before transforming.'
        new_cat = set(df.unique()).difference(set(self.code_dict.keys()))
        if new_cat:
            new_codes = dict(zip(new_cat, 
                     np.arange(len(new_cat)) + self.max_code + 1))
            self.code_dict.update(new_codes)
            self.__max_code__()
        return df.map(self.code_dict)

    def __max_code__(self):
        self.max_code = max(self.code_dict.values())
        return self

    def fit_transform(self, df):
        if self.fitted == False:
            self.fit(df)
        df = self.transform(df)
        return df

df_1 = pd.DataFrame({'IP': np.random.choice(list('ABCD'), size=5),
                   'Counts': np.random.randint(10, 20, size=5)})

df_2 = pd.DataFrame({'IP': np.random.choice(list('DEF'), size=5),
                     'Counts': np.random.randint(10, 20, size=5)})

df_3 = pd.DataFrame({'IP': np.random.choice(list('XYZ'), size=5),
                     'Counts': np.random.randint(10, 20, size=5)})

ip_encoder = TTLabelEncoder()
ip_encoder.fit(df_1['IP'])
ip_encoder.code_dict

df_1['IP'] = ip_encoder.transform(df_1['IP'])
df_2['IP'] = ip_encoder.transform(df_2['IP'])
df_3['IP'] = ip_encoder.fit_transform(df_3['IP'])

输出:

 df_1 #Before transformation
Out[54]: 
  IP  Counts
0  D      11
1  C      16
2  B      14
3  A      15
4  D      14

df_1 #After transformation
Out[58]: 
   IP  Counts
0   0      11
1   1      16
2   2      14
3   3      15
4   0      14

df_2 #Before transformation
Out[62]: 
  IP  Counts
0  F      15
1  D      10
2  E      19
3  F      18
4  F      14

df_2 #After transformation
Out[64]: 
   IP  Counts
0   4      15
1   0      10
2   5      19
3   4      18
4   4      14

df_3 #Before tranformation
Out[66]: 
  IP  Counts
0  X      19
1  Z      18
2  X      12
3  X      13
4  Y      18

df_3
Out[68]: #After tranformation
   IP  Counts
0   7      19
1   6      18
2   7      12
3   7      13
4   8      18

ip_encoder.code_dict
Out[69]: {'D': 0, 'C': 1, 'B': 2, 'A': 3, 'F': 4, 'E': 5, 'Z': 6, 'X': 7, 'Y': 8}

【讨论】:

  • 对于迟到的反馈,我深表歉意。感谢您的回答,将尽快尝试!另外,字典中有多少条目有限制吗?我猜我必须考虑可扩展性
  • 经过测试并按预期工作!通过一些调整,它可以满足我的用例的预期目的!谢谢 :) 希望有同样问题的人能找到这个
  • 太棒了!我很高兴听到这个消息。
猜你喜欢
  • 2020-12-22
  • 2018-08-02
  • 2018-12-20
  • 2017-02-16
  • 2015-07-21
  • 2021-01-26
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
相关资源
最近更新 更多