Python 实战:肘部法则与轮廓系数可视化(K-Means 聚类最优 K 值选择)
介绍使用 Python 通过肘部法则和轮廓系数确定 K-Means 聚类最优 K 值的方法。包含原理讲解、代码实现、可视化图表及避坑指南,适用于数据分析与机器学习场景。

介绍使用 Python 通过肘部法则和轮廓系数确定 K-Means 聚类最优 K 值的方法。包含原理讲解、代码实现、可视化图表及避坑指南,适用于数据分析与机器学习场景。

在聚类分析中,K-Means 算法的核心难点是确定最优聚类数 K。肘部法则(Elbow Method)和轮廓系数(Silhouette Score)是两种最常用的 K 值选择方法 —— 肘部法则直观高效,轮廓系数兼顾聚类内聚性与分离性。本文将手把手教你用 Python 实现这两种方法的可视化,结合真实数据集快速找到最优 K 值,代码精简易上手,零基础也能轻松落地。
K 值选择的核心是'平衡聚类效果与复杂度',两种方法的原理与流程如下:
关键概念说明:
工具 / 依赖 | 版本要求 | 作用描述 |
Python | 3.7+ | 核心运行环境 |
scikit-learn | 0.23+ | K-Means 聚类、SSE 与轮廓系数计算 |
pandas | 1.0+ | 数据处理与加载 |
matplotlib/seaborn | 3.0+/0.10+ | 可视化曲线绘制 |
numpy | 1.18+ | 数值计算支持 |
pip | 20.0+ | Python 包管理工具 |
打开终端执行以下命令,一键安装所需依赖:
pip install scikit-learn pandas matplotlib seaborn numpy
选用 scikit-learn 内置的 make_blobs 生成模拟聚类数据集(含 4 个真实聚类中心),也可替换为自己的数据集(如 CSV 文件):
# 生成模拟数据:1000 个样本,4 个聚类中心,每个样本 2 个特征
from sklearn.datasets import make_blobs
X, y_true = make_blobs(n_samples=1000, n_features=2, centers=4, cluster_std=0.6, random_state=42)
# 转换为 DataFrame(便于后续处理,可选)
import pandas as pd
data = pd.DataFrame(X, columns=["特征 1", "特征 2"])
print("数据集预览:")
print(data.head())
print(f"\n数据集形状:{data.shape}") # 输出:(1000, 2)
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
# 设置中文字体(避免中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows 系统
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # Mac 系统
plt.rcParams['axes.unicode_minus'] = False
def elbow_method_visualization(X, k_range):
"""
肘部法则可视化
:param X: 聚类数据集(特征矩阵)
:param k_range: K 值候选集(如 range(2, 11))
:return: 各 K 值对应的 SSE 列表
"""
sse_list = [] # 存储各 K 值的 SSE
for k in k_range:
# 训练 K-Means 模型
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) # n_init=10 避免局部最优
kmeans.fit(X)
# 计算 SSE(簇内平方和)
sse = kmeans.inertia_ # KMeans 内置属性,直接获取 SSE
sse_list.append(sse)
print(f"K={k} 时,SSE={sse:.2f}")
# 绘制肘部法则曲线
plt.figure(figsize=(10, 6))
sns.lineplot(x=k_range, y=sse_list, marker='o', linewidth=2, markersize=8, color='#2E86AB')
# 标记肘部点(示例:K=4,可根据实际曲线调整)
elbow_k = 4
elbow_sse = sse_list[elbow_k - k_range.start]
plt.scatter(elbow_k, elbow_sse, color='#E74C3C', s=200, zorder=)
plt.annotate(,
xy=(elbow_k, elbow_sse),
xytext=(elbow_k+, elbow_sse+),
arrowprops=(arrowstyle=, color=, linewidth=),
fontsize=, color=, fontweight=)
plt.title(, fontsize=, fontweight=)
plt.xlabel(, fontsize=)
plt.ylabel(, fontsize=)
plt.grid(, alpha=)
plt.xticks(k_range)
plt.tight_layout()
plt.savefig(, dpi=)
plt.show()
sse_list
k_candidates = (, )
sse_results = elbow_method_visualization(X, k_candidates)
from sklearn.metrics import silhouette_score, silhouette_samples
def silhouette_visualization(X, k_range):
"""
轮廓系数可视化(含平均轮廓系数曲线 + 样本轮廓图)
:param X: 聚类数据集(特征矩阵)
:param k_range: K 值候选集
:return: 各 K 值对应的平均轮廓系数列表
"""
silhouette_avg_list = [] # 存储各 K 值的平均轮廓系数
# 1. 计算各 K 值的平均轮廓系数,绘制系数曲线
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
cluster_labels = kmeans.fit_predict(X)
# 计算平均轮廓系数(所有样本的轮廓系数均值)
silhouette_avg = silhouette_score(X, cluster_labels)
silhouette_avg_list.append(silhouette_avg)
print(f"K={k} 时,平均轮廓系数={silhouette_avg:.4f}")
# 绘制平均轮廓系数曲线
plt.figure(figsize=(10, 6))
sns.lineplot(x=k_range, y=silhouette_avg_list, marker='s', linewidth=2, markersize=8, color='#8E44AD')
# 标记最优 K 值(平均轮廓系数最大的点)
best_k = k_range[np.argmax(silhouette_avg_list)]
best_score = max(silhouette_avg_list)
plt.scatter(best_k, best_score, color='#F39C12', s=200, zorder=5)
plt.annotate(f'最优 K 值 (K={best_k}, 系数={best_score:.4f})',
xy=(best_k, best_score),
xytext=(best_k+0.5, best_score-0.02),
arrowprops=dict(arrowstyle='->', color='#F39C12', linewidth=2),
fontsize=, color=, fontweight=)
plt.title(, fontsize=, fontweight=)
plt.xlabel(, fontsize=)
plt.ylabel(, fontsize=)
plt.grid(, alpha=)
plt.xticks(k_range)
plt.ylim(, )
plt.tight_layout()
plt.savefig(, dpi=)
plt.show()
best_kmeans = KMeans(n_clusters=best_k, random_state=, n_init=)
best_cluster_labels = best_kmeans.fit_predict(X)
sample_silhouette_values = silhouette_samples(X, best_cluster_labels)
plt.figure(figsize=(, ))
y_lower =
i (best_k):
ith_cluster_silhouette_values = sample_silhouette_values[best_cluster_labels == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[]
y_upper = y_lower + size_cluster_i
color = plt.cm.Spectral(i / (best_k))
plt.fill_betweenx(np.arange(y_lower, y_upper), , ith_cluster_silhouette_values,
facecolor=color, edgecolor=color, alpha=)
plt.text(-, y_lower + * size_cluster_i, (i),
ha=, va=, fontweight=)
y_lower = y_upper +
plt.axvline(x=best_score, color=, linestyle=, linewidth=, label=)
plt.title(, fontsize=, fontweight=)
plt.xlabel(, fontsize=)
plt.ylabel(, fontsize=)
plt.xlim([-, ])
plt.ylim([, (X) + (best_k + ) * ])
plt.legend(loc=)
plt.grid(, alpha=, axis=)
plt.tight_layout()
plt.savefig(, dpi=)
plt.show()
silhouette_avg_list
silhouette_results = silhouette_visualization(X, k_candidates)
# 输出两种方法的结果对比
print("\n=== 最优 K 值选择结果对比 ===")
result_df = pd.DataFrame({
'K 值': k_candidates,
'SSE': sse_results,
'平均轮廓系数': silhouette_results
})
print(result_df.to_string(index=False))
# 综合确定最优 K:肘部法则(K=4)与轮廓系数(K=4)一致
optimal_k = 4
print(f"\n综合两种方法,最优聚类数 K={optimal_k}")
# 用最优 K 训练 K-Means,可视化聚类结果
optimal_kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
optimal_labels = optimal_kmeans.fit_predict(X)
centers = optimal_kmeans.cluster_centers_ # 聚类中心
# 绘制聚类散点图
plt.figure(figsize=(10, 8))
sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=optimal_labels, palette='viridis',
s=60, alpha=0.8, legend='full')
# 标记聚类中心
sns.scatterplot(x=centers[:, 0], y=centers[:, 1], color='red', s=200, marker='X',
label='聚类中心', edgecolor='black', linewidth=2)
# 设置图表样式
plt.title(f'K={optimal_k}时 K-Means 聚类效果可视化', fontsize=14, fontweight='bold')
plt.xlabel('特征 1', fontsize=12)
plt.ylabel('特征 2', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend(title='聚类编号')
plt.tight_layout()
plt.savefig(f'K={optimal_k}_聚类效果可视化.png', dpi=300)
plt.show()
# 加载鸢尾花数据集
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
iris = load_iris()
X_iris = iris.data[:, :2] # 取前两个特征(便于可视化)
y_iris = iris.target
# 数据标准化(K-Means 对尺度敏感)
scaler = StandardScaler()
X_iris_scaled = scaler.fit_transform(X_iris)
# 运行肘部法则和轮廓系数可视化
k_iris = range(2, 8)
sse_iris = elbow_method_visualization(X_iris_scaled, k_iris)
silhouette_iris = silhouette_visualization(X_iris_scaled, k_iris)
def auto_select_optimal_k(sse_list, k_range):
"""自动化寻找肘部点(基于 SSE 一阶差分)"""
# 计算 SSE 一阶差分(相邻 K 值的 SSE 差值)
sse_diff = np.diff(sse_list)
# 计算差分的变化率(二阶差分)
sse_diff_rate = np.diff(sse_diff)
# 肘部点对应二阶差分最大的位置 +2(因为差分后索引偏移)
elbow_idx = np.argmax(np.abs(sse_diff_rate)) + 2
return elbow_idx
# 自动化选择最优 K
auto_optimal_k = auto_select_optimal_k(sse_results, k_candidates)
print(f"\n自动化选择的最优 K 值:{auto_optimal_k}")
# 若数据特征数>2,用 PCA 降维后再可视化聚类效果
from sklearn.decomposition import PCA
# 示例:用鸢尾花全 4 个特征聚类
X_iris_full = scaler.fit_transform(iris.data)
kmeans_iris = KMeans(n_clusters=3, random_state=42, n_init=10)
labels_iris = kmeans_iris.fit_predict(X_iris_full)
# PCA 降维到 2D
pca = PCA(n_components=2)
X_iris_pca = pca.fit_transform(X_iris_full)
# 可视化降维后的聚类结果
plt.figure(figsize=(10, 8))
sns.scatterplot(x=X_iris_pca[:, 0], y=X_iris_pca[:, 1], hue=labels_iris, palette='Set2', s=60)
plt.title('PCA 降维后 K-Means 聚类效果(鸢尾花数据集)', fontsize=14, fontweight='bold')
plt.xlabel('PCA 特征 1', fontsize=12)
plt.ylabel('PCA 特征 2', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
本文通过'原理讲解 + 代码实现 + 结果解读'的方式,手把手教你用 Python 实现肘部法则和轮廓系数可视化,核心亮点如下:
该方法适用于数据分析、机器学习、数据挖掘等场景,无论你是新手还是有经验的开发者,都能通过本文快速掌握 K 值选择的核心技巧。按照本文步骤操作,即可轻松实现聚类分析中的最优 K 值选择与效果可视化。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online