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

Posted

技术标签:

【中文标题】实施 Pipeline 以确保训练和测试虚拟变量相同的最佳方法是啥?【英文标题】:What is the best way to implement Pipeline to make sure train and test dummy variables are the same?实施 Pipeline 以确保训练和测试虚拟变量相同的最佳方法是什么? 【发布时间】:2020-01-05 19:17:32 【问题描述】:

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

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

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

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

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。当我进行转换时,除了将转换后的列保存到 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"

我正在尝试了解为什么会出现此错误,以及是否有比我执行此过程更好的方法来实现此过程。

【问题讨论】:

你能不能也展示一个数据和函数的最小例子,func_cols,drop_cols?没有这些信息,您的问题是不完整的 谢谢。我已经编辑了帖子并包含了其他信息。 【参考方案1】:

以下是我在您的代码中记下的几件事,可能会有所帮助

错误是抱怨您尝试删除的某些列在数据框中不存在。要解决此问题,您可以替换代码以删除列
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)

Fit 函数仅用于从训练数据推断转换参数。并且仅使用训练数据调用。在您的情况下,您只是在应用函数并删除指定的列之后推断训练数据的剩余列。您不需要实际应用这些功能。如您所知,每个函数添加了哪些列以及您需要删除哪些列。您只能使用列上的一些集合操作来找到它。

您还可以简化转换函数,因为您已经知道要包含哪些列,因此您首先添加缺少的列,而不是只取要包含的列而不是删除列

【讨论】:

谢谢你,开发者。关于您的第二个要点,我将函数包括在内,然后对 cat 变量进行虚拟化。在 CV 期间,我不知道某个变量的哪些类别将包含在训练集中,因此我试图在拟合期间保存那些虚拟猫变量,以确保它们包含在转换中,即之后的任何转换数据集合身。在拟合期间保存虚拟变量以应用于转换是否有意义? 我在上一条评论中的问题仍然存在,但我能够弄清楚我在删除列时遇到错误,因为我试图在模拟变量后删除列所以猫可变列以“_cat_type”为后缀,因此没有要删除的原始列。 是的,在拟合期间保存那些虚拟猫变量是完全可以的,因为您想在预测期间使用相同的值进行转换

以上是关于实施 Pipeline 以确保训练和测试虚拟变量相同的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 make_pipeline() 标准化训练和测试数据集

如何使用 scikit learn 确保测试集和训练集具有相同的功能?

如何使用 GridSearchCV 和 sklearn Pipeline 用训练数据的估算值估算测试数据

[持续交付实践] pipelne:pipeline 使用之项目样例

Cross-Validation交叉验证是什么?详解及实施

持续交付实践-pipeline 使用之 MultiBranch Pipeline