层次聚类如同"修家谱",通过逐步合并最相似的数据点,构建层级结构。自底向上法(最常用)从每个数据为独立簇开始,不断合并距离最近的簇,形成树状图(Dendrogram),可灵活选择聚类数量。相比K-Means需预设类别数,层次聚类提供全景式关系分析,适用于生物分类、组织架构、文档主题分层等场景。常用链接策略包括Ward法(方差最小)、平均距离与最小/最大距离,各有优劣。虽计算复杂度高(O(N³)),但对小规模数据能揭示深层结构,是探索性数据分析的利器。

简单理解
层次聚类 (Hierarchical Clustering) 就像是在**"修家谱"或者"画组织架构图"**。
它的核心思想不是一来就强行分堆(像 K-Means 那样),而是通过**"找亲戚"**的方式,一步步建立起数据之间的联系。
它分为两种截然相反的思路:
思路一:自底向上 (Agglomerative) ------ "草根逆袭"
(这是最常用的方法)
故事:
- 开局: 每个人都是一个独立的部落(假设有 100 个人,就有 100 个部落)。
- 第一轮: 大家互相看看,离得最近 的两个人,立刻拥抱,合并成一个新的小部落。
- 现在剩 99 个部落。
- 第二轮: 剩下的部落(有的是单人,有的是双人)再互相看,离得最近的两个部落,再次合并。
- 现在剩 98 个部落。
- 结局: 一直合啊合,直到最后所有人都合并成了一个巨大的帝国。
关键点:
在这个过程中,系统会记录下谁和谁在第几轮合并了。把这个过程画出来,就是一棵倒着的树。
思路二:自顶向下 (Divisive) ------ "分封诸侯"
(这种方法计算量大,用得少)
故事:
- 开局: 全世界属于一个大帝国(所有数据是一类)。
- 第一轮: 皇帝觉得人太多不好管,把帝国一分为二,切成两半(通常切差异最大的地方)。
- 第二轮: 两个诸侯国觉得内部差异还是大,于是各自再分裂。
- 结局: 一直分啊分,直到每个人都变成了一个独立的个体。
这种聚类有什么好处?
相比于 K-Means,层次聚类最大的好处是:它给你一张"全景图"。
- 在 K-Means 里: 你必须告诉机器"我要分 3 类",它就给你 3 类。如果你后悔了想分 4 类,得重算。
- 在层次聚类里: 机器先把树画好。
- 你想分 3 类?就在树的上面切一刀。
- 你想分 10 类?就在树的下面切一刀。
- 你可以随心所欲地决定分几类,不需要提前预设。
一句话总结
层次聚类就是:"远亲不如近邻,近邻不如对门。"
先把对门拉进群,再把邻居拉进群,最后把全小区拉进群。
应用场景
层次聚类因为计算量比较大(要算所有点两两之间的距离,复杂度 O(N2)O(N2)),所以不适合几万、几十万的大数据。
但它有一个 K-Means 无法比拟的优势:它能展现数据的层级结构(Hierarchy)。
只要你的目的是**"分析结构"**而不是简单的"分堆",首选层次聚类。
1. 生物信息学(基因/物种分类) ------ 绝对主场
这是层次聚类最老本行的应用。
- 场景: 科学家测出了 100 种动物的 DNA,想画出它们的进化树(Evolutionary Tree)。
- 做法: 层次聚类。
- 结果: 树状图会清晰地告诉你:
- 人、猴子、猩猩是很早就在树的底部聚在一起了(亲缘近)。
- 而它们和猫、狗是在树的更高层才聚在一起的(哺乳动物大家族)。
- 最后才和鱼、鸟聚在一起(脊椎动物)。
- K-Means 做不到: K-Means 只能把它们分成几堆,但告诉你谁是谁的祖宗、谁是谁的表亲。
2. 公司组织架构分析
- 场景: 一家大公司,员工之间有很多沟通记录(邮件、钉钉)。想通过数据自动分析出公司的非正式组织架构。
- 做法: 计算员工之间的沟通频率作为"距离"。
- 结果:
- 最底层聚在一起的,是同一个项目组的(天天聊)。
- 再往上一层,是同一个部门的(周会聊)。
- 再往上,是同一个事业群的。
- 这能帮老板发现:"哎?这两个部门明明业务没关系,为什么聚得这么近?是不是有猫腻?"
3. 文档/新闻的主题层级分析
- 场景: 整理一个巨大的知识库。
- 做法: 对文章进行层次聚类。
- 结果:
- 你切一刀(粗粒度):分成"科技"、"体育"、"政治"。
- 你在"科技"这枝上再切一刀(细粒度):分成了"互联网"、"半导体"、"生物科技"。
- 这对于构建目录导航非常有帮助。
4. 异常值检测(离群点挖掘)
虽然 DBSCAN 更擅长这个,但层次聚类也能看出来。
- 场景: 信用卡反欺诈。
- 做法: 画出树状图。
- 结果: 正常人的消费习惯会很早就聚成一大坨。而那个骗子,可能在树状图的最顶端,孤零零地挂着,直到最后一步才勉强被合并进去。这种**"晚合并"**的点,通常就是异常点。
5. 图像分割 (Image Segmentation)
- 场景: 自动把照片里的物体扣出来(PS 里的魔棒工具)。
- 做法: 把像素点看作数据。
- 结果:
- 最底层:像素颜色相近的聚在一起(比如一只猫身上的毛)。
- 上一层:猫身和猫头聚在了一起(整只猫)。
- 再上一层:猫和草地聚在了一起(背景)。
- 这种层级结构方便计算机理解"物体"和"背景"的关系。
总结
层次聚类是"小而美"的分析工具。
当你手头数据不多(几百几千条),但你想把它们的关系扒得底掉,想看清谁跟谁亲、谁跟谁疏时,用它准没错。
数学推导
层次聚类(以自底向上 Agglomerative 为例)的数学推导,其实就是回答一个核心问题:
"怎么定义两个'部落'(簇)之间的距离?"
我们知道怎么算两个点 之间的距离(欧氏距离),但在合并过程中,我们面对的是两个集合(Cluster A 和 Cluster B)。
- 集合 A:{张三, 李四}
- 集合 B:{王五, 赵六, 钱七}
请问:集合 A 和集合 B 离得有多近?
数学上定义了三种最常用的**"部落距离" (Linkage Criteria)**,这直接决定了聚类的形状和效果。
1. 最小距离 (Single Linkage / Min) ------ "只要有一个朋友,我们就是亲戚"
定义: 两个集合里,离得最近 的那两个人的距离。
D(A,B)=min{d(x,y):x∈A,y∈B}D(A,B)=min{d(x,y):x∈A,y∈B}
- 逻辑: 只要 A 里的张三和 B 里的王五是好朋友,那 A 和 B 就算很近。
- 数学后果:
- 优点: 能处理非球形数据(比如两个长条形的蛇形数据,只要头尾相接就能连起来)。
- 缺点: 容易产生**"长链效应" (Chaining Effect)**。哪怕 A 和 B 只有这一对人认识,其他人隔着十万八千里,也会被强行拉在一起,导致聚类结果像一条细长的链子。
2. 最大距离 (Complete Linkage / Max) ------ "所有人都要合得来"
定义: 两个集合里,离得最远 的那两个人的距离。
D(A,B)=max{d(x,y):x∈A,y∈B}D(A,B)=max{d(x,y):x∈A,y∈B}
- 逻辑: 只有当 A 里的所有人 和 B 里的所有人,都比较近的时候,A 和 B 才能算近。哪怕有一个人(李四)离对面(赵六)很远,这个距离就被拉大了。
- 数学后果:
- 优点: 聚出来的类非常紧凑,都是一个个结实的小团伙。
- 缺点: 对异常值(离群点)非常敏感。如果 A 里有个怪人离得很远,整个部落都被他拖累了。
3. 平均距离 (Average Linkage) ------ "大家平均关系不错"
定义: 两个集合里,所有点对所有点 的距离的平均值。
D(A,B)=1∣A∣⋅∣B∣∑x∈A∑y∈Bd(x,y)D(A,B)=∣A∣⋅∣B∣1∑x∈A∑y∈Bd(x,y)
- 逻辑: 折中方案。不看极端(最近或最远),看整体平均水平。
- 数学后果:
- 这是最稳健、最常用的方法。它综合了前两者的优点,既比较紧凑,又没那么怕异常值。
4. Ward's Method (沃德法) ------ "方差最小化"
这是另一种思路,它不直接算距离,而是算**"合并后的代价"**。
定义:
如果我把 A 和 B 合并成一个新的大集合 C,那么 C 内部的**方差(ESS, Error Sum of Squares)**会增加多少?
- 逻辑:
- 如果 A 和 B 本来就很像,合并后内部方差增加很少。
- 如果 A 和 B 差异很大,强行合并后,内部方差会暴涨。
- 数学后果:
- Ward 法倾向于聚出大小差不多、形状像球一样的类。
- 这和 K-Means 的目标函数(最小化平方误差)在数学本质上是一样的。
算法流程推导 (Algorithm)
- 输入: NN 个数据点,计算 N×NN×N 的距离矩阵。
- 初始化: 把每个点看作一个簇 CiCi。
- 循环:
- 在距离矩阵中,找到数值最小的那一个格子 (Ci,Cj)(Ci,Cj)。
- 合并: 将 CiCi 和 CjCj 合并为 CnewCnew。
- 更新距离矩阵:
- 划掉 CiCi 和 CjCj 的行列。
- 新增 CnewCnew 的行列。
- 利用上面的 Linkage 公式(Min/Max/Avg),计算 CnewCnew 和剩下所有旧部落的距离。
- 终止: 当只剩下一个簇时,结束。
复杂度推导:
因为每次循环都要扫描矩阵找最小值,且要更新矩阵。如果不做优化,复杂度是 O(N3)O(N3)。优化后(使用堆结构)可以降到 O(N2logN)O(N2logN)。
这就是为什么层次聚类处理不了 10万+ 数据的原因------矩阵太大,算不动。
代码实例
这段代码将带你体验层次聚类的核心魅力:树状图 (Dendrogram)。
我们将使用 scipy 库(它在层次聚类方面比 sklearn 更直观、功能更强)来从头构建并绘制这棵树,然后再用 sklearn 来展示如何获取最终的分类标签。
Python 代码实现
请确保安装了 numpy, matplotlib, scipy, sklearn。
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_blobs
# ==========================================
# 0. 准备数据
# ==========================================
# 生成 5 个小团伙的数据
X, y = make_blobs(n_samples=50, centers=5, random_state=42, cluster_std=1.0)
# ==========================================
# 1. Scipy 实现与可视化 (画出族谱树)
# ==========================================
print(">>> 正在计算层次聚类矩阵 (Linkage Matrix)...")
# linkage 函数执行层次聚类核心算法
# method='ward': 使用 Ward 方差最小化法 (最常用,聚类效果比较圆润)
# metric='euclidean': 使用欧氏距离
Z = linkage(X, method='ward', metric='euclidean')
# Z 是一个 (n-1) x 4 的矩阵,记录了每一步合并的过程
# 格式: [索引1, 索引2, 距离, 新簇样本数]
print(">>> 正在绘制树状图 (Dendrogram)...")
plt.figure(figsize=(12, 6))
plt.title("Hierarchical Clustering Dendrogram")
plt.xlabel("Sample Index")
plt.ylabel("Distance (Ward's linkage)")
# 画图
dendrogram(
Z,
leaf_rotation=90., # 标签旋转角度
leaf_font_size=8., # 字体大小
)
# 画一条水平线,模拟"剪枝"操作
cut_distance = 7.0
plt.axhline(y=cut_distance, c='k', ls='--', lw=2, label=f'Cut Height={cut_distance}')
plt.legend()
plt.show()
# ==========================================
# 2. Sklearn 实现 (获取聚类标签)
# ==========================================
print(f"\n>>> 正在使用 Sklearn 进行聚类 (n_clusters=5)...")
# AgglomerativeClustering 是 sklearn 的标准接口
# n_clusters=5: 告诉它我想切成 5 块 (相当于在树上某处切了一刀)
cluster = AgglomerativeClustering(n_clusters=5, metric='euclidean', linkage='ward')
cluster_labels = cluster.fit_predict(X)
# --- 可视化聚类结果 ---
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c=cluster_labels, cmap='viridis', s=50)
plt.title("Agglomerative Clustering Result (n_clusters=5)")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.show()
# ==========================================
# 3. 不同的 Linkage 效果对比 (Min vs Max vs Ward)
# ==========================================
print("\n>>> 对比不同 Linkage 策略的效果...")
methods = ['ward', 'single', 'complete']
plt.figure(figsize=(15, 5))
for i, method in enumerate(methods):
# 训练
model = AgglomerativeClustering(n_clusters=5, metric='euclidean', linkage=method)
labels = model.fit_predict(X)
# 绘图
plt.subplot(1, 3, i + 1)
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='tab10', s=30)
plt.title(f'Linkage: {method}')
plt.tight_layout()
plt.show()



