自定义FeatureUnion调用get_feature_names返回异常如何解决
问题根因
出现错误输出的核心原因有两个:
- 自定义的
NameLengthTransformer、DomainLengthTransformer中get_feature_names()方法返回值是字符串类型,不符合scikit-learn的接口约定:该方法需要返回特征名构成的列表。FeatureUnion聚合特征名时会将字符串作为可迭代对象逐字符遍历,给每个字符拼接[转换器名]__前缀,最终生成了你看到的单字符拼接的错误结果。 EmailTransformer直接调用FeatureUnion自带的get_feature_names()方法,该方法默认会给所有子特征名加上转换器名前缀,不符合你要的输出格式;同时原代码中EmailTransformer的fit方法没有拟合内部的FeatureUnion实例,存在未初始化的隐患。
修正方案
按以下规则调整代码即可:
- 所有子转换器的
get_feature_names()统一返回特征名列表,而非单个字符串 - 在
EmailTransformer的fit方法中调用内部FeatureUnion的fit方法完成初始化 - 重写
EmailTransformer的get_feature_names()逻辑,遍历内部所有子转换器,收集各转换器返回的特征名拼成平级列表,不额外添加前缀
修正后可运行代码
import re import numpy as np import pandas as pd from sklearn.base import BaseEstimator, TransformerMixin from sklearn.pipeline import FeatureUnion def get_email_name(email) -> str: return email.rsplit("@", 1)[0] def get_email_full_domain(email) -> str: return email.rsplit("@", 1)[1] def get_email_domain(email) -> str: full_domain = get_email_full_domain(email) domain = full_domain.split(".")[0] return domain def get_text_length(text) -> int: return len(text) class NameLengthTransformer(BaseEstimator, TransformerMixin): def fit(self, X, y=None): return self def transform(self, X, y=None): return X.apply(lambda x: get_text_length(get_email_name(x))).values.reshape( -1, 1 ) def get_feature_names(self): # 修正:返回列表而非字符串 return ["name_length"] class DomainLengthTransformer(BaseEstimator, TransformerMixin): def fit(self, X, y=None): return self def transform(self, X, y=None): return X.apply(lambda x: get_text_length(get_email_domain(x))).values.reshape( -1, 1 ) def get_feature_names(self): # 修正:返回列表而非字符串 return ["domain_length"] class EmailTransformer(BaseEstimator, TransformerMixin): def __init__(self): self.email_transformer = FeatureUnion( [ ("name_length", NameLengthTransformer()), ("domain_length", DomainLengthTransformer()), ] ) def fit(self, X, y=None): # 修正:拟合内部FeatureUnion self.email_transformer.fit(X, y) return self def transform(self, X, y=None): return self.email_transformer.transform(X) def get_feature_names(self): # 修正:遍历子转换器收集特征名,不使用FeatureUnion默认带前缀的逻辑 feature_names = [] for _, transformer in self.email_transformer.transformer_list: feature_names.extend(transformer.get_feature_names()) return feature_names # 测试代码 emails = pd.DataFrame({'email':['ladsf@gmail.com', 'pertro@gmail.com']}) et = EmailTransformer() transform_result = et.fit_transform(X=emails['email']) print(et.get_feature_names())
运行后输出结果为:
['name_length', 'domain_length']
完全符合预期,可以直接用这个特征名列表生成带列名的DataFrame。
内容的提问来源于stack exchange,提问作者Lucas Dresl
相关产品推荐
相关产品推荐

