【问题标题】:Scipy hierarchical clustering appropriate linkage methodscipy层次聚类合适的联动方法
【发布时间】:2021-08-28 06:46:13
【问题描述】:

抱歉,因为我昨天问了一个类似的问题,但我觉得我的问题缺乏内容,希望现在它会更容易理解。

我有一个具有个体之间成对距离的对称矩阵(见下文),并且我想以集群的 所有 成员的成对距离为零的方式对个体群体进行聚类。我为此使用了不同的链接方法和聚类标准应用了 scipy.cluster.hierarchy,但我没有得到预期的结果。在下面的示例中,我认为 ind5 不应该是集群 #1 的一部分,因为它与 ind9 的距离是 1 而不是 0。

from scipy.cluster.hierarchy import linkage, fcluster
from scipy.spatial.distance import squareform
import numpy as np
import pandas as pd

df = pd.read_csv(infile1, sep = '\t', index_col = 0)
print(df)

      ind1  ind2  ind3  ind4  ind5  ind6  ind7  ind8  ind9
ind1     0    29    27     1     2     1     2     1     1
ind2    29     0     2    30    31    29    31    30    30
ind3    27     2     0    28    29    27    29    28    28
ind4     1    30    28     0     0     0     1     2     0
ind5     2    31    29     0     0     0     2     2     1
ind6     1    29    27     0     0     0     1     2     0
ind7     2    31    29     1     2     1     0     3     1
ind8     1    30    28     2     2     2     3     0     2
ind9     1    30    28     0     1     0     1     2     0

X = squareform(df.to_numpy())
print(X)

[29 27  1  2  1  2  1  1  2 30 31 29 31 30 30 28 29 27 29 28 28  0  0  1
  2  0  0  2  2  1  1  2  0  3  1  2]

Z = linkage(X, 'single')
print(Z)

[[ 3.  4.  0.  2.]
 [ 5.  9.  0.  3.]
 [ 8. 10.  0.  4.]
 [ 0. 11.  1.  5.]
 [ 6. 12.  1.  6.]
 [ 7. 13.  1.  7.]
 [ 1.  2.  2.  2.]
 [14. 15. 27.  9.]]

max_d = 0
clusters = fcluster(Z, max_d, criterion='distance')
sample_list = df.index.to_list()
clust_name_list = clusters.tolist()
result = pd.DataFrame({'Inds': sample_list, 'Clusters': clust_name_list})
print(result)

   Inds  Clusters
0  ind1         2
1  ind2         5
2  ind3         6
3  ind4         1
4  ind5         1
5  ind6         1
6  ind7         3
7  ind8         4
8  ind9         1

我希望任何更熟悉这些方法的人都可以建议是否有任何链接方法可以从集群中排除与集群中至少一个其他元素的距离 > 0 的任何元素(在本例中为 ind5) .

感谢您的帮助!

贡萨洛

