数据挖掘与R语言

第19讲:层次聚类(二)——标准化、聚类画像与实战

2026年06月03日

上讲回顾

  • 聚类三要素:距离度量(欧氏/曼哈顿/切比雪夫)→ 合并算法(Single/Complete/Average/centroid/Ward.D2)→ 树状图切割(k 值选取)
  • 凝聚法五步:每样本自成一类 → 找最近两类合并 → 重算距离 → 重复 → 全程记录为谱系图
  • 标准化必要性scale() 消除量纲影响,财务数据必须执行
  • R 核心代码scaledisthclustplotcutreerect.hclust
  • iris 验证:setosa 完美分离,整体准确率约 91%

本讲内容

  • Part 1:知识点回顾与本讲重难点 ——标准化
  • Part 2:实战演示 ——为什么不标准化的聚类是"灾难"?
  • Part 3:案例剖析 ——聚类画像:从数字到群体特征描述
  • Part 4:分类变量的距离计算 ——Gower 距离与混合型数据处理
  • Part 5:R 实训 ——college 数据集完整分析(对应课后作业)
  • Part 6:总结与能力拓展 ——保守型 vs 激进型客户的差异化策略

Part 1:重难点导入

本讲核心:让聚类结果"开口说话"

本讲重点与难点

本讲重点

对聚类产生后的"群体特征"进行文字描述——即聚类画像(Cluster Profiling)

聚类的终点不是一张谱系图,而是能够回答"每一类客户/学校/企业是什么特征?"

本讲难点

标准化(Standardization)在实际财务数据中的必要性

财务数据的典型挑战:

  • 资产总额:单位亿元,数量级 10⁸
  • 资产周转率:单位次/年,数量级 10⁰
  • 不标准化 → 大数值变量完全主导距离计算 → 聚类结果失真

三个能力层次(本讲目标):

层次 目标
🔵 低阶 巩固 disthclust 的代码流程
🟡 中阶 熟练使用 rect.hclust() 切分;理解聚类中心的财务含义
🔴 高阶 根据企业特征独立完成聚类画像,制定差异化财务策略

Part 2:实战演示

标准化 vs 不标准化——真实的对比

场景设定:4家企业,跨行业财务数据

假设我们有 4 家上市公司(A、B、C、D),两项财务指标如下: * \(x_1\) (核心质地指标):如净资产收益率、研发投入比。数值小,但反映本质。 * \(x_2\) (资产规模指标):量纲极大的绝对数(百万元)。

样本企业 \(x_1\) (核心质地) \(x_2\) (资产规模)
A 0 1000
B 1 1500
C 10 1001
D 12 1801

🤔 思考:如果让你把它们分成两类,你会怎么分?为什么?

关键问题:量纲不同时,算法"看到"的距离已经失真

如果不标准化,算法怎么想?

如果我们直接把原始数据扔进欧氏距离公式: \[d = \sqrt{(\Delta x_1)^2 + (\Delta x_2)^2}\]

▶️ 查看代码
demo_data <- data.frame(
  样本 = c("A", "B", "C", "D"),
  x1 = c(0, 1, 10, 12),
  x2 = c(1000, 1500, 1001, 1801)
)

dist(demo_data[,2:3])
       1      2      3
2 500.00              
3  10.05 499.08       
4 801.09 301.20 800.00

未标准化,计算出来的欧式距离显示A和C较近,其次是B和D较近。

标准化后

▶️ 查看代码
dist(scale(demo_data[,2:3]))
      1     2     3
2 1.277            
3 1.631 1.937      
4 2.819 1.949 2.052

注记

标准化后,量纲较大的\(x_2\)不再有那么大的影响力。

标准化的数学原理

scale() 做了什么?

\[z_i = \frac{x_i - \bar{x}}{s_x}\]

标准化后每个变量:均值 = 0,标准差 = 1,所有变量处于同等地位

提示

黄金法则:只要变量的量纲不同(或数量级差异超过 10 倍),聚类前必须 scale()

场景 是否必须标准化
资产(亿)+ 周转率(次) 必须
花瓣长(cm)+ 花瓣宽(cm) 建议
全部为百分比数据 ⚠️ 视情况
已经是标准分数(Z-score) ❌ 无需

Part 3:案例剖析

聚类画像:从分组标签到群体特征描述

什么是聚类画像(Cluster Profiling)?

聚类的终点不是数字,而是能够"说出来"的群体故事

三步聚类画像法:

案例:企业财务聚类画像

提示

