Scipy 链接格式

58

我编写了自己的聚类程序,想要生成一个树状图。最简单的方法是使用scipy的dendrogram函数。然而,这需要输入数据与scipy的linkage函数产生的格式相同。我找不到关于此输出格式的示例。我想知道是否有人能为我解答。

5个回答

52
我同意 https://stackoverflow.com/users/1167475/mortonjt 的看法,即文档未完全解释中间聚类的索引,但我同意https://stackoverflow.com/users/1354844/dkar指出该格式其余部分已被准确解释。
使用此问题中的示例数据:scipy.cluster.hierarchy教程
A = np.array([[0.1,   2.5],
              [1.5,   .4 ],
              [0.3,   1  ],
              [1  ,   .8 ],
              [0.5,   0  ],
              [0  ,   0.5],
              [0.5,   0.5],
              [2.7,   2  ],
              [2.2,   3.1],
              [3  ,   2  ],
              [3.2,   1.3]])

可以使用单一的(即最接近的匹配点)构建连锁矩阵:

z = hac.linkage(a, method="single")

 array([[  7.        ,   9.        ,   0.3       ,   2.        ],
        [  4.        ,   6.        ,   0.5       ,   2.        ],
        [  5.        ,  12.        ,   0.5       ,   3.        ],
        [  2.        ,  13.        ,   0.53851648,   4.        ],
        [  3.        ,  14.        ,   0.58309519,   5.        ],
        [  1.        ,  15.        ,   0.64031242,   6.        ],
        [ 10.        ,  11.        ,   0.72801099,   3.        ],
        [  8.        ,  17.        ,   1.2083046 ,   4.        ],
        [  0.        ,  16.        ,   1.5132746 ,   7.        ],
        [ 18.        ,  19.        ,   1.92353841,  11.        ]])

文档解释如下:小于n(此处为11)的群集就是原始矩阵A中的数据点。随后形成的中间群集将被逐个索引。

因此,第一个合并的7和9号群集合并为第11号群集,4号和6号群集合并为第12号群集。然后观察第三行,将A中的5号群集和未显示的中间群集12号合并,结果得到群内距离(WCD)为0.5。单一方法意味着新的WCS为0.5,即A[5]与群集12中最近点A[4]和A[6]之间的距离。让我们检查一下:

 In [198]: norm([a[5]-a[4]])
 Out[198]: 0.70710678118654757
 In [199]: norm([a[5]-a[6]])
 Out[199]: 0.5

这个聚类现在应该是中间聚类13,随后与A[2]合并。因此,新的距离应该是点A[2]和A[4,5,6]之间最接近的距离。

 In [200]: norm([a[2]-a[4]])
 Out[200]: 1.019803902718557
 In [201]: norm([a[2]-a[5]])
 Out[201]: 0.58309518948452999
 In [202]: norm([a[2]-a[6]])
 Out[202]: 0.53851648071345048

正如可以看到的那样,这也验证了并解释了新集群的中间格式。


2
这是一个非常简洁明了的答案,完全清晰地解释了scipy文档的含义! - codeananda
应该将其添加到文档中。文档编写者应该明白,每个函数的文档中都应该提供一个详细的、逐步的和全面的示例! - sol
讲得非常清楚,谢谢!矩阵终于让我明白了! - vk1011

42
这是来自scipy.cluster.hierarchy.linkage()函数文档的描述,我认为这是输出格式的相当清晰的说明:

返回一个(n-1)行4列的矩阵Z。在第i次迭代中,将索引为Z[i,0]和Z[i,1]的聚类组合形成聚类n+i。索引小于n的聚类对应于原始观测值之一。聚类Z[i,0]和Z[i,1]之间的距离由Z[i,2]给出。第四个值Z[i,3]表示新形成的聚类中原始观测值的数量。

您需要更多的信息吗?


25
是的,更多的信息将会非常有帮助。例如,如果我要枚举所有的索引,它的遍历方式是什么?节点的标签是如何确定的?请提供一个清晰而精确的例子,并逐步介绍它的格式、树形结构以及与每个节点相对应的标签。 - mortonjt

13

正如dkar所指出的那样,scipy文档是准确的...但将返回的数据转化为可用于进一步分析的数据有些困难。

在我看来,他们应该包含将数据以树形数据结构返回的能力。下面的代码将遍历矩阵并构建一棵树:

from scipy.cluster.hierarchy import linkage
import numpy as np

a = np.random.multivariate_normal([10, 0], [[3, 1], [1, 4]], size=[100,])
b = np.random.multivariate_normal([0, 20], [[3, 1], [1, 4]], size=[50,])
centers = np.concatenate((a, b),)

def create_tree(centers):
    clusters = {}
    to_merge = linkage(centers, method='single')
    for i, merge in enumerate(to_merge):
        if merge[0] <= len(to_merge):
            # if it is an original point read it from the centers array
            a = centers[int(merge[0]) - 1]
        else:
            # other wise read the cluster that has been created
            a = clusters[int(merge[0])]

        if merge[1] <= len(to_merge):
            b = centers[int(merge[1]) - 1]
        else:
            b = clusters[int(merge[1])]
        # the clusters are 1-indexed by scipy
        clusters[1 + i + len(to_merge)] = {
            'children' : [a, b]
        }
        # ^ you could optionally store other info here (e.g distances)
    return clusters

print create_tree(centers)

0

输入
输出

考虑 [输入] 是您感兴趣绘制树状图的数据
当您使用联接返回一个具有四列的矩阵时

第一列和第二列-表示按顺序形成的聚类

例如 2 和 3 首先组成一个聚类,该聚类命名为 5
(2 和 3 表示索引,即第 2 行和第 3 行) 1 和 5 是第二个形成的聚类,该聚类命名为 6

第三列-表示聚类之间的距离

第四列-表示参与形成此聚类的数据点数

树状图


0
这里有另一段实现相同功能的代码。此版本跟踪每个聚类(node_id)的距离(大小),并确认成员数量。
这使用了scipy linkage()函数,该函数是Aggregator聚类器的基础。
from scipy.cluster.hierarchy import linkage
import copy
Z = linkage(data_x, 'ward')

n_points = data_x.shape[0]
clusters = [dict(node_id=i, left=i, right=i, members=[i], distance=0, log_distance=0, n_members=1) for i in range(n_points)]
for z_i in range(Z.shape[0]):
    row = Z[z_i]
    cluster = dict(node_id=z_i + n_points, left=int(row[0]), right=int(row[1]), members=[], log_distance=np.log(row[2]), distance=row[2], n_members=int(row[3]))
    cluster["members"].extend(copy.deepcopy(members[cluster["left"]]))
    cluster["members"].extend(copy.deepcopy(members[cluster["right"]]))
    clusters.append(cluster)

on_split = {c["node_id"]: [c["left"], c["right"]] for c in clusters}
up_merge = {c["left"]: {"into": c["node_id"], "with": c["right"]} for c in clusters}
up_merge.update({c["right"]: {"into": c["node_id"], "with": c["left"]} for c in clusters})

在for循环中,members指的是什么?它是未定义的。 - Blake Regalia
我已经很久没审查这段代码了。成员应该参考设计为在集群中的索引。如果成员不存在,你可能将模型配置为创建了不必要的更多的集群?最坏的情况是,也许你可以简单地将成员设置为空集合,如果键不存在的话?对不起我帮不上更多忙。 - David Bernat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接