PCA降维后K-means聚类中心偏离集群问题求助
聚类异常问题排查与解决
问题背景
数据集包含约1200条观测值、25个特征,计划先通过PCA降维再执行K-means聚类,但出现两个异常:
- 聚类中心显示位置远离对应集群
- 调整k值后集群划分无有效变化
异常现象展示
k=3时的聚类可视化

k=2时的聚类中心结果
array([[-0.10621571, -0.33357351, -0.31757225, -0.41859302, -0.34180062, -0.40582209, -0.39882614, -0.41326583, -0.40258176, -0.27391731, -0.38387384, -0.373283 , 0.30265487, -0.16514534, -0.38956931, -0.18097048, -0.38672061, -0.38919503, -0.3935108 , -0.26672966, -0.3337329 , -0.27723583, -0.29186106, -0.32065177, -0.29334765], [ 0.47938423, 1.50551998, 1.43330135, 1.88923921, 1.54265145, 1.8316001 , 1.80002522, 1.86519597, 1.81697549, 1.23627317, 1.73254086, 1.68474116, -1.36597464, 0.74535178, 1.75824629, 0.8167755 , 1.74538923, 1.75655704, 1.77603542, 1.20383309, 1.50623935, 1.25125068, 1.31725887, 1.44720019, 1.3239683 ]])
k=2时的聚类可视化

实现代码
kmeans = KMeans(n_clusters=2, random_state=0).fit(scores_data) metadata['cluster'] = kmeans.labels_ # Apply PCA to the data to visualize the clusters in 2D pca = PCA(n_components=2) pca_result = pca.fit_transform(scores_data) plt.scatter(pca_result[:, 0], pca_result[:, 1], c=metadata['cluster'], s=50) cluster_centers = kmeans.cluster_centers_ plt.scatter(cluster_centers[:, 0], cluster_centers[:, 1], c='red', marker='o') # Add labels to the cluster centers for i, center in enumerate(cluster_centers): plt.annotate(f"Cluster {i}", (center[0], center[1]), textcoords="offset points", xytext=(0,10), ha='center', fontsize=20) plt.rcParams["figure.figsize"] = (16,11) plt.show()
问题原因分析
- 聚类中心可视化逻辑错误:代码直接将25维的聚类中心取前两维绘制,但PCA仅对原始数据做了降维,高维聚类中心未经过同一PCA模型转换,导致坐标不在PCA的二维空间内,显示位置偏离实际集群。
- 数据未标准化:K-means是基于距离的算法,特征尺度差异大会让聚类结果被尺度大的特征主导。从k=2的聚类中心可看出,不同特征的数值范围差异明显(如第13个特征,两个中心值差4倍以上),未标准化的数据会导致K-means收敛到不合理的聚类,调整k值也无法得到有效变化。
- 流程不符合预期:原计划是先PCA降维再聚类,但实际代码先在高维数据上执行K-means,再用PCA降维可视化,流程顺序错误也会影响聚类效果的合理性。
解决办法
1. 修正聚类中心的可视化方式
将高维聚类中心通过同一PCA模型转换到二维空间:
# 先拟合PCA并转换原始数据 pca = PCA(n_components=2) pca_result = pca.fit_transform(scores_data) # 执行K-means聚类 kmeans = KMeans(n_clusters=2, random_state=0).fit(scores_data) metadata['cluster'] = kmeans.labels_ # 将聚类中心转换到PCA二维空间 cluster_centers_pca = pca.transform(kmeans.cluster_centers_) # 可视化 plt.scatter(pca_result[:, 0], pca_result[:, 1], c=metadata['cluster'], s=50) plt.scatter(cluster_centers_pca[:, 0], cluster_centers_pca[:, 1], c='red', marker='o') for i, center in enumerate(cluster_centers_pca): plt.annotate(f"Cluster {i}", (center[0], center[1]), textcoords="offset points", xytext=(0,10), ha='center', fontsize=20) plt.rcParams["figure.figsize"] = (16,11) plt.show()
2. 对数据做标准化处理
在PCA或K-means之前,使用StandardScaler统一特征尺度:
from sklearn.preprocessing import StandardScaler # 标准化数据 scaler = StandardScaler() scaled_data = scaler.fit_transform(scores_data) # 后续PCA和K-means使用标准化后的数据 pca = PCA(n_components=2) pca_result = pca.fit_transform(scaled_data) kmeans = KMeans(n_clusters=2, random_state=0).fit(scaled_data) metadata['cluster'] = kmeans.labels_ cluster_centers_pca = pca.transform(kmeans.cluster_centers_) # 可视化代码同上
3. 调整为“先降维再聚类”的流程(可选)
如果严格按照原计划执行,可修改流程为:
# 标准化数据 scaler = StandardScaler() scaled_data = scaler.fit_transform(scores_data) # 先PCA降维 pca = PCA(n_components=2) pca_data = pca.fit_transform(scaled_data) # 在降维后的数据上执行K-means kmeans = KMeans(n_clusters=2, random_state=0).fit(pca_data) metadata['cluster'] = kmeans.labels_ # 直接使用二维聚类中心可视化 plt.scatter(pca_data[:, 0], pca_data[:, 1], c=metadata['cluster'], s=50) plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], c='red', marker='o') # 标注代码同上 plt.show()
4. 验证k值的有效性
标准化后可通过肘部法则或轮廓系数选择合适的k值:
from sklearn.metrics import silhouette_score # 肘部法则 inertias = [] for k in range(1, 10): kmeans = KMeans(n_clusters=k, random_state=0).fit(scaled_data) inertias.append(kmeans.inertia_) plt.plot(range(1,10), inertias) plt.xlabel('k') plt.ylabel('Inertia') plt.title('Elbow Method') plt.show() # 轮廓系数 sil_scores = [] for k in range(2, 10): kmeans = KMeans(n_clusters=k, random_state=0).fit(scaled_data) sil_scores.append(silhouette_score(scaled_data, kmeans.labels_)) plt.plot(range(2,10), sil_scores) plt.xlabel('k') plt.ylabel('Silhouette Score') plt.title('Silhouette Score Method') plt.show()
内容的提问来源于stack exchange,提问作者Programming Noob
相关产品推荐
相关产品推荐

