如何手动交换Scipy树状图指定分裂的两个分支?
可行!手动交换树状图分支的实现方法
完全可以实现你的需求——只调整树状图分支的左右顺序,不改动任何聚类数据或距离计算结果。核心思路是直接修改scipy生成的链接矩阵Z,因为树状图的分支顺序完全由这个矩阵的结构决定。
先搞懂链接矩阵Z的结构
linkage()生成的Z是一个(n-1)×4的矩阵,每一行代表一次合并操作:
- 第1、2列:被合并的两个簇/样本的索引(样本索引从0开始,簇的索引从n开始)
- 第3列:两个簇之间的距离
- 第4列:合并后簇包含的样本数量
你要做的就是找到对应分裂步骤的那一行,交换第1、2列的值——这样树状图绘制时就会把原来的左分支放到右边,右分支放到左边,完全不影响距离和聚类逻辑。
具体操作步骤
定位目标分裂行
先运行你原有的代码,观察树状图:你提到的标签11和25所在分支是从大簇中分离出来的,对应的就是Z中最后几次合并的某一行(因为层次聚类是从下往上合并)。
或者你可以通过dendrogram()的返回值来精准定位:# 先获取树状图的结构信息,不绘图 dend = dendrogram(Z, no_plot=True) # 查看叶子节点的顺序,反向推导对应的合并行 print(dend['leaves'])修改链接矩阵Z
假设你定位到目标合并行是第k行(索引从0开始),直接交换该行的前两个元素:# 比如找到目标行是Z的最后一行(对应最大簇的分裂) target_row = len(Z) - 1 # 交换左右分支索引 Z[target_row, 0], Z[target_row, 1] = Z[target_row, 1], Z[target_row, 0]重新绘制树状图
修改后的Z可以直接用来绘制树状图,此时你想要的分支就会出现在右侧了。
完整修改后的代码示例
import numpy as np import matplotlib.pyplot as plt from scipy.cluster.hierarchy import dendrogram, linkage # 生成数据集 np.random.seed(65) a = np.random.multivariate_normal([10, 0], [[3, 1], [1, 4]], size=[10,]) b = np.random.multivariate_normal([0, 20], [[3, 1], [1, 4]], size=[20,]) X = np.concatenate((a, b),) # 创建聚类链接 Z = linkage(X, 'ward') # 定位并交换目标分支(这里直接选取最后一行,对应最大簇的分裂) target_row = len(Z) - 1 Z[target_row, 0], Z[target_row, 1] = Z[target_row, 1], Z[target_row, 0] # 绘制调整后的树状图 plt.figure(figsize=(15, 5)) plt.title('Adjusted Hierarchical Clustering Dendrogram') plt.xlabel('sample index') plt.ylabel('distance') dendrogram( Z, leaf_rotation=90., leaf_font_size=12., ) plt.show()
注意事项
- 这种修改仅影响可视化效果,不会改变聚类的距离、簇的组成等核心数据。
- 如果需要调整更底层的分支,只需要找到对应合并行重复交换操作即可。
- 如果你不确定目标行,可以多打印Z的内容或者结合
dendrogram()返回的icoord(分支坐标)信息来辅助定位。
内容的提问来源于stack exchange,提问作者dmeu




