实施 Pipeline 以确保训练和测试虚拟变量相同的最佳方法是什么?

What is the best way to implement Pipeline to make sure train and test dummy variables are the same?

我正在构建一个自定义的 t运行sformer,它执行几个步骤来预处理数据。首先是它应用了我编写的一组函数,这些函数将采用现有功能并设计新功能。从那里,分类变量将被一次性编码。最后一步是从 DataFrame 中删除不再需要的特征或列。

我使用的数据集是 Kaggle House Prices 数据集。

这里的问题是确保测试集中的分类虚拟变量与训练集相同,因为训练集中某个特征的某些类别可能不在测试集中,因此测试集中该类别不会有虚拟变量。我已经完成研究并 运行 进入这个 solution 并且我正在尝试在我的自定义 t运行sformer class 中实现第一个答案。首先,我不确定这是否是最好的方法。其次,我遇到了下面讨论的错误。

我已经包括了我应用于数据的函数的完整列表,但下面只显示了几个实际函数。

class HouseFeatureTransformer(BaseEstimator, TransformerMixin):

    def __init__(self, funcs, func_cols, drop_cols, drop_first=True):

        self.funcs = funcs
        self.func_cols = func_cols

        self.train_cols = None
        self.drop_cols = drop_cols

        self.drop_first = drop_first

    def fit(self, X, y=None):

        X_trans = self.apply_funcs(X)
        X_trans.drop(columns=self.drop_cols, inplace=True)
        #save training_columns to compare to columns of any later seen dataset
        self.train_cols = X_trans.columns

        return self

    def transform(self, X, y=None):

        X_test = self.apply_funcs(X)
        X_test.drop(columns=self.drop_cols, inplace=True)
        test_cols = X_test.columns

        #ensure that all columns in the training set are present in the test set
        #set should be empty for first fit_transform
        missing_cols = set(self.train_cols) - set(test_cols)
        for col in missing_cols:
            X_test[col] = 0

        #reduce columns in test set to only what was in the training set
        X_test = X_test[self.train_cols]

        return X_test.values

    def apply_funcs(self, X):

        #apply each function to respective column
        for func, func_col in zip(self.funcs, self.func_cols):
            X[func_col] = X.apply(func, axis=1)

        #one hot encode categorical variables    
        X = pd.get_dummies(X, drop_first=self.drop_first)

        return X

#functions to apply
funcs = [sold_age, yrs_remod, lot_shape, land_slope, rfmat, bsmt_bath, baths, 
                other_rooms, fence_qual, newer_garage]
#feature names
func_cols = ['sold_age', 'yr_since_remod', 'LotShape', 'LandSlope', 'RoofMatl', 'BsmtBaths', 'Baths', \
                'OtherRmsAbvGr', 'Fence', 'newer_garage']

#features to drop
to_drop = ['Alley', 'Utilities', 'Condition2', 'HouseStyle', 'LowQualFinSF', 'EnclosedPorch', \
    '3SsnPorch', 'ScreenPorch', 'PoolArea', 'PoolQC', 'MiscFeature', 'MiscVal', \
    'YearBuilt', 'YrSold', 'YearRemodAdd', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath', \
    'TotRmsAbvGrd', 'GarageYrBlt', '1stFlrSF', '2ndFlrSF', 'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF', 'ExterQual', \
    'ExterCond', 'BsmtQual', 'BsmtCond', 'KitchenQual', 'FireplaceQu', 'GarageQual', 'GarageCond', 'BsmtFinType2', \
    'Exterior1st', 'Exterior2nd', 'GarageCars', 'Functional', 'SaleType', 'SaleCondition']

#functions to transform data
def sold_age(row):
    '''calculates the age of the house when it was sold'''
    return row['YrSold'] - row['YearBuilt']

def yrs_remod(row):
    '''calculates the years since house was remodeled'''
    yr_blt = row['YearBuilt']
    yr_remodeled = row['YearRemodAdd']
    yr_sold = row['YrSold']
    if yr_blt == yr_remodeled:
        return 0
    else:
        return yr_sold - yr_remodeled

def lot_shape(row):
    '''consolidates all irregular categories into one'''
    if row['LotShape'] == 'Reg':
        return 'Reg'
    else:
        return 'Irreg'

在拟合期间,我应用了函数,虚拟化分类,删除不需要的列,然后将列保存到 self.train_cols。当我执行 t运行sformation 时,除了将 t运行sformed 列保存到 test_cols 之外,我执行相同的步骤。我将这些列与拟合中获得的列进行比较,并添加训练中测试集中缺少的任何列,如我链接的答案所示。我得到的错误如下:

KeyError: "['Alley' 'Utilities' 'Condition2' 'HouseStyle' 'PoolQC' 'MiscFeature'\n 'ExterQual' 'ExterCond' 'BsmtQual' 'BsmtCond' 'KitchenQual' 'FireplaceQu'\n 'GarageQual' 'GarageCond' 'BsmtFinType2' 'Exterior1st' 'Exterior2nd'\n 'Functional' 'SaleType' 'SaleCondition'] not found in axis"

我想了解为什么会出现此错误,以及是否有比我现在的做法更好的方法来实施此过程。

以下是我在您的代码中注意到的一些可能有帮助的事情

  • 错误是抱怨您要删除的某些列在数据框中不存在。要解决此问题,您可以用
  • 替换代码以删除列
data = np.random.rand(50,4)
df = pd.DataFrame(data, columns=["a","b","c","d"])
drop_columns=['b', 'c', 'e', 'f']


## code to drop columns
columns = df.columns
drop_columns = set(columns) & set(drop_columns)
df.drop(columns=drop_columns, inplace=True)
  • 拟合函数仅用于从训练数据推断变换参数。并且仅使用列车数据调用。在您的情况下,您只是在应用函数并删除指定列后推断训练数据的剩余列。您不需要实际应用这些功能。如您所知,每个函数添加了哪些列以及您需要删除哪些列。您只能在列上使用一些设置操作才能找到它。

  • 您还可以简化转换功能,您已经知道要包含哪些列,因此您首先添加缺失的列,而不是仅采用要包含的列而不是删除列