大家好,欢迎来到IT知识分享网。
哈喽,我是小白~
咱们今天聊聊层次聚类,这种聚类方法在后面的使用,也是非常频繁的~
首先,聚类很好理解, 聚类(Clustering)就是把一堆“东西”自动分组 。
这些“东西”可以是人、物品、图像、文本、客户等等。
分组是 让相似的归在一组,不相似的分开 。
核心干货,文末领取
层次聚类
层次聚类就是: 一步一步地合并(或拆分)小组,最后形成一个“层次结构” ,像一棵树一样。
有两种方式:
-
自底向上(常见)
:一开始每个点都是一个小组,然后
两两合并最像的组
,一直合并,直到只剩一个大组。
-
自顶向下(不常用)
:一开始是一个大组,不断拆分。
类比解释
假设有 6 个小朋友,他们身高如下(单位:cm):
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
你不知道他们属于哪个年级,但你怀疑:他们应该可以分成“低年级”和“高年级”。
怎么做层次聚类?
第一步:先把他们看成 6 个独立的组:
A B C D E F
第二步:找“最像”的两个小朋友(距离最小的)
他们的“相似度”可以用“身高差”来表示:
最小的是 AB 和 DE,都是距离 2。
我们先合并 AB,变成一个新组(叫 AB),然后重新计算“组与其他人”的距离。
第三步:继续合并(演示部分)
现在的组是:
[AB] C D E F
我们假设用“组的平均值”代表组的身高:
再计算距离:
现在是:
[AB] C [DE] F
继续:
我们可以任选一个合并,比如 AB 和 C → ABC,平均身高 = (100+102+105)/3 = 102.3
最后也会合并 DE 和 F → DEF,平均身高 = (160+162+165)/3 ≈ 162.3
最终变成:
[ABC] [DEF]
你看到没有?我们 成功把6个小朋友分成了两组:一个是100多身高,一个是160多身高的!
公式解析
我们有一个数据集 ,其中每个数据点 是一个 -维向量。
聚类的目标是把这 个样本划分成 个组(簇,cluster),使得:
这种“相似”通常用 距离函数 衡量。
核心思想:从每个点一个簇开始(共有 个簇),找出两个“最近”的簇,合并成一个新簇;重复,直到只剩一个簇(或满足停止条件)
这个过程会生成一个 嵌套的层级结构(树) ,称为 树状图 。
常用的距离度量公式
数据点之间的距离(欧几里得距离):
对于两个点 和 :
也可以用曼哈顿距离、余弦相似度等。
簇之间的距离计算
层次聚类中,两个簇之间的“距离”可以有不同的定义方式。
最重要最常见的是 4种经典的 Linkage 方法 :
1. 单链接(Single Linkage)
定义两个簇 之间的距离为:
也就是两个簇中 最近的两个点 之间的距离。
2. 全链接(Complete Linkage)
定义两个簇 之间的距离为:
两个簇中 最远的两个点 之间的距离。
3. 平均链接(Average Linkage)
两个簇中 所有点对的平均距离 。
4. 重心法(Centroid Linkage)
先计算簇的 重心(中心) ,再比较两个重心之间的距离:
设 是簇 的重心:
那么:
推导例子
让我们再用前面的数据:
|
|
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
欧几里得距离为:
初始簇:
使用单链接(Single Linkage)
计算最小距离:
最小是 ,合并成
现在:
再计算所有距离,使用 single linkage:
再继续……直到你达到目标簇数。
完整案例
基于上面的小朋友身高示例,生成一个稍微复杂的合成数据集,模拟两类人群(儿童与成年人)在四个维度(身高 Height、体重 Weight、年龄 Age、收入 Income)上的分布,使用平均链接的层次聚类方法进行分析~
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
from scipy.spatial.distance import pdist, squareform
from sklearn.metrics import silhouette_samples, silhouette_score
# 1. 生成合成数据:基于“小朋友”和“成年人”两个簇
np.random.seed(42)
# “小朋友”簇
children_height = np.random.normal(120, 5, 25)
children_weight = children_height * 0.45 + np.random.normal(0, 3, 25)
children_age = np.random.normal(10, 2, 25)
children_income = np.random.normal(50, 10, 25)
# “成年人”簇
adult_height = np.random.normal(170, 5, 25)
adult_weight = adult_height * 0.5 + np.random.normal(0, 5, 25)
adult_age = np.random.normal(35, 5, 25)
adult_income = np.random.normal(200, 50, 25)
# 合并到 DataFrame
data = pd.DataFrame({
'Height': np.concatenate([children_height, adult_height]),
'Weight': np.concatenate([children_weight, adult_weight]),
'Age': np.concatenate([children_age, adult_age]),
'Income': np.concatenate([children_income, adult_income])
})
# 2. 计算欧氏距离矩阵
dist_matrix = pdist(data.values, metric='euclidean')
# 3. 进行层次聚类(平均链接)
Z = linkage(dist_matrix, method='average')
# 4. 绘制树状图(Dendrogram)
plt.figure(figsize=(10, 5))
dendrogram(Z, leaf_rotation=90)
plt.title('Figure 1: Hierarchical Clustering Dendrogram')
plt.xlabel('Sample Index')
plt.ylabel('Distance')
plt.tight_layout()
# 5. 绘制距离矩阵热图
plt.figure(figsize=(8, 6))
dm = squareform(dist_matrix)
plt.imshow(dm, aspect='auto', origin='lower')
plt.title('Figure 2: Distance Matrix Heatmap')
plt.xlabel('Sample Index')
plt.ylabel('Sample Index')
plt.colorbar(label='Euclidean Distance')
plt.tight_layout()
# 6. 根据距离阈值截断,获取簇标签
max_d = 30 # 根据树状图设定
clusters = fcluster(Z, max_d, criterion='distance')
# 7. 绘制 Height vs Weight 散点图,标记簇
plt.figure(figsize=(8, 6))
plt.scatter(data['Height'], data['Weight'], c=clusters)
plt.title('Figure 3: Height vs Weight Clustering')
plt.xlabel('Height (cm)')
plt.ylabel('Weight (kg)')
plt.tight_layout()
# 8. 轮廓系数(Silhouette)分析
sil_score = silhouette_score(data.values, clusters)
sil_samples = silhouette_samples(data.values, clusters)
plt.figure(figsize=(8, 6))
y_lower = 10
for i in np.unique(clusters):
ith_silhouette = sil_samples[clusters == i]
ith_silhouette.sort()
y_upper = y_lower + len(ith_silhouette)
plt.fill_betweenx(np.arange(y_lower, y_upper),
0, ith_silhouette)
plt.text(-0.05, y_lower + 0.5 * len(ith_silhouette), str(i))
y_lower = y_upper + 10
plt.title(f'Figure 4: Silhouette Plot (Score = {sil_score:.2f})')
plt.xlabel('Silhouette Coefficient Values')
plt.ylabel('Cluster Label')
plt.tight_layout()
# 9. 各簇特征分布箱线图
data['Cluster'] = clusters
plt.figure(figsize=(10, 8))
data.boxplot(by='Cluster', layout=(2, 2))
plt.suptitle('Figure 5: Feature Distribution by Cluster')
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
# 显示所有图形
plt.show()
Figure 1 – Dendrogram(树状图)

可以清晰看到:
此图告诉我们左右两侧分别代表两大类群体——高度、体重、年龄、收入都近似的儿童簇与成年人簇。
Figure 2 – Distance Matrix Heatmap(距离矩阵热图)

热图以视觉形式强化:跨两大簇的距离远超簇内距离,聚类分界明显。
Figure 3 – Height vs Weight Clustering(身高-体重散点图)

二维投影依旧能显著区分簇的分布,并验证 平均链接 在主要特征上表现优秀。
Figure 4 – Silhouette Plot(轮廓系数图)

该图量化了簇的内部凝聚力与群间分离度,平均 0.67 属于比较理想的聚类结果。
Figure 5 – Feature Distribution by Cluster(按簇分组特征箱线图)

四个子图分别展示 Age、Height、Income、Weight 在不同簇(1/2)下的箱线分布
箱线图直观展现每个特征在两簇间的中位数、四分位距与异常值情况。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/186425.html