Matplotlib点群自动检测与圈选问题及聚类数确定需求
解决KMeans聚类失效与自动识别聚类数问题
一、排查KMeans聚类失效的常见原因
- 数据未标准化:KMeans基于欧氏距离计算,若特征维度的尺度差异过大,会被数值范围大的特征主导聚类结果。务必先对数据做标准化处理:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaled_data = scaler.fit_transform(your_data) - 初始质心随机性影响:默认的k-means++初始化虽能降低随机性,但特殊分布数据可能仍受影响。增加初始化次数取最优结果:
from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=3, n_init=10, random_state=42) - 错误指定聚类数:如果硬设
n_clusters=3但数据实际分布不符合,会导致聚类错乱,这就需要自动识别最优聚类数。
二、自动识别聚类数(含单一群体场景)
结合肘部法则、轮廓系数,搭配DBSCAN辅助判断,可覆盖无聚类(单一群体)的情况。
1. 肘部法则
计算不同k值下的SSE(平方误差和),曲线“肘部”对应最优k;若曲线从k=1到k=2下降平缓,说明数据为单一群体:
import matplotlib.pyplot as plt sse = [] max_k = 10 for k in range(1, max_k+1): kmeans = KMeans(n_clusters=k, n_init=10, random_state=42) kmeans.fit(scaled_data) sse.append(kmeans.inertia_) plt.plot(range(1, max_k+1), sse) plt.xlabel("聚类数k") plt.ylabel("平方误差和(SSE)") plt.title("肘部法则判断最优聚类数") plt.show()
2. 轮廓系数判断
轮廓系数范围[-1,1],越接近1聚类效果越好。若k≥2时的最大系数低于阈值(如0.2),判定为单一群体:
from sklearn.metrics import silhouette_score sil_scores = [] for k in range(2, max_k+1): kmeans = KMeans(n_clusters=k, n_init=10, random_state=42) labels = kmeans.fit_predict(scaled_data) sil_scores.append(silhouette_score(scaled_data, labels)) # 确定最优聚类数 if max(sil_scores) < 0.2: optimal_k = 1 else: optimal_k = 2 + sil_scores.index(max(sil_scores))
3. DBSCAN辅助验证
DBSCAN无需指定聚类数,可直接检测聚类数量。若仅识别出1个核心聚类且噪声极少,判定为单一群体:
from sklearn.cluster import DBSCAN dbscan = DBSCAN(eps=0.5, min_samples=5) # eps和min_samples需根据数据调整 db_labels = dbscan.fit_predict(scaled_data) unique_clusters = len(set(db_labels)) - (1 if -1 in db_labels else 0) if unique_clusters == 1: optimal_k = 1
三、图表中圈选点群
确定最优k后,用凸包(Convex Hull)圈选每个聚类的边界:
from scipy.spatial import ConvexHull import numpy as np # 绘制结果 plt.figure(figsize=(8,6)) if optimal_k == 1: plt.scatter(your_data[:,0], your_data[:,1], c='steelblue', label='单一群体') else: kmeans = KMeans(n_clusters=optimal_k, n_init=10, random_state=42) labels = kmeans.fit_predict(scaled_data) centroids = scaler.inverse_transform(kmeans.cluster_centers_) colors = ['#ff4444', '#00cc66', '#3366ff', '#ffaa00'] for i in range(optimal_k): cluster_points = your_data[labels == i] plt.scatter(cluster_points[:,0], cluster_points[:,1], c=colors[i], label=f'聚类{i+1}') # 绘制凸包边界 if len(cluster_points) >= 3: hull = ConvexHull(cluster_points) for simplex in hull.simplices: plt.plot(cluster_points[simplex, 0], cluster_points[simplex, 1], c=colors[i], linestyle='--') # 绘制质心 plt.scatter(centroids[i,0], centroids[i,1], c='black', marker='x', s=120) plt.legend() plt.title("自动识别并圈选点群") plt.show()
四、关键注意事项
- 参数适配:DBSCAN的
eps、min_samples需根据数据密度调整;标准化步骤可根据数据分布选择(如坐标点尺度一致可跳过); - 阈值灵活调整:轮廓系数的判定阈值需结合自身场景测试优化;
- 多方法交叉验证:单一方法可能误判,建议结合肘部法则、轮廓系数、DBSCAN的结果综合确定最优聚类数。
内容的提问来源于stack exchange,提问作者Bast38
相关产品推荐
相关产品推荐

