如何在Python中基于预计算距离实现层次凝聚聚类
基于预计算距离实现层次凝聚聚类方案
核心实现逻辑
不管是scipy还是sklearn的层次聚类接口,都支持直接输入预计算的距离,不需要原始特征数据,你查到的需要原始数据的用法是默认用内置距离指标的场景。
步骤1:将成对距离数据转换为标准输入格式
你现有存储为字典列表的成对距离,需要先转换为两种格式之一:
- 紧凑距离矩阵(Condensed Distance Matrix):长度为
n*(n-1)/2的一维数组,存储所有i<j的样本对距离,为scipy的linkage接口标准输入 - 方阵形式距离矩阵:
n*n的二维数组,dist[i][j]为样本i和样本j的距离,对角线为0,为sklearn的预计算距离场景标准输入
代码示例:
import numpy as np from scipy.cluster.hierarchy import linkage, dendrogram from sklearn.cluster import AgglomerativeClustering # 你的原始预计算距离数据 data = [ {'id1': 1, 'id2': 2, 'dist': 3}, {'id1': 1, 'id2': 3, 'dist': 2}, {'id1': 2, 'id2': 3, 'dist': 4} ] # 映射原始ID到连续索引(方便矩阵对齐) all_ids = sorted({d['id1'] for d in data} | {d['id2'] for d in data}) id_to_idx = {id_val: idx for idx, id_val in enumerate(all_ids)} n_samples = len(all_ids) # 生成方阵形式距离矩阵 square_dist = np.zeros((n_samples, n_samples)) for item in data: i = id_to_idx[item['id1']] j = id_to_idx[item['id2']] square_dist[i][j] = square_dist[j][i] = item['dist'] # 如需生成紧凑距离矩阵,直接调用scipy工具方法即可 from scipy.spatial.distance import squareform condensed_dist = squareform(square_dist)
步骤2:执行层次聚类(和R的hclust效果等价)
用scipy的linkage接口可以输出和Rhclust()完全一致的聚类树合并矩阵,还支持直接绘制树状图:
# 全连接(complete linkage)聚类,对应R hclust(method='complete') merge_matrix = linkage(condensed_dist, method='complete') # 可选:绘制聚类树状图 import matplotlib.pyplot as plt plt.figure(figsize=(8,4)) dendrogram(merge_matrix, labels=all_ids) plt.show()
如果需要直接得到指定簇数的分类标签,可以用sklearn的接口,注意指定metric='precomputed':
# 示例:聚为2类,使用全连接 clust = AgglomerativeClustering( n_clusters=2, metric='precomputed', linkage='complete' ) labels = clust.fit_predict(square_dist) # 标签映射回原始ID id_to_label = {all_ids[i]: labels[i] for i in range(n_samples)}
可选的链接方法说明
除了要求的全连接,常用链接方法还有:
single:单连接,取两个簇之间所有样本对的最小距离average:平均连接,取两个簇之间所有样本对距离的平均值centroid:质心连接,取两个簇的质心之间的距离ward:沃德方差最小化,仅支持欧氏距离,非欧氏预计算距离不可用median:中位连接,为质心连接的变体,降低大簇对距离计算的影响
补充:未提前预计算全量距离的处理方式
如果你还没有计算完全部成对距离,只需要把自定义的距离函数传给pdist接口,会自动完成全量距离计算:
from scipy.spatial.distance import pdist # objs为所有待聚类的自定义类对象的列表 condensed_dist = pdist(objs, metric=objectclass.calculate_distance) # 后续直接用linkage聚类即可
内容的提问来源于stack exchange,提问作者Max Candocia
相关产品推荐
相关产品推荐