聚类命名示例

  • 激进扩张型:资产规模大(800亿)、高负债(65%)、利润率适中——"规模先行,以债驱动"
  • 稳健成长型:资产规模小(200亿)、低负债(30%)、高利润率(~15%)——"精耕细作,内生增长"
  • 保守防御型:中等规模、中等负债、低利润——"维持现状,无明显战略方向"

聚类中心(Cluster Centroids)的财务含义

聚类中心 = 该类的"典型代表",是群体特征描述的数字基础

▶️ 查看代码
# 计算聚类中心(各类均值)
centroids <- college_clustered %>%
  group_by(cluster) %>%
  summarise(
    均值_Apps   = mean(Apps),     # 对数转换后的申请数
    均值_Accept = mean(Accept),   # 对数转换后的录取数
    均值_Expend = mean(Expend),   # 对数转换后的生均经费
    样本量      = n()
  )

print(centroids)

注记

解读聚类中心的三个步骤:

  1. 纵向看:同一类内,哪些指标值最高/最低?→ 确定该类的"标签"
  2. 横向比:不同类之间,同一指标的均值差异有多大?→ 区分类别的"关键变量"
  3. 回原始尺度:若做过对数变换,要换算回原始量级(exp())才有业务含义

Part 4:分类变量的距离计算

聚类不只适用于数值型变量

为什么需要处理分类变量?

现实数据往往是"混合型"的

注记

前两列(年龄、年收入)是数值型,可直接计算欧氏距离;后三列(学历、是否购房、风险偏好)是分类型,不能直接计算距离。混合数据需要专门的距离方法。

分类变量的距离:三种思路

思路一:简单匹配(Simple Matching)——二元变量

对于只有两个取值的二元变量(是/否、男/女),定义:

\[d_{ij}^{\text{二元}} = \begin{cases} 0 & \text{两者取值相同} \\ 1 & \text{两者取值不同} \end{cases}\]

思路二:虚拟变量编码(One-Hot Encoding)——多分类变量

将有 \(k\) 个类别的变量展开为 \(k\) 列 0/1 虚拟变量,再用欧氏距离或曼哈顿距离:

学历(原始)       →   本科  硕士  博士
  "本科"          →     1     0     0
  "硕士"          →     0     1     0
  "博士"          →     0     0     1

警告

虚拟变量列数越多,数值型变量的相对权重越小——列数多时仍需标准化或权重调整。

思路三:Gower 距离——混合型数据的通用方案

\[d^{\text{Gower}}_{ij} = \frac{\sum_{f=1}^{p} w_f \cdot d^{(f)}_{ij}}{\sum_{f=1}^{p} w_f}\]

对每一个变量 \(f\) 单独计算"部分距离",再加权平均:

变量类型 部分距离 \(d^{(f)}_{ij}\)
连续型 \(\|x_{if} - x_{jf}\| / \text{range}_f\),归一化到 [0,1]
二元型 0(相同)或 1(不同)
名义型(多分类) 0(相同)或 1(不同)
有序型(等级) 按秩归一化后取差值

Gower 距离的 R 实现

cluster 包的 daisy() 函数自动识别变量类型

▶️ 查看代码
library(cluster)

# 混合型数据示例
customers <- data.frame(
  年龄     = c(28, 45, 31, 52),
  年收入_万 = c(12, 38, 15, 60),
  学历     = factor(c("本科","硕士","本科","博士"),
                    levels=c("本科","硕士","博士"), ordered=TRUE),  # 有序因子
  是否购房 = factor(c("否","是","否","是")),      # 名义因子
  风险偏好 = factor(c("激进","保守","激进","保守")) # 名义因子
)

# daisy() 自动处理混合类型,metric="gower" 时无需预先标准化数值列
gower_dist <- daisy(customers, metric = "gower")
round(as.matrix(gower_dist), 3)
      1     2     3     4
1 0.000 0.750 0.038 1.000
2 0.750 0.000 0.713 0.250
3 0.038 0.713 0.000 0.963
4 1.000 0.250 0.963 0.000
▶️ 查看代码
# 对 Gower 距离矩阵直接做层次聚类
fit_mixed <- hclust(gower_dist, method = "average")
cutree(fit_mixed, k = 2)   # 切分为 2 类
[1] 1 2 1 2

提示

关键优势daisy() 自动识别因子、有序因子和数值列,无需手动编码;Gower 距离内置了对每个变量的归一化,数值列和分类列天然处于同等权重,通常不必再额外 scale()

方法选择指南

重要

常见误区:对分类变量强行使用数值编码(如将学历编为 1/2/3),隐含了"博士与本科的距离是硕士与本科的两倍"这样的假设。若没有明确理由支持这种等距假设,应使用 ordered factor 配合 daisy(),让算法按秩归一化处理。

