One-Hot-Encoding 训练和测试数据时的形状不匹配。在将 get_dummies 与管道一起使用时,Train_Data 具有比 Test_data 更多的虚拟列

Posted

技术标签:

【中文标题】One-Hot-Encoding 训练和测试数据时的形状不匹配。在将 get_dummies 与管道一起使用时,Train_Data 具有比 Test_data 更多的虚拟列【英文标题】:Shape mismatch when One-Hot-Encoding Train and Test data. Train_Data has more Dummy Columns than Test_data while using get_dummies with pipeline 【发布时间】:2019-06-26 23:46:49 【问题描述】:

我正在尝试为我的数据创建一个 get_dummies 类,稍后我想在管道中使用它:

class Dummies(BaseEstimator, TransformerMixin):
     def transform(self, df):
           dummies=pd.get_dummies(df[self.cat],drop_first=True) ## getting dummy cols
           df=pd.concat([df,dummies],axis=1) ## concatenating our dummies
           df.drop(self.cat,axis=1,inplace=True) ## dropping our original cat_cols

     def fit(self, df):
           self.cat=[]    
           for i in df.columns.tolist():    
               if i[0]=='c': ## My data has categorical cols start with 'c'  
                  self.cat.append(i)  ## Storing all my categorical_columns for dummies
              else:
                continue

现在当我在 X_train 上调用 fit_transform 然后转换 X_test

z=Dummies()
X_train=z.fit_transform(X_train)
X_test=z.transform(X_test)

X_train 和 X_test 形状的列不同:

X_train.shape
X_test.shape

输出:

(10983, 1797) (3661, 1529)

X_train 中的 Dummies 比我的 X_test 中的多。 显然,我的 X_test 的类别比 X_train 少。如何在我的类中编写逻辑,以便 X_test 中的类别广播到 X_train 的形状?我希望 X_test 具有与我的 X_train 相同数量的虚拟变量。

【问题讨论】:

【参考方案1】:

您可以附加两个数据帧,然后执行 get_dummies()。

【讨论】:

是的,我可以做到,但我想稍后在为 ex 构建模型时使用管道:pipe1=make_pipeline(Dummies(), StandardScaler(), PCA(n_components=7), LogisticRegression()) 然后我适合我的火车 pipe1.fit(X_train,y_train) 然后我得到我的分数 pipe1.score(X_test, y_test)。在这里,我不必显式地 OHE 和扩展我的 X_train 或 X_test。 pipe.fit 会为我做这件事。它首先适合 X_train,然后它会转换 X_test,然后给我 LogReg 模型的分数。【参考方案2】:

如果我们从两个小的示例数据框开始:

train = pd.DataFrame('job=carpenter': [0, 1, 0],
                   'job=plumber': [0, 0, 1],
                   'job=electrician': [1, 0, 0])

    job=carpenter   job=plumber  job=electrician
0               0             0                1
1               1             0                0
2               0             1                0


test = pd.DataFrame('job=carpenter': [0, 1, 0],
                   'job=plumber': [1, 1, 0])

    job=carpenter   job=plumber
0               0             1
1               1             1
2               0             0

我们可以使用字典推导来获取测试集中缺少的训练集中的每一列,并将其赋值为 0,然后将使用该值将特定列添加到测试集中并填充它带零(因为测试集中没有任何行包含任何这些缺失的类别):

train_cols = list(train.columns)
test_cols = list(test.columns)
cols_not_in_test = c:0 for c in train_cols if c not in test_cols
test = test.assign(**cols_not_in_test)

这为我们提供了以下测试数据框:

test

   job=carpenter   job=plumber  job=electrician
0              0             1                0
1              1             1                0
2              0             0                0

【讨论】:

【参考方案3】:

你想在这里使用的(我认为)是 scikit learn 的 OneHotEncoder

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncode(categories = "auto")
X_train_encoded = encoder.fit_transform("X_train")
X_test_encoded = encoder.transform("X_test")

这将保留fit_transform 语法并确保 X_test_encoded 与 X_train_encoded 具有相同的形状。它也可以在您提到的管道中使用,而不是Dummies()。示例:

pipe1=make_pipeline(OneHotEncoder(categories = "auto"), StandardScaler(), PCA(n_components=7), LogisticRegression())

【讨论】:

以上是关于One-Hot-Encoding 训练和测试数据时的形状不匹配。在将 get_dummies 与管道一起使用时,Train_Data 具有比 Test_data 更多的虚拟列的主要内容,如果未能解决你的问题,请参考以下文章

从 scikit-learn 中的 one-hot-encoding 回溯分类特征?

在 sklearn 和命名列中对多个列进行 One-hot-encoding

当测试和训练数据集来自不同来源时,为啥测试准确性保持不变并且不会增加二进制分类

有人可以解释为啥我们在将数据拆分为训练和测试时使用 random_state 吗? [复制]

如何有选择地选择训练和测试数据

为啥要划分训练集、验证集和测试集