为何出现TypeError: DataUndersampler.transform()缺少必填参数'y'?
基于支持向量的自定义欠采样器报错修复
问题概述
自定义的支持向量欠采样器,通过拟合SVC获取支持向量,基于多数类样本到支持向量的距离进行欠采样,但在结合OneVsOneClassifier与Pipeline运行时触发错误:
TypeError: DataUndersampler.transform() missing 1 required positional argument: 'y'
报错根源
- 接口不兼容:Scikit-learn的
TransformerMixin要求transform方法仅接收特征矩阵X作为参数,但欠采样器需要同时处理X和y,属于重采样器而非普通转换器 - 依赖缺失:代码中使用
pd.concat但未导入pandas库 - 多分类适配问题:原逻辑仅处理二分类场景,与
OneVsOneClassifier生成的多二分类子任务不匹配 - 参数错误:示例中
make_classification的weights数组长度(4)与n_classes=5不匹配
修复方案
改用imblearn的标准重采样器接口(继承BaseSampler),实现_fit_resample方法(该方法支持同时接收X和y),同时修复依赖、适配多分类子任务并修正参数错误。
修正后完整代码
import pandas as pd from sklearn.base import BaseEstimator from sklearn.svm import SVC import numpy as np from sklearn.multiclass import OneVsOneClassifier from imblearn.pipeline import Pipeline from sklearn.ensemble import RandomForestClassifier from imblearn.base import BaseSampler class SVGBasedUndersampler(BaseSampler, BaseEstimator): def __init__(self, random_state=None): self.random_state = random_state self.svc = SVC(kernel='linear') self.majority_class = None self.minority_class = None def fit(self, X, y): # 拟合SVC获取支持向量 self.svc.fit(X, y) # 记录当前二分类子任务的类别分布(适配OneVsOne逻辑) class_counts = pd.Series(y).value_counts() self.majority_class = class_counts.idxmax() self.minority_class = class_counts.idxmin() return self def _fit_resample(self, X, y): # 统一数据格式为DataFrame/Series X_df = pd.DataFrame(X) if not isinstance(X, pd.DataFrame) else X y_series = pd.Series(y) if not isinstance(y, pd.Series) else y # 获取SVC支持向量 support_vectors = self.svc.support_vectors_ # 分离多数类与少数类样本 X_majority = X_df[y_series == self.majority_class] y_majority = y_series[y_series == self.majority_class] X_minority = X_df[y_series == self.minority_class] y_minority = y_series[y_series == self.minority_class] # 计算多数类样本到最近支持向量的距离 distances = np.min(np.linalg.norm(X_majority.values[:, np.newaxis] - support_vectors, axis=2), axis=1) # 按距离排序,保留与少数类数量一致的多数类样本 sorted_indices = np.argsort(distances) indices_to_keep = sorted_indices[:len(y_minority)] # 合并采样后的数据集并返回numpy数组(兼容后续模型) X_resampled = pd.concat([X_majority.iloc[indices_to_keep], X_minority]).values y_resampled = pd.concat([y_majority.iloc[indices_to_keep], y_minority]).values return X_resampled, y_resampled # 验证示例 from sklearn.datasets import make_classification # 修正参数:weights长度与n_classes一致 X, y = make_classification(n_samples=10_000, n_classes=4, weights=[0.226, 0.037, 0.164, 0.573], n_informative=4, random_state=42) rf_clf = RandomForestClassifier(random_state=234) resampler = SVGBasedUndersampler(random_state=234) # 使用imblearn Pipeline兼容重采样器 pipeline = Pipeline([('sampler', resampler), ('clf', rf_clf)]) classifier = OneVsOneClassifier(estimator=pipeline) # 训练模型 classifier.fit(X, y) # 测试预测 print("预测结果示例:", classifier.predict(X[:5]))
核心修改点
- 切换继承类:使用
imblearn.base.BaseSampler替代TransformerMixin,实现_fit_resample方法满足重采样器接口要求 - 适配多分类:在
fit阶段记录当前二分类子任务的类别分布,确保OneVsOneClassifier的每个子任务都能正确执行欠采样 - 补充依赖:添加pandas导入
- 参数修正:调整
make_classification的参数,确保weights与类别数匹配 - 格式兼容:将最终返回数据转为numpy数组,适配后续Scikit-learn模型输入要求
内容的提问来源于stack exchange,提问作者arilwan
相关产品推荐
相关产品推荐

