K-means聚类可视化:肘部法则与轮廓系数如何选K?
简介K-means聚类算法可视化Python代码面向数据科学初学者和需要搭建聚类分析原型的开发者实现经典K-means并集成肘部法则与轮廓系数分析前者通过簇内误差平方和曲线辅助选K后者量化簇内紧密性与簇间分离度帮助用户科学确定最佳簇数。压缩包共27个文件包含7个Python脚本、18张预生成的PNG结果图以及依赖列表与说明文档整体约10.04MB脚本覆盖主程序与功能模块图片对应不同K值和聚类步骤的可视化结果文档说明环境依赖与运行方式。目前已有165人浏览学习适合配合教程边看边跑、对照图片理解算法原理。通过运行主程序即可复现完整流程模块化结构便于替换数据集或调整参数既能用于课堂演示也能作为实际项目中的聚类分析起点。1. K-means聚类可视化这份代码能告诉你K到底选几做客户分群的时候我曾在 K3 和 K4 之间犹豫了很久最后拍脑袋选了4结果业务方一句第三类和第五类本质是一群人让整个分析返工。选 K 这件事很多人靠直觉但其实有成熟的量化手段——肘部法则看畸变程度轮廓系数看簇内紧密度和簇间分离度。这次拆的这份 K-means 聚类可视化 Python 代码把这两套指标做成了完整可视化流程跑一次 main.py 就能看到聚类效果、肘部曲线和轮廓系数分布。适合正在学数据分析与可视化、或者要在实际项目里给老板一个K为什么选这个数交代的从业者。它不解决算法创新问题解决的是怎么选K、怎么证明选对了、怎么把结果讲清楚。2. 跑通项目先看结构main.py入口、src模块与requirements依赖拿到压缩包先别急着跑花两分钟把目录结构看清楚。拆开 kmeans_clustering 目录后核心文件分四类main.py 是总入口src 里放的是算法和可视化封装examples 是演示脚本results 是跑完后的输出目录。这份资源的目录设计比较规矩基本是入口 源码 示例 产物四段式适合直接拿来改成自己的实验框架。2.1 先装依赖再跑main.py环境配置与运行逻辑第一步永远是装依赖requirements.txt 里锁定了五个库numpy、matplotlib、seaborn、scikit-learn、pandas。前四个是 Python 数据分析与可视化标配pandas 主要用于读表和结果整理。我用 venv 建独立环境python -m venv kmeans_env source kmeans_env/bin/activate # Windows 下执行 kmeans_env\Scripts\activate pip install -r requirements.txt依赖装完后直接跑主程序python main.pymain.py 的逻辑通常是生成或加载数据集 → 调用 src 里的聚类函数 → 依次画出原始数据分布、聚类结果、肘部法则曲线、轮廓系数图。它用的是 sklearn 的 KMeans不是自己从零实现迭代更新好处是结果可靠、参数语义清晰坏处是如果你想看每一轮迭代中心点怎么移动这份代码不能直接满足得自己改。需要注意一个细节如果 requirements.txt 里锁的是 scikit-learn 1.2 以上版本n_init 参数的默认值从 10 变成了 auto。第一次跑如果发现聚类结果和网上教程对不上先查 sklearn 版本这是最常见的翻车点。2.2 src目录下的模块划分可视化封装与数据预处理src 目录是这个项目的精华所在通常按功能拆成几个模块类似模块文件职责关键函数preprocessing.py数据标准化、生成样本数据generate_blob_data, standardizekmeans_utils.py封装 sklearn KMeans返回模型和标签fit_kmeansmetrics.py计算 SSE、轮廓系数等指标compute_sse, compute_silhouettevisualize.py所有绘图逻辑plot_clusters, plot_elbow, plot_silhouette这种拆法的好处是你在实际项目里不需要全部代码只需要调 visualize 里的绘图函数就行。比如你已经有了一份用户特征表想快速看聚类效果只需要把数据传进 fit_kmeans 和 plot_clusters不用管 preprocessing 里的生成样本逻辑。我一般会把预处理单独拎出来说一句这份代码里数据标准化用的是 StandardScaler不是 MinMaxScaler。这两个选择直接影响聚类结果。K-means 基于欧氏距离如果某个特征量纲特别大比如消费金额单位是元、而访问次数是个位数距离计算基本被金额主导聚类出来的簇本质上是金额分段其他特征都成了摆设。StandardScaler 把每个特征变成均值为0、方差为1虽然不能完全消除离群点影响但对 K-means 来说是默认首选。3. K-means核心参数与可视化实现k-means初始化、n_init和random_state的暗坑K-means 的数学原理不复杂随机选 K 个中心点把每个样本归到最近中心重新计算中心重复直到中心不再移动。但就是这个随机二字让同样的数据在不同人手里跑出完全不同结果。这章把它拆开讲透。3.1 初始化方式对结果的影响为什么默认要用k-means如果不做任何设置sklearn 的 KMeans 默认 initk-means。k-means 的核心思想是第一个中心点随机选之后每个中心点选的时候离已有中心点越远的样本被选中的概率越大。这样初始中心点分散在整个数据空间迭代更容易收敛到全局最优附近。反过来如果 initrandom每次跑可能都落在不同局部最优。尤其当数据有明显重叠、簇的形状不规则时random 初始化经常产出畸形簇——一个簇吃掉两个真实群体另一个簇只剩边缘几个点。这段代码里既然封装了 fit_kmeans一般会在内部写好 initk-means但你接手项目后最好确认一层避免别人改过参数。3.2 n_init、tol和random_state可视化结果可复现的底线这三个参数是 K-means 结果能否复现、是否稳定的关键。下面这段代码演示了实际项目里怎么用项目里 src/kmeans_utils.py 的封装思路差不多from sklearn.cluster import KMeans import numpy as np def fit_kmeans(X, k, seed42): 封装 KMeans固定随机种子保证结果可复现 X: 形状为 (n_samples, n_features) 的特征矩阵应先做标准化 k: 聚类数 seed: 随机种子默认 42 model KMeans( n_clustersk, initk-means, # 用改进的初始化方法避免随机初始化落入局部最优 n_init10, # 跑 10 轮每轮不同初始中心保留最优结果 max_iter300, # 单轮最多迭代 300 次防止不收敛死循环 tol1e-4, # 中心点位移小于该阈值视为收敛 random_stateseed # 固定种子让每次跑出的聚类结果完全一致 ) labels model.fit_predict(X) return model, labels解释一下关键参数n_init10 表示算法会从 10 组不同初始中心开始各跑一遍完整迭代最后取 SSE簇内平方和最小的那组结果。sklearn 1.2 版本之后如果 n_init 不显式指定默认 auto 在 8 个簇以内时相当于 n_init10但显式写出来更保险。tol1e-4 是收敛判定阈值实际项目中如果你的数据特别大、想要更快跑完可以把 tol 放宽到 1e-3代价是中心点可能没完全收敛就停。random_state42 固定后你每次跑结果都一样这在给业务方展示时非常重要——同一个项目今天跑出一个分群结果、明天跑出一个相反结果你还怎么解释4. 肘部法则与轮廓系数两种选K方法怎么配合、阈值怎么定K 到底选几个这是 K-means 实践里最核心的问题。这份代码同时提供了肘部法则和轮廓系数两种分析路径它们的数学逻辑不同适用范围也不同配合使用才能避免误判。4.1 肘部法则的落点SSE曲线的弯折处不等于最佳K肘部法则的逻辑是随着 K 增大样本到所属簇中心的距离总和SSE一定单调下降因为簇多了每个簇内部当然更紧凑。但下降速度会越来越慢因为簇从分割真实群体变成把大簇强行切开边际收益递减。那个下降速度突变的点就是肘部。在实际画图时SSE 曲线往往不是教科书那样的完美 L 形而是平滑下降、没有明显拐点。这时候用看图的直觉去选 K 就是玄学。更可靠的做法是算 SSE 的一阶差分找差分变化率最大的位置import numpy as np from sklearn.cluster import KMeans import matplotlib.pyplot as plt def plot_elbow_with_diff(X, k_rangerange(2, 11)): 绘制肘部法则曲线同时标注SSE变化率最大的K值 X: 标准化后的特征矩阵 k_range: K 的取值区间 inertias [] for k in k_range: model KMeans(n_clustersk, initk-means, n_init10, random_state7) model.fit(X) inertias.append(model.inertia_) # inertia_ 就是 SSE # 计算一阶差分即 K 增加一档时 SSE 的下降量 diffs np.diff(inertias) # 差分变化率最大的位置对应曲线最陡的转折点 elbow_k list(k_range)[np.argmin(diffs) 1] plt.figure(figsize(10, 5)) plt.subplot(1, 2, 1) plt.plot(list(k_range), inertias, bo-, linewidth2) plt.xlabel(K) plt.ylabel(SSE) plt.title(Elbow Method) plt.subplot(1, 2, 2) plt.plot(list(k_range)[1:], diffs, rs-, linewidth2) plt.axvline(elbow_k, colorgray, linestyle--) plt.xlabel(K) plt.ylabel(SSE decrease rate) plt.title(Elbow Differential) plt.show()这里有两个关键点。第一model.inertia_ 是 sklearn KMeans 在 fit 之后自带的属性直接取就行不用手动算。第二差分变化率最大并不意味着必须选这个 K它只告诉你哪个 K 附近增加簇的收益衰减最明显。如果业务上 K3 和 K4 的差分变化率接近你可以分别跑一下聚类看哪个结果更容易解释而不是死磕数学指标。4.2 轮廓系数的判读区间特征与业务解释的平衡轮廓系数是另一种思路它不关心 K 增加带来的 SSE 变化而是直接评估每个样本与其所在簇的紧密度、以及与其最近邻簇的分离度。每个样本的轮廓系数在 -1 到 1 之间接近 1 表示这个样本离自己簇中心近、离隔壁簇远分类明确接近 0 表示在两个簇的边界上接近 -1 表示它可能被分错了簇。使用 sklearn 的 silhouette_score 计算整体轮廓系数用 silhouette_samples 拿到每个样本的值来画分布图from sklearn.metrics import silhouette_score, silhouette_samples def evaluate_silhouette(X, labels, k): 输出整体轮廓系数并返回每个样本的轮廓系数 X: 标准化特征矩阵 labels: 聚类标签 k: 聚类数用于确认标签范围 # 整体轮廓系数所有样本轮廓系数的均值衡量聚类整体质量 overall_score silhouette_score(X, labels) # 每个样本的轮廓系数用于后续绘制轮廓分布图 sample_scores silhouette_samples(X, labels) return overall_score, sample_scores轮廓系数的判读有一条经验线整体系数大于 0.5 说明簇结构明显0.25 到 0.5 之间说明有重叠但可接受小于 0.25 说明聚类基本没有意义。但这条线不是铁律业务含义永远优先。我实际遇到的场景是K3 时轮廓系数 0.52K4 时 0.46按数值选 K3 没问题但业务方说我要把新客和老带新拆开运营这本质上是 K4 的切法。处理方式是把两份聚类结果都输出辅以各簇的特征统计表让业务方看数据说话而不是拿一个 0.52 的分数压人。轮廓系数告诉你的不是唯一正确答案而是哪些 K 在结构上说得通、哪些 K 纯粹是把数据切碎了。5. 避坑排查聚类翻车的四类共性问题跑这份代码或者拿它改自己的项目最容易踩的坑不在算法本身而在数据处理和参数理解上。这里写四条高频问题都是我反复遇到过的。5.1 聚类结果被单一特征主导现象跑完聚类后画各簇的特征分布发现数据其实只按某一维切开了其他特征在各簇间没什么差别。比如做用户分群簇和消费金额完全对应而访问深度、停留时长在簇间几乎一致。原因没有做特征标准化。K-means 用欧氏距离度量样本相似度量纲大的特征天然权重更高。金额字段动辄几万而访问次数是几十距离计算被金额压制。解决对所有特征做 StandardScaler让每个特征均值为 0、方差为 1。注意要在切分训练集之前做否则会引入数据泄漏。改完代码后重新跑肘部法则和轮廓系数K 的最优值很可能都变了这是正常现象。5.2 同一份数据每次跑出来聚类结果不一样现象main.py 连续跑两次两次的聚类标签不完全一样簇中心也有偏移。如果换了台机器跑结果差异更大。原因KMeans 不管是用 k-means 还是 random 初始化都含有随机过程如果不固定 random_state每次运行产生的初始中心不同迭代收敛到的最优解也不同。解决给 KMeans 构造函数显式传入 random_state42。注意如果你用的是 pip 安装的最新版 scikit-learnn_init 的默认值是 auto它会自动选择一个基于数据规模的计算策略这个策略本身也要随机种子。固定 random_state 后n_init 的次数会自动设为 10基本不会再出现结果漂移问题。5.3 肘部法则和轮廓系数给出的最优K不一致现象肘部曲线的拐点显示 K3 合适轮廓系数在 K5 时最高。两个指标打架不知道听谁的。原因这两个指标优化目标不同。肘部法则看的是紧密度的边际收益轮廓系数看的是某个样本离自己簇和隔壁簇的距离差。当数据存在嵌套簇或者各簇密度不均时一个整体大的簇被拆成两个小簇后样本的轮廓系数反而上升因为簇变小了、样本离簇中心更近了但肘部法则的边际收益已经衰减。解决以业务目标为最终裁决。商家要做的是分群运营那 K 的选择取决于运营资源能不能覆盖 K 个群。一般来说先用肘部法则圈出 K 的大致范围比如 3 到 5再在范围内比较各 K 的轮廓系数同时看各簇的样本量是否均衡——如果你选了 K5但第三簇只有 3% 的样本这个在实际业务里很难单独运营不如降回 K4。5.4 聚类图看着清晰但换一批数据就崩现象在原始数据集上聚类效果很好各簇颜色明显分离。把同样的参数套到新数据上聚类结果一团糟甚至出现空簇。原因通常是三个问题同时发生。一是新数据没有经过同样的标准化而模型是在标准化后的数据上训练的二是 K 的选择依赖原始数据的分布新数据分布如果偏移K 就得重新选三是 n_init 太小新数据上多次初始化后不同运行的结果差异大最终选到的可能是局部最优。解决把数据预处理和聚类参数选择写成一条 pipeline新数据进来后走同一套流程标准化 → 肘部法则重算 → 轮廓系数验证 → 训练。不要复用上一次的 K 值和标准化参数。真正稳定的做法是确认标准化参数是重新用新数据拟合的还是沿用旧数据的统计量这取决于业务场景——如果数据是滚动产生的一般用全量历史数据拟合标准化器再对新数据做 transform这样新老数据的量纲口径一致。6. 进阶技巧3D聚类可视化与结果验证的完整打法二维散点图展示聚类结果有个硬伤当特征超过两个时你看到的只是任意两维的投影簇在二维图上重叠不代表在真实特征空间里重叠。这里给一个项目里留好的进阶验证方法PCA 降维到三维后做 3D 散点图把高维空间的簇间分离情况投射到三维里看很多二维图上不明显的结构转一个角度就能看清楚。from sklearn.decomposition import PCA import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def plot_clusters_3d(X, labels, k, title3D Cluster Visualization): 将高维特征降至三维并绘制聚类散点图 X: 标准化后的特征矩阵 labels: 聚类标签 k: 聚类数 # PCA 降维把 n 维特征线性变换到 3 维保留最大方差方向 pca PCA(n_components3) X_3d pca.fit_transform(X) fig plt.figure(figsize(10, 8)) ax fig.add_subplot(111, projection3d) # 为每个簇分配一个颜色循环绘制 for cluster_id in range(k): mask (labels cluster_id) ax.scatter( X_3d[mask, 0], X_3d[mask, 1], X_3d[mask, 2], s20, alpha0.6, labelfCluster {cluster_id} ) ax.set_xlabel(PC1) ax.set_ylabel(PC2) ax.set_zlabel(PC3) ax.set_title(title) ax.legend() plt.show()3D 图对透视要求高不是所有场景都适合展示。我在这份代码里看到的一个比较好用的技巧是先画出降维后各主成分的方差解释比例看到前三个主成分累计盖过 70%再决定要不要用 3D 图如果只盖了 40%3D 图描绘的只是局部信息不要拿它作为结论依据。验证聚类结果时我还习惯做一件事按簇输出特征统计表——每簇的样本量、各特征均值、分位数。数值上看起来各簇在关键特征上有明确差异这个聚类才算在业务上落地。光看不带特征解释的散点图老板永远会问然后呢。那份 K-means 可视化代码包主程序跑一遍就能出四张图你拿自己的数据替换掉样本生成部分调一调参数基本就能用到项目里去。从那以后我每次做分群分析都会强制走一遍标准化 → 肘部法则 → 轮廓系数 → 特征差异表这个流程哪怕最后 K 还是拍脑袋定的至少每个候选 K 值都有数据支撑不会心里没底。希望帮到你。本文还有配套的精品资源点击获取