【问题标题】:One hot encoding train with values not present on test一个热编码火车,其值在测试中不存在
【发布时间】:2020-01-16 15:37:18
【问题描述】:

我有一个存储为 Dataframes 的训练集和测试集。我正在尝试对我的数据集上的名义特征进行 One-hot 编码。但我有以下问题:

  1. 总共有3个分类特征,但我不知道每个特征的值是多少,因为数据集很大。
  2. 测试集的值在训练集上不存在,因此当我进行 one-hot 编码时,训练集应该将向量标记为 0,以表示未见过的值。但正如我在 1 中提到的,我并不了解所有功能。
  3. 我发现我可以使用df = pd.get_dummies(df, prefix_sep='_') 进行一次热编码,该命令适用于所有分类特征,但我注意到它将新特征移动到训练数据帧的末尾,我认为这是一个问题,因为我们不知道哪个特征的索引。还有第 2 个问题,新的火车/集合应该具有相同的索引。

有没有自动化的方法来做到这一点?或者图书馆?

编辑

感谢以下答案,我能够对许多功能执行一次热编码。但是下面的代码给出了以下问题:

  1. 我认为scikit-learn 去除列标题并将结果生成为数组而不是 DataFrame
  2. 由于特征被剥离,我们不知道哪个向量属于哪个特征。即使我执行df_scaled = pd.DataFrame(ct.fit_transform(data2)) 将结果存储在数据帧中,创建的数据帧df_scaled 也没有标头,尤其是当标头在预处理后现在更改时。也许sklearn.preprocessing.OneHotEncoder 有一种方法可以跟踪新功能及其索引??

【问题讨论】:

    标签: python pandas scikit-learn


    【解决方案1】:

    pd.get_dummies 应该以一种允许您区分每个分类特征的方式来命名新列。如果你想给它一组自定义的前缀来使用,你可以使用prefix 参数。然后,您可以查看列列表以查看与每个特征对应的所有列。 (你不需要prefix_sep='_',这是默认的。)

    df = pd.get_dummies(df, prefix=['first_feature', 'second_feature', 'third_feature']
    first_feature_column_names = [c for c in df.columns if c.startswith('first_feature_')]
    

    您还可以一次对一个分类特征执行 one-hot 编码,如果这有助于您了解每个特征的列。

    df = pd.get_dummies(df, columns=['first_feature'])
    

    至于您的某些标签仅存在于您的测试集或训练集中的问题:如果df 将您的训练集和测试集包含在一起(并且您打算稍后将它们用sklearn.model_selection.train_test_split 之类的东西分开),那么仅存在于您的测试集中的任何特征都将在您的训练集中有一个全零列。显然,这实际上不会为您的模型提供任何价值,但它会使您的列索引保持一致。然而,在你的训练数据中没有一个在该特征中具有非零值的单热列实际上是没有意义的——它对你的模型没有影响。您可以使用sklearn.preprocessing.OneHotEncoder 避免训练和测试之间的错误和不一致的列索引。

    from sklearn.compose import ColumnTransformer
    from sklearn.preprocessing import Normalizer
    ct = ColumnTransformer([
        ("onehot", OneHotEncoder(handle_unknown='ignore'), ['first_feature', 'second_feature', 'third_feature']),
    ], remainder='passthrough')
    
    df_train = ct.fit_transform(df_train)
    df_test = ct.transform(df_test)
    
    # Or simply
    
    df = ct.fit_transform(df)
    

    handle_unknown 告诉它忽略(而不是抛出错误)初始训练集中不存在的任何值。

    【讨论】:

    • 我不太了解这个概念,所以 handle_unknown = 'ignore' 将标记初始训练集中不存在的任何值。知道该命令首先在训练集上运行,然后在测试集上运行。这是如何运作的?这就像相反的情况应该发生:它应该用 0 标记 THE TRAINING SET 上的看不见的功能?
    • 我已经尝试了两个答案的命令。 “fit_and_transform 产生”错误,也许是 fit_transform?结果也被转换为数组而不是 DataFrames。
    【解决方案2】:

    不要使用pd.get_dummies,它具有您发现的缺点,而是使用sklearn.preprocessing.OneHotEncoder。它会自动从您的训练数据中获取所有名义类别,然后根据训练步骤中确定的类别对您的测试数据进行编码。如果测试数据中有新类别,它只会将您的数据编码为 0。

    例子:

    from sklearn.preprocessing import OneHotEncoder
    import numpy as np
    
    x_train = np.array([["A1","B1","C1"],["A2","B1","C2"]])
    x_test = np.array([["A1","B2","C2"]]) # As you can see, "B2" is a new attribute for column B
    
    ohe = OneHotEncoder(handle_unknown = 'ignore') #ignore tells the encoder to ignore new categories by encoding them with 0's
    ohe.fit(x_train)
    print(ohe.transform(x_train).toarray())
    >>> array([[1., 0., 1., 1., 0.],
               [0., 1., 1., 0., 1.]])
    

    要按列获取训练集中的类别摘要,请执行以下操作:

    print(ohe.categories_)
    >>> [array(['A1', 'A2'], dtype='<U2'), 
         array(['B1'], dtype='<U2'), 
         array(['C1', 'C2'], dtype='<U2')]
    

    要将一个热门编码列映射到类别,请执行以下操作:

    print(ohe.get_feature_names())
    >>> ['x0_A1' 'x0_A2' 'x1_B1' 'x2_C1' 'x2_C2']
    

    最后,这是编码器处理新测试数据的方式:

    print(ohe.transform(x_test).toarray())
    >>> [[1. 0. 0. 0. 1.]] # 1 for A1, 0 for A2, 0 for B1, 0 for C1, 1 for C2
    

    编辑

    您似乎担心在进行编码后会丢失标签。实际上很容易回到这些,只需将答案包装在数据框中并指定来自ohe.get_feature_names()的列名:

    pd.DataFrame(ohe.transform(x_test).toarray(), columns = ohe.get_feature_names())
    

    【讨论】:

    • 感谢您的回答。该命令的输出适用于数组,我有一个 Dataframe。如何将结果转换为 DF?
    • @U.User,如果这确实是您需要作为 df 的东西,只需执行 pd.DataFrame(ohe.transform(x_test).toarray())
    • 感谢您的回答。您能否阅读有关标题问题的编辑?当我进行一次热编码时,标头会被剥离,经过预处理后,标头会发生变化。如果我想将新的标题添加到新的火车和相应的测试集,这可能吗?很抱歉给您带来不便,但我发现这么多线程需要不同的库,但实际上没有一个是一致的
    • 编辑了我的答案,很简单
    • 真的;谢谢您的帮助。您的方法似乎会导致记忆问题,因为我有很多观察结果。所以我改用了 ColumnTransformer 方法。但这似乎有点复杂,尽管它给出了正确的输出。这里的问题是我无法链接新功能名称。请阅读编辑。
    猜你喜欢
    • 1970-01-01
    • 2017-02-21
    • 2021-07-26
    • 1970-01-01
    • 2020-11-21
    • 2020-04-14
    • 2018-05-07
    • 2020-01-30
    • 2020-05-21
    相关资源
    最近更新 更多