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

sklearn中KMeans与FeatureUnion结合的fit_predict及聚类结果异常求助

问题解答:KMeans聚类Pipeline与FeatureUnion的两个常见问题

咱们逐个拆解你遇到的两个问题,给出原因分析和可落地的解决办法:

问题1:聚类输出为浮点型距离而非整数标签

原因

KMeans默认的transform()方法返回的是每个样本到所有聚类中心的欧氏距离矩阵(形状是(样本数, 聚类数)的浮点数组),而不是我们需要的「样本所属聚类的整数标签」。你的Pipeline里直接用了KMeans()作为最后一步,FeatureUnion在执行fit_transform()时会触发这个transform()方法,所以得到的是距离值而非聚类标签。

解决办法

自定义一个包装器Transformer,让它调用KMeans的predict()方法输出聚类标签,替代默认的transform()行为:

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.cluster import KMeans

class KMeansPredictor(BaseEstimator, TransformerMixin):
    def __init__(self, n_clusters=8, random_state=42, **kwargs):
        # 初始化KMeans实例,保留原有的参数配置
        self.kmeans = KMeans(n_clusters=n_clusters, random_state=random_state, **kwargs)
    
    def fit(self, X, y=None):
        # 拟合KMeans模型
        self.kmeans.fit(X, y)
        return self
    
    def transform(self, X):
        # 返回聚类标签,并且转成列向量(方便和其他特征合并)
        return self.kmeans.predict(X).reshape(-1, 1)

然后修改你的聚类Pipeline:

Clustering = Pipeline([
    ('SelectPosition', SelectPosition), 
    # 替换成自定义的KMeansPredictor,指定聚类数等参数
    ('Clustering', KMeansPredictor(n_clusters=3, random_state=42))  
])

现在再运行FinalPipeline.fit_transform(data),聚类部分就会输出整数类型的聚类标签,和area列合并成你需要的特征矩阵。


问题2:FeatureUnion对象无fit_predict属性

原因

FeatureUnion的设计定位是合并多个特征提取器的输出,它只实现了fit()、transform()、fit_transform()这几个特征处理相关的方法,本身并不具备fit_predict()——这个方法是给分类、聚类这类预测型模型用的,FeatureUnion不属于这类模型。

解决办法

根据你的需求,有两种可行方案:

方案1:手动拆分fit和预测流程

如果只是想先拟合整个FeatureUnion,再获取合并后的特征(原类别+聚类标签),可以手动分步操作:

# 先拟合整个FeatureUnion
FinalPipeline.fit(data)

# 分别提取两部分的结果
area_features = FinalPipeline.transformer_list[0][1].transform(data)
# 调用自定义KMeansPredictor的predict方法获取标签
cluster_labels = FinalPipeline.transformer_list[1][1].predict(data)

# 合并成DataFrame方便查看
final_result = pd.concat([
    area_features, 
    pd.DataFrame(cluster_labels, columns=['cluster_label'])
], axis=1)

方案2:自定义包装类实现fit_predict

如果希望像调用普通模型一样直接用fit_predict(),可以写一个简单的包装类,给FeatureUnion补上这个方法:

class FeatureUnionWithPredict(BaseEstimator, TransformerMixin):
    def __init__(self, transformer_list):
        self.feature_union = FeatureUnion(transformer_list)
    
    def fit(self, X, y=None):
        self.feature_union.fit(X, y)
        return self
    
    def transform(self, X):
        return self.feature_union.transform(X)
    
    def fit_predict(self, X, y=None):
        # 先拟合再转换,模拟fit_predict的逻辑
        self.fit(X, y)
        return self.transform(X)

然后用这个类替换原来的FeatureUnion:

FinalPipeline = FeatureUnionWithPredict([
    ('cat', SelecCategoricalVariable), 
    ('cluster', Clustering)
])

# 现在就可以正常调用fit_predict了
result = FinalPipeline.fit_predict(data)

完整可运行测试代码(结合你的示例数据)

最后给你一个完整的测试版本,用你提供的示例数据验证:

import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.cluster import KMeans
from sklearn.pipeline import Pipeline, FeatureUnion

# 自定义列选择器(保留你原来的代码)
class SelectColumns(BaseEstimator, TransformerMixin):
    def __init__(self, columns):
        self.columns = columns
    def transform(self, data_train):
        return data_train[self.columns]
    def fit(self, *_):
        return self

# 自定义KMeans预测器
class KMeansPredictor(BaseEstimator, TransformerMixin):
    def __init__(self, n_clusters=8, random_state=42, **kwargs):
        self.kmeans = KMeans(n_clusters=n_clusters, random_state=random_state, **kwargs)
    def fit(self, X, y=None):
        self.kmeans.fit(X, y)
        return self
    def transform(self, X):
        return self.kmeans.predict(X).reshape(-1, 1)

# 自定义带fit_predict的FeatureUnion包装类
class FeatureUnionWithPredict(BaseEstimator, TransformerMixin):
    def __init__(self, transformer_list):
        self.feature_union = FeatureUnion(transformer_list)
    def fit(self, X, y=None):
        self.feature_union.fit(X, y)
        return self
    def transform(self, X):
        return self.feature_union.transform(X)
    def fit_predict(self, X, y=None):
        self.fit(X, y)
        return self.transform(X)

# 你的示例数据
data = pd.DataFrame({
    'air': ['Dining bar', 'Dining bar', 'Dining bar', 'Bar/Cocktail', 'Japanese food', 'Izakaya', 'Izakaya'],
    'latitude': [35.641463, 35.662665, 35.693840, 35.670651, 35.712607, 35.743575, 35.693840],
    'longitude': [139.698171, 139.668268, 139.703549, 139.771861, 139.779996, 139.847180, 139.703549]
})

# 构建Pipeline(注意你示例里的列名是air,不是area,这里做了对应)
SelectPosition = SelectColumns(columns=['latitude', 'longitude'])
Clustering = Pipeline([('SelectPosition', SelectPosition), ('Clustering', KMeansPredictor(n_clusters=2, random_state=42))])
SelecCategoricalVariable = SelectColumns(columns=['air'])
FinalPipeline = FeatureUnionWithPredict([('cat', SelecCategoricalVariable), ('cluster', Clustering)])

# 测试fit_predict
result = FinalPipeline.fit_predict(data)
print(pd.DataFrame(result, columns=['air', 'cluster_label']))

运行后会输出包含原类别和聚类标签的结果,完全符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:57:59