Part 5:R 实训练习

college 数据集完整分析流程

数据准备:读入与预处理

项目背景:college 数据集包含美国高校的办学信息,对私立学校进行聚类分析,探索私立高校的分类特征。

▶️ 查看代码
library(dplyr)

# 读入数据,指定变量类型
college <- read.csv("college.csv",
  colClasses = c(rep("character", 2), rep("numeric", 17))) #指定前两列为字符型,后17列为数值型

# 只保留私立学校
college <- college %>% filter(Private == "Yes")

str(college)

# 删除 Private 列(第2列)
college <- college[-2]
# college <- college %>%
#   select(-2)


# 对有极值的连续变量进行对数变换
college <- college %>%
  mutate(
    Apps       = log(Apps),
    Accept     = log(Accept),
    Enroll     = log(Enroll),
    F.Undergrad = log(F.Undergrad),
    P.Undergrad = log(P.Undergrad),
    Books      = log(Books),
    Personal   = log(Personal),
    Expend     = log(Expend)
  )

hist(college$Expend)

注记

为什么对这些变量做对数变换? Apps(申请数)等变量存在极右偏(少数超级名校申请量极大),对数变换可压缩极值、使分布更接近正态,令距离计算更稳健。

步骤 1:标准化处理

将所有连续变量(2–18列)进行标准化处理,并查看前三行记录。

▶️ 查看代码
# 对第2–18列(所有连续变量)进行标准化
college_scale <- scale(college[,2:18], center = TRUE, scale = TRUE)
head(college_scale, 3)
       Apps Accept   Enroll Top10perc Top25perc F.Undergrad P.Undergrad
[1,] 0.3200 0.3674  1.01626   -0.3546   -0.2531      1.0116      0.7442
[2,] 0.6123 0.8946  0.55596   -0.7468   -1.4273      0.9132      1.2961
[3,] 0.1602 0.2302 -0.01043   -0.4107   -0.3552     -0.3761     -0.3851
     Outstate Room.Board   Books Personal      PhD Terminal S.F.Ratio
[1,]  -1.1765    -1.1803 -0.6306   1.5082 -0.06304  -0.0346   1.46494
[2,]   0.1290     1.7104  1.3800   0.6890 -2.42603  -3.1413  -0.21187
[3,]  -0.1488    -0.7673 -1.0942   0.1484 -1.04282  -0.8113  -0.01293
     perc.alumni  Expend Grad.Rate
[1,]     -1.1201 -0.7479   -0.5372
[2,]     -0.7976  0.2430   -0.7760
[3,]      0.3314 -0.2168   -0.8954

提示

解读:标准化后,每列的均值 ≈ 0,标准差 = 1。正值表示该校在该指标上高于平均水平,负值表示低于平均。

步骤 2:层次聚类与谱系图

对标准化数据做层次聚类(平均链接法),画谱系图。

▶️ 查看代码
# 计算欧氏距离矩阵
# distance <- dist(college_scale)
# 
# # 平均链接法层次聚类
# fit <- hclust(distance, method = "average")

fit <- hclust(dist(college_scale), method = "average")

# 绘制谱系图
plot(fit, hang = -1, labels = FALSE,
     main = "私立高校层次聚类谱系图(Average Linkage)",
     xlab = "", sub = "", ylab = "欧氏距离")

注记

谱系图分析:观察纵轴高度的"大跳跃"位置——两个相邻合并步骤之间距离差最大处,是确定 k 值的参考依据。本数据集高度跳跃约在 6–8 之间,提示可考虑 k=4 或 k=5。

步骤 3:切分为 5 类,查看各类样本数

▶️ 查看代码
# 切分为 5 类
groups <- cutree(fit, k = 5)

# 查看各类别样本数
table(groups)
groups
  1   2   3   4   5 
520  41   1   2   1 

注记

解读:各类样本数差异反映了私立高校的分布不均匀性——某些类型的学校占多数,另一些属于"特色小众"类型。

步骤 4:在谱系图上叠加类别划分

▶️ 查看代码
# 在谱系图上叠加 5 类划分
plot(fit)
rect.hclust(fit, k = 5,
            border = c("#4472C4","#e05c2a","#2eab6e","#e8a838","#9b59b6"))

步骤 5:将聚类结果加入原始数据框

▶️ 查看代码
# 将聚类结果合并到原始数据框
# college_clustered <- college %>%
#   mutate(cluster = factor(groups))

college_clustered <- data.frame(college, cluster = groups)

