第20讲:K均值聚类(K-Means Clustering)
2026年06月05日
scale() 是黄金法则kmeans、可视化与结果解读从层次聚类到划分聚类的跨越
场景:你有 10 万条客户交易记录,需要快速分群
提示
K均值 vs 层次聚类——最核心的区别
| 层次聚类 | K均值聚类 | |
|---|---|---|
| 需要预设 k | ❌ 不需要 | ✅ 必须预设 |
| 输出形式 | 谱系图(树状结构) | k 个簇的分配标签 |
| 计算复杂度 | \(O(n^2)\) 至 \(O(n^3)\) | \(O(n \cdot k \cdot t)\) |
| 适合数据量 | 小样本(< 5000) | 大数据集(百万级可行) |
| 能否撤销合并 | ❌ 一旦合并不可逆 | ✅ 每轮迭代可重新分配 |
目标:找到 k 个"最优中心点",使每个样本到其所属中心的距离之和最小
注记
WCSS(Within-Cluster Sum of Squares):簇内平方和,是衡量聚类质量的核心指标。WCSS 越小,说明每个簇内的样本越紧凑、越相似。
迭代优化:从随机初始化到稳定收敛
算法核心:反复"分配—更新—再分配",直到中心不再移动
重要
关键数学保证:每次"分配+更新"循环后,WCSS 单调不增——算法一定会收敛,但不一定收敛到全局最优(可能是局部最优)。因此实践中通常运行多次(nstart=25),取WCSS最小的结果。
数据:6个二维点(A–F),目标:用 K均值聚为 2 组
以真实样本 A 和 C 作为初始质心 设定初始质心:μ₁ 选定 A(1.0, 1.5),μ₂ 选定 C(3.0, 3.5)。右侧远端样本因距离原因暂归属于 C
警告
当前临时聚类状态:簇1 = {A, B},簇2 = {C, D, E, F}。由于右侧三个高坐标点的强力拉扯,橙色组的几何中心即将会发生剧烈漂移!
右侧高值引发橙色质心“大漂移”,重新计算两簇的均值坐标,橙色质心被大幅度拉向右上方,与 C 点彻底拉开距离
边界样本 C 的“戏剧性反转”,用全新质心重新度量距离:橙色质心离 C 变远了!C 点果断脱离橙色组,重新回归蓝色大家庭!
再无成员变动,迭代完美终止。
重要
K均值算法完整回顾:随机初始化 → 分配(1步)→ 更新质心 → 再分配(1步,结果不变)→ 收敛。
本例最终 WCSS = 12.33,两个簇完美分离,聚类效果极佳。
肘部法则:让数据告诉你最佳 k 值
核心思路:k 增大 → WCSS 必然下降;找"下降速度骤减"的拐点
轮廓系数:同时考虑"簇内紧凑度"与"簇间分离度",是更严格的评估指标
\[s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}\]
提示
实践建议:同时使用肘部法则(看 WCSS 拐点)和轮廓系数(取最大值对应的 k);若二者结论一致,则该 k 值可信度更高。
scale → kmeans → plot → 解读结果
数据集:R 内置 mtcars(汽车性能数据集)
| 变量 | 中文名 | 说明 |
|---|---|---|
mpg |
油耗(英里/加仑) | 越大越省油 |
hp |
马力 | 发动机功率 |
wt |
车重(1000磅) | 车辆自重 |
qsec |
1/4英里用时(秒) | 加速性能 |
disp |
排量(立方英寸) | 发动机大小 |
共32辆汽车;实训中不使用气缸数
cyl列,聚类完成后用它验证。
# ── Step 1:选取数值列(不含气缸数 cyl)──────────────────────
data(mtcars)
mtcars_num <- mtcars[, c("mpg","hp","wt","qsec","disp")]
# ── Step 2:标准化(必须!各变量量纲差异较大)──────────────────
mtcars_scaled <- scale(mtcars_num)
# ── Step 3:肘部法则选择 k ──────────────────────────────────────
library(factoextra)
library(ggplot2)
fviz_nbclust(mtcars_scaled, kmeans, method = "wss")
fviz_nbclust(mtcars_scaled, kmeans, method = "silhouette")
# ── Step 4:执行 K均值聚类(k=3, nstart=25)────────────────────
set.seed(123)
km_result <- kmeans(mtcars_scaled, centers = 3, nstart = 25)
# ── Step 5:查看结果 ────────────────────────────────────────────
km_result$centers # 各簇质心(标准化空间)
km_result$size # 各簇样本数
km_result$tot.withinss # 总 WCSS
# ── Step 6:结果可视化────────────────────────────
fviz_cluster(km_result, data = mtcars_scaled)注记
nstart=25 的意义:运行25次随机初始化,每次得到一个局部最优解,最终返回 WCSS 最小的结果。计算成本很低(mtcars 只有32行),但可以大幅提升结果的稳定性与全局最优性。
层次聚类 vs K均值聚类——全面对比
提示
黄金组合:先用层次聚类在小样本上探索最优 k 值(看谱系图),再用K均值在全量数据上高效执行。两步走,兼顾准确性与计算效率。
K均值目标:最小化 WCSS(簇内平方和);算法保证单调收敛,但可能是局部最优
四步迭代:随机初始化 k 个质心 → 分配(最近质心原则)→ 更新(重算质心均值)→ 判断收敛;nstart=25 多次运行取最优
k 值选择:肘部法则(WCSS 拐点)+ 轮廓系数(最大化 \(s(i)\))双重验证,结合领域知识
R 核心代码:scale → kmeans(nstart=25) → 查看 $centers/$size/$tot.withinss → table() 验证
mtcars 实训:k=3 时自动发现省油小车 / 均衡中车 / 高性能跑车三类,与气缸数高度吻合
vs 层次聚类:K均值更快(\(O(nkt)\))、更适合大数据、可迭代纠错;但需预设 k,对球形以外的簇效果有限
互补策略:先用层次聚类探索 k → 再用 K均值处理全量数据,是实战中的黄金组合
用R内置数据iris进行K均值聚类
Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,] -0.8977 1.01560 -1.336 -1.311
[2,] -1.1392 -0.13154 -1.336 -1.311
[3,] -1.3807 0.32732 -1.392 -1.311
[4,] -1.5015 0.09789 -1.279 -1.311
[5,] -1.0184 1.24503 -1.336 -1.311
[6,] -0.5354 1.93331 -1.166 -1.049
1 2 3
50 53 47
Murder(x轴)和 Assault(y轴)绘制散点图,颜色区分聚类library(ggplot2)
ggplot(arrests_clustered,
aes(x = Murder, y = Assault,
color = factor(cluster), label = rownames(USArrests))) +
geom_point(size = 3) +
geom_text(vjust = -0.6, size = 2.5) +
scale_color_manual(values = c("#4472C4","#e05c2a","#2eab6e"),
name = "聚类") +
labs(title = "美国各州犯罪率 K 均值聚类(k=3)",
x = "谋杀率(Murder)", y = "袭击率(Assault)")第20讲:K均值聚类(K-Means Clustering)
「K均值的智慧在于:不停地问每一个点——'你现在的归属,真的是你最近的家吗?'——直到所有点都满意为止。」
数据挖掘与R语言 | K均值聚类