【问题标题】:sklearn ColumnTransformer based preprocessor outputs different columns on Train and Test dataset基于 sklearn ColumnTransformer 的预处理器在训练和测试数据集上输出不同的列
【发布时间】:2023-04-08 22:38:01
【问题描述】:

我试图学习如何使用 Pipelines 和 ColumnTransformer 在回归之前有效地预处理数据。 这是我的尝试:

  def preprocess_data(X):
    cat_var = X.select_dtypes(['bool','object']).columns
    num_var = X.select_dtypes(['int64','float64']).columns

    steps = [('c', Pipeline(steps=[('s',SimpleImputer(strategy='most_frequent')),
                                   ('oe',OneHotEncoder(handle_unknown='ignore'))]), cat_var),
             ('n', SimpleImputer(strategy='median'), num_var)]

    transformer = ColumnTransformer(transformers=steps, remainder='passthrough')


    X = transformer.fit_transform(X=X)

    return X

问题在于,当我转换训练数据集和测试数据集时,它为测试数据集选择了不同数量(更少)的列。这意味着我训练的模型不适用于测试数据。

我已经很好地修改了代码,没有数据泄露或相关问题

这可能是什么原因?我的逻辑有问题吗?

我正在使用以下dataset

谢谢!

解决方案: 返回变压器以及预处理数据。

   return X, transformer 

然后在预测之前使用transformer object对测试数据进行转换

【问题讨论】:

    标签: python scikit-learn


    【解决方案1】:

    如果 test 中的某个分类列在 train 中没有类别,那么您最终会得到更少的列。您可以在下面看到使用 onehot 转换的每个变量的分类级别数:

    import pandas as pd
    from sklearn.impute import SimpleImputer
    from sklearn.pipeline import Pipeline
    from sklearn.compose import ColumnTransformer
    from sklearn.preprocessing import OneHotEncoder
    
    train = pd.read_csv("train.csv")
    test = pd.read_csv("test.csv")
    
    COL = x.select_dtypes(['bool','object']).columns
    pd.DataFrame({'var':COL,
                  'n_train':[len(train[i].unique()) for i in COL],
                  'n_test':[len(test[i].unique()) for i in COL]})
    
    
        var n_train n_test
    0   MSZoning    5   6
    1   Street  2   2
    2   Alley   3   3
    3   LotShape    4   4
    4   LandContour 4   4
    5   Utilities   2   2
    6   LotConfig   5   5
    7   LandSlope   3   3
    8   Neighborhood    25  25
    9   Condition1  9   9
    10  Condition2  8   5
    11  BldgType    5   5
    12  HouseStyle  8   7
    13  RoofStyle   6   6
    14  RoofMatl    8   4
    15  Exterior1st 15  14
    16  Exterior2nd 16  16
    17  MasVnrType  5   5
    18  ExterQual   4   4
    19  ExterCond   5   5
    20  Foundation  6   6
    21  BsmtQual    5   5
    22  BsmtCond    5   5
    23  BsmtExposure    5   5
    24  BsmtFinType1    7   7
    25  BsmtFinType2    7   7
    26  Heating 6   4
    27  HeatingQC   5   5
    28  CentralAir  2   2
    29  Electrical  6   4
    30  KitchenQual 4   5
    31  Functional  7   8
    32  FireplaceQu 6   6
    33  GarageType  7   7
    34  GarageFinish    4   4
    35  GarageQual  6   5
    36  GarageCond  6   6
    37  PavedDrive  3   3
    38  PoolQC  4   3
    39  Fence   5   5
    40  MiscFeature 5   4
    41  SaleType    9   10
    42  SaleCondition   6   6
    

    您需要使测试集变量具有相同定义的类别,请参阅the examples here

    【讨论】:

    • 这不适用于此处,因为 OP 在管道中指定了handle_unknown='ignore',这使得转换器忽略了确实出现在测试集中但不在训练集中的类别。
    • 如果您分别处理训练和测试,忽略与否无关紧要。尝试运行代码@00schneider
    • @00schneider StupidWolf ,您的两个答案都是正确的。我不得不重用适合训练数据的转换器对象来转换我的测试数据。我会更新我的问题。
    • 如果您认为某个答案确实回答了您的问题,那么您可以考虑将此答案标记为已接受的答案。
    【解决方案2】:

    你不能使用这个函数来训练和测试集,因为你会 fit_transform 两次。您需要将变换器与训练数据相匹配,但仅对测试数据进行变换。我建议为此使用 sklearn 管道,它会自动执行此过程,例如:

    pipeline = Pipeline(
    [
        ('preprocessing', preprocessor),
        ('clf', MLalgorithm())
    ]
    

    )

    猜你喜欢
    • 2020-06-26
    • 2019-03-15
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 2018-05-07
    • 2018-08-18
    相关资源
    最近更新 更多