# 查看前 6 行
head(college_clustered)
                          Name  Apps Accept Enroll Top10perc Top25perc
1 Abilene Christian University 7.415  7.116  6.581        23        52
2           Adelphi University 7.690  7.562  6.238        16        29
3               Adrian College 7.264  7.000  5.817        22        50
4          Agnes Scott College 6.033  5.855  4.920        60        89
5    Alaska Pacific University 5.263  4.984  4.007        16        44
6            Albertson College 6.375  6.172  5.063        38        62
  F.Undergrad P.Undergrad Outstate Room.Board Books Personal PhD Terminal
1       7.967       6.286     7440       3300 6.109    7.696  70       78
2       7.895       7.112    12280       6450 6.620    7.313  29       30
3       6.943       4.595    11250       3750 5.991    7.060  53       66
4       6.234       4.143    12960       5450 6.109    6.774  92       97
5       5.517       6.767     7560       4120 6.685    7.313  76       72
6       6.519       3.714    13500       3335 6.215    6.515  67       73
  S.F.Ratio perc.alumni Expend Grad.Rate cluster
1      18.1          12  8.860        60       1
2      12.2          16  9.262        56       1
3      12.9          30  9.075        54       1
4       7.7          37  9.853        59       1
5      11.9           2  9.299        15       1
6       9.4          11  9.183        55       1

步骤 6:聚类结果可视化(Apps vs Accept)

▶️ 查看代码
library(ggplot2)
# 以 Apps 为横轴、Accept 为纵轴绘制散点图

ggplot(college_clustered,
       aes(x = Apps, y = Accept, color=as.factor(cluster))) +
  geom_point() +
  scale_shape_manual(values=c("circle", "square", "triangle", "plus", "cross"))

注记

图形分析

  • 图中第一类和第二类样本多一点,第三、四、五类样本数量太少。
  • 第一类的样本申请数和录取率都比较低(低申请、低录取)
  • 第二类的样本申请数和录取率都比较高(高申请、高录取)

步骤 7:对比样本较多的两类均值差异

  • 方法一
▶️ 查看代码
# 筛选样本最多的两个聚类(观察 table(groups) 的结果后填入)
top2_clusters <- college_clustered %>%
  filter(cluster == 1 | cluster == 2)

# 用聚合函数比较 Apps 和 Accept 的均值
top2_clusters %>%
  group_by(cluster) %>%
  summarise(
    均值_Apps   = mean(Apps),
    均值_Accept = mean(Accept)
  )
# A tibble: 2 × 3
  cluster 均值_Apps 均值_Accept
    <int>     <dbl>       <dbl>
1       1      6.97        6.70
2       2      8.89        8.20
  • 方法二
▶️ 查看代码
# 筛选样本最多的两个聚类(观察 table(groups) 的结果后填入)
top2_clusters <- subset(college_clustered,cluster == 1 |cluster == 2)
# 用聚合函数比较 Apps 和 Accept 的均值
aggregate(top2_clusters[,2:3],
          by = list(top2_clusters$cluster),
          FUN = mean)
  Group.1  Apps Accept
1       1 6.972  6.696
2       2 8.891  8.201

重要

结果解读示例:第1类均值_Apps < 第2类,说明第2类学校接收的申请量更多,可能为更知名或规模更大的私立高校;录取数均值同方向变化则进一步验证这一判断。

本讲小结

  • 标准化是聚类的生命线:量纲差异超过 10 倍,不标准化的聚类结果必然失真;scale() 是黄金法则

  • rect.hclust() 一行可视化:在谱系图上直接框出分组,是"确认 k 值"的最直观方式

  • 聚类中心的业务含义group_by(cluster) %>% summarise(mean) 计算各类均值,是"读懂分组"的核心操作

  • 聚类画像三步法:计算聚类中心 → 横向对比关键指标 → 用业务语言为群体命名

  • 分类变量不能直接算距离:使用 daisy(metric="gower") 处理混合型数据;有序变量设为 ordered factor,名义变量设为普通 factor,让算法正确识别

  • 高阶目标:根据群体特征(保守型 vs 激进型)制定差异化策略,这才是聚类的真正价值

能力拓展:保守型 vs 激进型——差异化策略框架

提示

高阶思考:聚类不是终点,画像与策略才是。在实际项目中,对每一类群体,都应能回答:"这类客户/学校/企业的核心特征是什么?对应的管理/经营/投资策略应该如何差异化?"

谢谢!

第19讲:层次聚类(二)——标准化、聚类画像与实战


「聚类的价值,不在于把数据分成几堆,而在于为每一堆找到它独有的名字——这个名字,是数据给决策者的礼物。」