【问题讨论】:

    标签: python pandas numpy scipy hierarchical-clustering


    【解决方案1】:

    你的解决方案是正确的!

    您将获得以下集群:

    • 集群 1 包含元素 ind4ind5ind6ind9(距离为 0)。
    • 集群 2 与元素 ind1
    • 集群 3 与元素 ind7
    • 集群 4 与元素 ind8
    • 集群 5 与元素 ind2
    • 集群 6 与元素 ind3

    根据您的需要,只有距离为 0 的元素会聚集在集群 1 中。簇 2 到 6 是退化簇,具有单个孤立元素。

    让我们修改距离以便创建更合适的集群:

    X = np.array([ 0, 27,  1,  2,  1,  2,  1,  1,
                       2, 30, 31, 29, 31, 30, 30,
                          28, 29, 27, 29, 28, 28,
                               0,  0,  1,  2,  0,
                                   0,  2,  2,  1,
                                       1,  2,  0,
                                           0,  1,
                                               2])
    Z = linkage(X, 'single')
    max_d = 0
    clusters = fcluster(Z, max_d, criterion='distance')
    print("Clusters:", clusters)
    for cluster_id in np.unique(clusters):
        members = np.where(clusters == cluster_id)[0]
        print(f"Cluster {cluster_id} has members {members}")
    

    获取:

    Clusters: [2 2 4 3 3 3 1 1 3]
    Cluster 1 has members [6 7]
    Cluster 2 has members [0 1]
    Cluster 3 has members [3 4 5 8]
    Cluster 4 has members [2]
    

    【讨论】:

      【解决方案2】:

      您可以将您的问题重新解释为在graph 中找到cliques 的问题。该图是通过将距离 0 解释为在两个节点之间创建边而从距离矩阵中获得的。获得图表后,您可以使用networkx(或其他一些图论库)在图表中查找派系。图中的团将是其中所有团中成对距离均为 0 的节点集。

      这是您的距离矩阵(但请注意,您的距离不满足三角不等式):

      In [136]: D
      Out[136]: 
      array([[ 0, 29, 27,  1,  2,  1,  2,  1,  1],
             [29,  0,  2, 30, 31, 29, 31, 30, 30],
             [27,  2,  0, 28, 29, 27, 29, 28, 28],
             [ 1, 30, 28,  0,  0,  0,  1,  2,  0],
             [ 2, 31, 29,  0,  0,  0,  2,  2,  1],
             [ 1, 29, 27,  0,  0,  0,  1,  2,  0],
             [ 2, 31, 29,  1,  2,  1,  0,  3,  1],
             [ 1, 30, 28,  2,  2,  2,  3,  0,  2],
             [ 1, 30, 28,  0,  1,  0,  1,  2,  0]])
      

      将距离矩阵转换为邻接矩阵A

      In [137]: A = D == 0
      
      In [138]: A.astype(int)  # Display as integers for a more compact output.
      Out[138]: 
      array([[1, 0, 0, 0, 0, 0, 0, 0, 0],
             [0, 1, 0, 0, 0, 0, 0, 0, 0],
             [0, 0, 1, 0, 0, 0, 0, 0, 0],
             [0, 0, 0, 1, 1, 1, 0, 0, 1],
             [0, 0, 0, 1, 1, 1, 0, 0, 0],
             [0, 0, 0, 1, 1, 1, 0, 0, 1],
             [0, 0, 0, 0, 0, 0, 1, 0, 0],
             [0, 0, 0, 0, 0, 0, 0, 1, 0],
             [0, 0, 0, 1, 0, 1, 0, 0, 1]])
      

      创建一个networkx图G,并找到nx.find_cliques的派系:

      In [139]: import networkx as nx
      
      In [140]: G = nx.Graph(A)
      
      In [141]: cliques = nx.find_cliques(G)
      
      In [142]: list(cliques)
      Out[142]: [[0], [1], [2], [3, 5, 8], [3, 5, 4], [6], [7]]
      

      (列表中的值是索引;例如,集团[2] 对应于标签集['ind3']。)

      请注意,有两个非平凡的派系,[3,5,8] 和 [3,5,4],并且 3 和 5 出现在两者中。这是您的距离具有此异常数据的结果:距离(ind5,ind4)= 0,距离(ind4,ind9)= 0,但距离(ind5,ind9)= 1(即不满足triangle inequality) .因此,根据您对“集群”的定义,有两个可能的非平凡集群:[ind4, ind5, ind9] 或 [ind4, ind5, ind6]。

      最后,请注意networkx documentation 中的警告:“在图中找到最大的团是 NP 完全问题,因此这些算法中的大多数都有指数运行时间”。如果你的距离矩阵很大,这个计算可能需要很长时间!

      【讨论】:

      • 嗨,Warren,这太好了,非常感谢您抽出宝贵时间来回答。事实上,三角不等式不满足这一事实使手头的任务变得复杂——我现在看到它可以产生不止一个正确答案。然而,使用图表似乎是前进的道路。很有帮助的东西,再次感谢!贡萨洛
      猜你喜欢
      • 1970-01-01
      • 2012-04-10
      • 2021-07-02
      • 2018-08-17
      • 2019-04-27
      • 2021-01-15
      • 2017-07-04
      • 1970-01-01
      • 2020-08-27
      相关资源
      最近更新 更多