You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何手动交换Scipy树状图指定分裂的两个分支?

可行!手动交换树状图分支的实现方法

完全可以实现你的需求——只调整树状图分支的左右顺序,不改动任何聚类数据或距离计算结果。核心思路是直接修改scipy生成的链接矩阵Z,因为树状图的分支顺序完全由这个矩阵的结构决定。

先搞懂链接矩阵Z的结构

linkage()生成的Z是一个(n-1)×4的矩阵,每一行代表一次合并操作:

  • 第1、2列:被合并的两个簇/样本的索引(样本索引从0开始,簇的索引从n开始)
  • 第3列:两个簇之间的距离
  • 第4列:合并后簇包含的样本数量

你要做的就是找到对应分裂步骤的那一行,交换第1、2列的值——这样树状图绘制时就会把原来的左分支放到右边,右分支放到左边,完全不影响距离和聚类逻辑。

具体操作步骤

  1. 定位目标分裂行
    先运行你原有的代码,观察树状图:你提到的标签11和25所在分支是从大簇中分离出来的,对应的就是Z中最后几次合并的某一行(因为层次聚类是从下往上合并)。
    或者你可以通过dendrogram()的返回值来精准定位:

    # 先获取树状图的结构信息,不绘图
    dend = dendrogram(Z, no_plot=True)
    # 查看叶子节点的顺序,反向推导对应的合并行
    print(dend['leaves'])
    
  2. 修改链接矩阵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]
    
  3. 重新绘制树状图
    修改后的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

火山引擎 最新活动