You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Scikit-learn TaxonomyEncoder在ColumnTransformer中遇NotFittedError求助

自定义Scikit-learn分类体系编码转换器的ColumnTransformer兼容问题解决

问题背景

需要处理具有层级结构的数值分类特征(如MCC码、邮政编码、NAICS行业代码):这类特征既不能当作有序特征处理,按纯分类特征(如独热编码)处理又需选择合适的层级粒度。基于此,实现了一个类似TargetEncoder的自定义转换器,用决策树回归器生成编码,通过cross_val_predict获取样本外预测避免过拟合。

原转换器代码如下:

from sklearn.tree import DecisionTreeRegressor
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.model_selection import cross_val_predict

class TaxonomyEncoder(TransformerMixin, BaseEstimator):
    def __init__(self, n_leafs=10, cv=3):
        self.n_leafs = n_leafs
        self.cv = cv

    def fit(self, X, y=None):
        self.tree_ = DecisionTreeRegressor(max_leaf_nodes=self.n_leafs).fit(X,y)
        return self

    def transform(self, X):
        return self.tree_.predict(X).reshape(-1,1)

    def fit_transform(self, X, y=None):
        self.tree_ = DecisionTreeRegressor(max_leaf_nodes=self.n_leafs)
        return cross_val_predict(self.tree_, X, y, cv=self.cv).reshape(-1,1)

单独使用时fit+transform或直接fit_transform均正常,但放入ColumnTransformer后调用fit再transform会抛出错误:

NotFittedError: This DecisionTreeRegressor instance is not fitted yet. Call 'fit' with appropriate arguments before using this estimator.

错误原因

原fit_transform方法违反了Scikit-learn的API契约:该方法仅返回样本外预测结果,但将self.tree_赋值为未拟合的模型实例。当ColumnTransformer内部调用fit_transform后,后续transform会尝试使用这个未拟合的模型,从而触发错误;即使直接调用ColumnTransformer.fit,若转换器的状态被意外修改,也会导致类似问题。

同时,原fit_transform没有保证转换器在执行后处于拟合状态,不符合Scikit-learn对转换器的要求:调用fit_transform后必须可以正常执行transform。

解决方案

修改fit_transform方法,先通过父类的fit方法完成模型拟合(保证self.tree_处于可用状态),再返回cross_val_predict生成的样本外预测结果。这样既满足训练阶段用样本外编码避免过拟合的需求,又保证测试阶段transform能正常使用拟合好的模型。

修正后的完整代码:

from sklearn.tree import DecisionTreeRegressor
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.model_selection import cross_val_predict

class TaxonomyEncoder(TransformerMixin, BaseEstimator):
    def __init__(self, n_leafs=10, cv=3):
        self.n_leafs = n_leafs
        self.cv = cv

    def fit(self, X, y=None):
        self.tree_ = DecisionTreeRegressor(max_leaf_nodes=self.n_leafs).fit(X,y)
        return self

    def transform(self, X):
        return self.tree_.predict(X).reshape(-1,1)

    def fit_transform(self, X, y=None):
        # 先完成模型拟合,确保transform时模型可用
        super().fit(X, y)
        # 返回样本外的预测结果,避免训练过拟合
        return cross_val_predict(self.tree_, X, y, cv=self.cv).reshape(-1,1)

验证测试

使用原复现示例测试:

import pandas as pd
df = pd.DataFrame({'mcc':[3000,3500,7339], 'y':[0,0,1]})

# 单独使用正常
te = TaxonomyEncoder().fit(df[['mcc']], df['y'])
print(te.transform(df[['mcc']]))
# 输出:
# array([[0.],
#        [0.],
#        [1.]])

print(te.fit_transform(df[['mcc']], df['y']))
# 输出:
# array([[0.],
#        [0.],
#        [0.]])

# 放入ColumnTransformer中正常运行
from sklearn.compose import ColumnTransformer
transformer = ColumnTransformer([('taxonomy', TaxonomyEncoder(), ['mcc'])])
transformer.fit(df[['mcc']], df['y'])
print(transformer.transform(df[['mcc']]))
# 输出:
# array([[0.],
#        [0.],
#        [1.]])

内容的提问来源于stack exchange,提问作者Frits

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:32:31