有没有办法根据 y 中的真实标签对特征 X 进行转换?
Posted
技术标签:
【中文标题】有没有办法根据 y 中的真实标签对特征 X 进行转换?【英文标题】:Is there a way to do transformation on features X based on true labels in y? 【发布时间】:2020-09-10 12:32:16 【问题描述】:我检查了其他有关该主题的问题,例如 this、this、this、this 和 this 以及一些很棒的博客文章,blog1、blog2 和 @987654328 @(向各自的作者致敬)但没有成功。
我想要做的是转换 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):
# Calculate thresholds for each column
thresholds = X.loc[y == 9, :].quantile(q=self.percentile, interpolation='linear').to_dict()
# Store them for later use
self.thresholds = thresholds
return self
def transform(self, X, y):
# Create a copy of X
X_ = X.copy(deep=True)
# Replace values lower than the threshold for each column
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...
# Create some example data to work with
random.seed(12)
target = [randint(1, 8) for _ in range(60)] + [9]*40
shuffle(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']
# Create a final nested pipeline where the data pre-processing steps and the final estimator are included
pipeline = Pipeline(steps=[('CustomTransformer', CustomTransformer(percentile=.90)),
('estimator', RandomForestClassifier())])
# Parameter tuning with 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._result
TypeError: transform() missing 1 required positional argument: 'y'
我还尝试了其他方法,例如在fit()
期间存储相应的类索引,然后在transform()
期间使用这些索引。但是,由于交叉验证期间的训练和测试索引不相同,因此在 transform()
中替换值时会出现索引错误。
那么,有没有聪明的办法解决这个问题?
【问题讨论】:
嘿@Jakob,你的这个用例似乎有点无效。考虑一下在部署时您将如何提供目标(此处为y
),在真实数据上,您将无法提供实际目标。
这是一个很好的观点@VivekKumar。当它在生产中运行时,我将无法使用 any y
进行此转换,因为我不知道它的类。理解正确吗?
这正是@VivekKumar 的意思,他是对的;由于这个原因,考虑到真实标签的每个特征转换基本上都是无效的。
即使看起来有可能,在特征工程或选择的任何阶段混合标签也是guaranteed to lead you astray。
你们都提出了非常重要的观点,谢谢@VivekKumar 和desertnaut。但是假设我有信心在生产中数据将以我在上面试图描述的格式出现(无论出于何种原因。但假设我当前的数据集并不完全代表我以后将体验的内容)。为了在生产之前评估我的模型,我想让数据尽可能具有代表性(并因此对其进行转换),并且我想在交叉验证期间对每个训练集/测试集对这样做,如上所述。有没有办法做到这一点?
【参考方案1】:
在 cmets 中我谈到了这个:
class CustomTransformer(BaseEstimator):
def __init__(self, percentile=.90):
self.percentile = percentile
def fit(self, X, y):
# Calculate thresholds for each column
# We have appended y as last column in X, so remove that
X_ = X.iloc[:,:-1].copy(deep=True)
thresholds = X_.loc[y == 9, :].quantile(q=self.percentile, interpolation='linear').to_dict()
# Store them for later use
self.thresholds = thresholds
return self
def transform(self, X):
# Create a copy of actual X, except the targets which are appended
# We have appended y as last column in X, so remove that
X_ = X.iloc[:,:-1].copy(deep=True)
# Use that here to get y
y = X.iloc[:, -1].copy(deep=True)
# Replace values lower than the threshold for each column
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:
# We are appending the target into X
example_x = example[['feat1', 'feat2', 'target']]
example_y = example['target']
【讨论】:
哦,好吧,所以你将 y 附加到 X 之前 将它提供给 fit()。这是一个更好的方法。奇迹般有效。谢谢@VivekKumar。 @Jakob 但这是一个假设条件,您已经使用y
来测试/验证数据,而不仅仅是比较以上是关于有没有办法根据 y 中的真实标签对特征 X 进行转换?的主要内容,如果未能解决你的问题,请参考以下文章