我已经查看了其他涉及此主题的问题,例如这个,这个,这个,这个和这个,以及一些很棒的博客文章,博客1,博客2和博客3(向各自的作者致敬),但都没有成功。
我想做的是对X
中低于某个阈值的行进行变换,但仅限于目标y
中对应某些特定类别的行(y != 9
)。阈值是基于另一类别(y == 9
)计算的。然而,我在理解如何正确实现这一点上遇到了问题。
由于我想对这个进行参数调优和交叉验证,我将不得不使用管道进行变换。我的自定义变换器类如下所示。请注意,我没有包括TransformerMixin
,因为我认为我需要在fit_transform()
函数中考虑y
。
class CustomTransformer(BaseEstimator): def __init__(self, percentile=.90): self.percentile = percentile def fit(self, X, y): # 计算每列的阈值 thresholds = X.loc[y == 9, :].quantile(q=self.percentile, interpolation='linear').to_dict() # 存储以便后续使用 self.thresholds = thresholds return self def transform(self, X, y): # 创建X的副本 X_ = X.copy(deep=True) # 替换每列低于阈值的值 for p in self.thresholds: X_.loc[y != 9, p] = X_.loc[y != 9, p].apply(lambda x: 0 if x < self.thresholds[p] else x) return X_ def fit_transform(self, X, y=None): return self.fit(X, y).transform(X, y)
然后将其输入到管道和后续的GridSearchCV中。我在下面提供了一个工作示例。
imports...# 创建一些示例数据来使用random.seed(12)target = [randint(1, 8) for _ in range(60)] + [9]*40shuffle(target)example = pd.DataFrame({'feat1': sample(range(50, 200), 100), 'feat2': sample(range(10, 160), 100), 'target': target})example_x = example[['feat1', 'feat2']]example_y = example['target']# 创建一个最终的嵌套管道,其中包含数据预处理步骤和最终的估计器pipeline = Pipeline(steps=[('CustomTransformer', CustomTransformer(percentile=.90)), ('estimator', RandomForestClassifier())])# 使用GridSearchCV进行参数调优p_grid = {'estimator__n_estimators': [50, 100, 200]}gs = GridSearchCV(pipeline, p_grid, cv=10, n_jobs=-1, verbose=3)gs.fit(example_x, example_y)
上述代码给我以下错误。
/opt/anaconda3/envs/Python37/lib/python3.7/concurrent/futures/_base.py in __get_result(self) 382 def __get_result(self): 383 if self._exception:--> 384 raise self._exception 385 else: 386 return self._resultTypeError: transform() missing 1 required positional argument: 'y'
我也尝试了其他方法,例如在fit()
期间存储相应的类别索引,然后在transform()
期间使用这些索引。然而,由于交叉验证期间的训练和测试索引不相同,当在transform()
中替换值时会出现索引错误。
那么,有没有巧妙的方法来解决这个问题呢?
回答:
在评论中我提到的是这个:
class CustomTransformer(BaseEstimator): def __init__(self, percentile=.90): self.percentile = percentile def fit(self, X, y): # 计算每列的阈值 # 我们将y作为X的最后一列添加,所以移除它 X_ = X.iloc[:,:-1].copy(deep=True) thresholds = X_.loc[y == 9, :].quantile(q=self.percentile, interpolation='linear').to_dict() # 存储以便后续使用 self.thresholds = thresholds return self def transform(self, X): # 创建实际X的副本,除了附加的目标 # 我们将y作为X的最后一列添加,所以移除它 X_ = X.iloc[:,:-1].copy(deep=True) # 使用这里获取y y = X.iloc[:, -1].copy(deep=True) # 替换每列低于阈值的值 for p in self.thresholds: X_.loc[y != 9, p] = X_.loc[y != 9, p].apply(lambda x: 0 if x < self.thresholds[p] else x) return X_ def fit_transform(self, X, y): return self.fit(X, y).transform(X)
然后更改你的X, y:
# 我们将目标附加到Xexample_x = example[['feat1', 'feat2', 'target']]example_y = example['target']