引例:
层次聚类就像玩一个“分组游戏”🎮。想象你有一堆小动物玩具🐶🐱🐰,你想把它们分成几组,但你不知道该怎么分。你可以这样做:
-
开始:一开始,每个小动物都是自己的一小组。🐶 一组,🐱 一组,🐰 一组……
-
找最像的两个小动物:看看哪两个小动物最像(比如都是小狗🐶🐶,或者颜色一样),把它们合并成一个稍大一点的组。
-
重复:继续找最像的两个组(可能是单个小动物,或者已经合并的组),再把它们合并。
-
直到结束:一直这样合并,直到所有的小动物都变成一个大组。🐶🐱🐰……
最后,你会得到一个“分组树”🌳,从最下面的一片片叶子(单个小动物)慢慢长到最上面的树干(所有小动物在一起)。你可以根据这棵树决定怎么分组。原理解释:
层次聚类通过计算数据点之间的距离(比如看它们有多像),逐步将最近的点或组合并,形成一个树状结构(称为“树状图”或“聚类树”🌳)。它有两种主要方式:
-
凝聚法(自底向上):从单个点开始,逐步合并最近的组。
-
分裂法(自顶向下):从整个数据集开始,逐步分裂成更小的组。
数学原理
层次聚类的核心是计算数据点之间的距离或相似度。常用的距离度量包括:
-
欧氏距离:
对于两个点 x和 y,欧氏距离公式为: -
这就像在平面上测量两点之间的直线距离📏。
-
簇间距离:
当合并两个簇时,需要计算簇间的距离。常用的方法有:-
单链法:用两个簇中最近的两个点的距离。
-
全链法:用两个簇中最远的两个点的距离。
-
平均法:用两个簇中所有点对的平均距离。
-
质心法:用两个簇的质心(中心点)之间的距离。
-
-
树状图(Dendrogram):
树状图记录了每次合并的簇和它们的距离。通过“剪枝”✂️(选择一个距离阈值),可以将数据分成不同的簇。
Python代码
# 导入必要的库
import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt
# 设置 Matplotlib 支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体为 SimHei(黑体)
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 生成示例数据
# 假设我们有 10 个二维数据点
np.random.seed(42) # 设置随机种子,确保结果可重复
data = np.random.rand(10, 2) # 生成 10 个二维随机点
# 打印生成的数据
print("生成的数据点:")
print(data)
# 使用层次聚类进行聚类
# linkage 函数用于计算簇之间的距离
# method='single' 表示使用单链法(最近邻距离)
# metric='euclidean' 表示使用欧氏距离
linked = linkage(data, method='single', metric='euclidean')
# 打印层次聚类的结果
print("\n层次聚类的结果(链接矩阵):")
print(linked)
# 绘制树状图(Dendrogram)
plt.figure(figsize=(10, 7)) # 设置画布大小
dendrogram(linked, labels=range(1, 11)) # 绘制树状图,标签为 1 到 10
plt.title("层次聚类的树状图") # 设置标题
plt.xlabel("数据点索引") # 设置 x 轴标签
plt.ylabel("距离") # 设置 y 轴标签
plt.show() # 显示图像
结果展示:
生成的数据点:
[[0.37454012 0.95071431]
[0.73199394 0.59865848]
[0.15601864 0.15599452]
[0.05808361 0.86617615]
[0.60111501 0.70807258]
[0.02058449 0.96990985]
[0.83244264 0.21233911]
[0.18182497 0.18340451]
[0.30424224 0.52475643]
[0.43194502 0.29122914]]
层次聚类的结果(链接矩阵):
[[ 2. 7. 0.0376467 2. ]
[ 3. 5. 0.11030352 2. ]
[ 1. 4. 0.17058938 2. ]
[ 8. 9. 0.26616347 2. ]
[10. 13. 0.27237142 4. ]
[ 0. 11. 0.32755369 3. ]
[12. 15. 0.33198071 5. ]
[14. 16. 0.34891009 9. ]
[ 6. 17. 0.39916488 10. ]]