数据挖掘与R语言

第20讲:K均值聚类(K-Means Clustering)

2026年06月05日

上讲回顾

  • 标准化是生命线:量纲差异 > 10 倍时,不标准化的聚类必然失真;scale() 是黄金法则
  • 聚类画像三步法:计算聚类中心 → 横向对比关键指标 → 用业务语言命名群体
  • 层次聚类的局限:计算复杂度为 \(O(n^2)\),样本量超过几千时运算极慢
  • 今天的主题K均值聚类——预先指定 k 个中心,通过迭代优化,更适合大数据集

本讲内容

  • Part 1:K均值聚类概览 ——与层次聚类的关系与区别
  • Part 2:K均值算法原理 ——六步逐步迭代推导(完整手算示例)
  • Part 3:k 值的选择 ——肘部法则与轮廓系数
  • Part 4:R 语言实现 ——kmeans、可视化与结果解读
  • Part 5:mtcars 聚类实训 ——用内置数据集走完完整分析流程
  • Part 6:小结 ——层次聚类 vs K均值聚类并排对比

Part 1:K均值聚类概览

从层次聚类到划分聚类的跨越

为什么需要 K均值聚类?

场景:你有 10 万条客户交易记录,需要快速分群

  • 层次聚类的瓶颈:计算 \(n \times n\) 距离矩阵,\(n=100,000\) 时需存储 \(10^{10}\) 个数值——内存爆炸
  • K均值的优势:只需计算每个样本到 k 个中心的距离,复杂度仅为 \(O(n \cdot k \cdot t)\)(t=迭代次数)
  • 代价:必须预先指定 k 值;结果依赖初始随机中心;对非球形簇效果欠佳

提示

K均值 vs 层次聚类——最核心的区别

层次聚类 K均值聚类
需要预设 k ❌ 不需要 必须预设
输出形式 谱系图(树状结构) k 个簇的分配标签
计算复杂度 \(O(n^2)\)\(O(n^3)\) \(O(n \cdot k \cdot t)\)
适合数据量 小样本(< 5000) 大数据集(百万级可行)
能否撤销合并 ❌ 一旦合并不可逆 ✅ 每轮迭代可重新分配

K均值聚类的核心思想

目标:找到 k 个"最优中心点",使每个样本到其所属中心的距离之和最小

注记

WCSS(Within-Cluster Sum of Squares):簇内平方和,是衡量聚类质量的核心指标。WCSS 越小,说明每个簇内的样本越紧凑、越相似。

Part 2:K均值算法原理

迭代优化:从随机初始化到稳定收敛

K均值算法的四个步骤

算法核心:反复"分配—更新—再分配",直到中心不再移动

重要

关键数学保证:每次"分配+更新"循环后,WCSS 单调不增——算法一定会收敛,但不一定收敛到全局最优(可能是局部最优)。因此实践中通常运行多次(nstart=25),取WCSS最小的结果。

初始状态(6个点,k=2)

数据:6个二维点(A–F),目标:用 K均值聚为 2 组

第 1 轮分配

以真实样本 A 和 C 作为初始质心 设定初始质心:μ₁ 选定 A(1.0, 1.5),μ₂ 选定 C(3.0, 3.5)。右侧远端样本因距离原因暂归属于 C

警告

当前临时聚类状态:簇1 = {A, B},簇2 = {C, D, E, F}。由于右侧三个高坐标点的强力拉扯,橙色组的几何中心即将会发生剧烈漂移!

第 1 轮更新

右侧高值引发橙色质心“大漂移”,重新计算两簇的均值坐标,橙色质心被大幅度拉向右上方,与 C 点彻底拉开距离

第 2 轮分配

边界样本 C 的“戏剧性反转”,用全新质心重新度量距离:橙色质心离 C 变远了!C 点果断脱离橙色组,重新回归蓝色大家庭!

第 3 轮验证

再无成员变动,迭代完美终止。

最终状态:质心不再移动,算法完美收敛!

重要

K均值算法完整回顾:随机初始化 → 分配(1步)→ 更新质心 → 再分配(1步,结果不变)→ 收敛
本例最终 WCSS = 12.33,两个簇完美分离,聚类效果极佳。

Part 3:k 值的选择

肘部法则:让数据告诉你最佳 k 值

肘部法则(Elbow Method)

核心思路:k 增大 → WCSS 必然下降;找"下降速度骤减"的拐点

轮廓系数(Silhouette Score)

轮廓系数:同时考虑"簇内紧凑度"与"簇间分离度",是更严格的评估指标

\[s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}\]

提示

实践建议:同时使用肘部法则(看 WCSS 拐点)和轮廓系数(取最大值对应的 k);若二者结论一致,则该 k 值可信度更高。

Part 4:R 语言实现

scalekmeansplot → 解读结果

核心代码:五步走

数据集: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行),但可以大幅提升结果的稳定性与全局最优性。

Part 5:本讲小结

层次聚类 vs K均值聚类——全面对比

层次聚类 vs K均值聚类:并排对比

两种方法的适用场景与选择建议

提示

黄金组合:先用层次聚类在小样本上探索最优 k 值(看谱系图),再用K均值在全量数据上高效执行。两步走,兼顾准确性与计算效率。

本讲小结

  • K均值目标:最小化 WCSS(簇内平方和);算法保证单调收敛,但可能是局部最优

  • 四步迭代:随机初始化 k 个质心 → 分配(最近质心原则)→ 更新(重算质心均值)→ 判断收敛;nstart=25 多次运行取最优

  • k 值选择:肘部法则(WCSS 拐点)+ 轮廓系数(最大化 \(s(i)\))双重验证,结合领域知识

  • R 核心代码scalekmeans(nstart=25) → 查看 $centers/$size/$tot.withinsstable() 验证

  • mtcars 实训:k=3 时自动发现省油小车 / 均衡中车 / 高性能跑车三类,与气缸数高度吻合

  • vs 层次聚类:K均值更快(\(O(nkt)\))、更适合大数据、可迭代纠错;但需预设 k,对球形以外的簇效果有限

  • 互补策略:先用层次聚类探索 k → 再用 K均值处理全量数据,是实战中的黄金组合

作业

用R内置数据iris进行K均值聚类

数据标准化

▶️ 查看代码
iris_scaled <- scale(iris[,1:4])
head(iris_scaled)
     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

确定最佳 k 值

▶️ 查看代码
library(factoextra)
library(ggplot2)
fviz_nbclust(iris_scaled, kmeans, method = "wss")

▶️ 查看代码
fviz_nbclust(iris_scaled, kmeans, method = "silhouette")

执行 K 均值聚类(k=3)

▶️ 查看代码
set.seed(123)
km_iris <- kmeans(iris_scaled, centers = 3, nstart = 25)
table(km_iris$cluster)

 1  2  3 
50 53 47 

结果可视化

▶️ 查看代码
fviz_cluster(km_iris, data = iris_scaled)

可视化:用 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均值的智慧在于:不停地问每一个点——'你现在的归属,真的是你最近的家吗?'——直到所有点都满意为止。」