代码深度解析与看点
1. 树状图 (Dendrogram) ------ 最重要的一张图
运行代码后,第一张图就是这棵树。
- X轴: 是那 50 个样本点(叶子)。
- Y轴: 代表距离(合并时的代价)。Y 轴越高,说明这两个部落离得越远,合并得越晚。
- U形结构: 每一道 U 形线,代表一次合并。
- 剪枝线 (虚线): 我在 Y=7.0 的地方画了一条黑线。
- 你可以数一下,这条线切断了多少根竖线?答案应该是 5 根。
- 这就意味着,如果你在这个距离停止合并,你就会得到 5 个类。
- 这就是层次聚类定 K 值的逻辑: 看着图,找竖线最长、最稀疏的地方切一刀。
2. Sklearn 的聚类结果
第二张图展示了切完之后的效果。你会发现颜色分得很清楚,这证明算法成功把这 5 堆数据找回来了。
3. 三种 Linkage 的对比图
第三张图展示了 ward (默认/方差), single (最小距离), complete (最大距离) 的区别。
- Ward: 分出的类最均匀,形状像圆球(最推荐)。
- Single: 也就是"最小距离"。你会发现它容易把一些离得近的点连成一片,甚至可能会出现"一个大类吞并所有,剩下几个孤零零的点"的情况(长链效应)。
- Complete: 也就是"最大距离"。它分出的类比较紧凑,但也可能把本来属于一伙的人强行拆散。