Scikit HDBSCAN *tree* 标签(不是单片标签)

Scikit HDBSCAN *tree* labeling (not single-slice labeling)

BLUF:对于特定的 epsilon(或 HDBSCAN 的 'favorite' epsilon),我可以提取该 epsilon 分区中的数据映射。但是如何查看我的数据的完整树成员资格?

我从很棒的教程中学到了很多东西 here。在 scikit learn 的 HDBSCAN 中,我可以使用 clusterer.labels 来查看最佳 epsilon 的分区标签。我可以使用 clusterer.single_linkage_tree_.get_clusters(0.023, min_cluster_size=2) 查看任意 epsilon 的分区标签。我什至可以使用 clusterer.condensed_tree_.plot() 绘制整个树状图。但是如何查看单个数据点的树状图标签?

例如:很高兴我的宠物的名字是 {Spot、Felix、Nemo、Fido、Tigger}。或者物种是 {Dog, Cat, Guppy, Dog, Cat}。但我想要一个输出告诉我:

Spot Dog Mammal Animal
Felix Cat Mammal Animal
Nemo Guppy Fish Animal
Fido Dog Mammal Animal
Tigger Cat Mammal Animal

使用 this 这样的输出,我可以准确地看到 how 相关的 Spot 和 Felix,而不是“他们有相同的东西吗物种?Y/N?” “他们有同一个王国吗?Y/N?”

clusterer.condensed_tree_ object has a number of conversion utilities, e.g. to_pandas() and to_networkx(). For this particular use case, it looks like you want to print an ancestor list for each leaf node in the condensed tree. You can accomplish this in many ways, but a pretty straightforward one is to convert the tree to a networkx 图并使用其上的实用方法提取您要查找的结构:

import hdbscan
import networkx as nx
import numpy as np

# run HDBSCAN
data = np.load('clusterable_data.npy')
clusterer = hdbscan.HDBSCAN(min_cluster_size=15).fit(data)

# convert tree to networkx graph
tree = clusterer.condensed_tree_.to_networkx()
assert nx.algorithms.tree.recognition.is_tree(tree)

# find the root by picking an arbitrary node and walking up
root = 0
while True:
    try:
        root = next(tree.predecessors(root))
    except StopIteration:
        break

# create the ancestor list for each data point
all_ancestors = []
for leaf_node in range(len(data)):
    ancestors = nx.shortest_path(tree, source=root, target=leaf_node)[::-1]
    all_ancestors.append(ancestors)

打印 all_ancestors 会给你这样的东西:

[[0, 2324, 2319, 2317, 2312, 2311, 2309],
 [1, 2319, 2317, 2312, 2311, 2309],
 [2, 2319, 2317, 2312, 2311, 2309],
 [3, 2333, 2324, 2319, 2317, 2312, 2311, 2309],
 [4, 2324, 2319, 2317, 2312, 2311, 2309],
 [5, 2334, 2332, 2324, 2319, 2317, 2312, 2311, 2309],
 ...
 [995, 2309],
 [996, 2318, 2317, 2312, 2311, 2309],
 [997, 2318, 2317, 2312, 2311, 2309],
 [998, 2318, 2317, 2312, 2311, 2309],
 [999, 2318, 2317, 2312, 2311, 2309],
 ...]

每个列表中的第一个条目是节点 ID(对应于 data 数组中节点的索引),第二个条目是该节点的父节点,依此类推直到根(在本例中,其 ID 为 2309)。请注意,任何大于您拥有的数据项数量的节点 ID 都是“簇节点”(即树的内部节点),任何较低的节点 ID 都是“数据点节点”(即树中的叶节点)树)。

通过将节点排序到它们的集群中,可能更容易理解这种列表格式,例如有:

all_ancestors.sort(key=lambda path: path[1:])

现在打印 all_ancestors 会给你这样的东西:

[[21, 2309],
 [126, 2309],
 [152, 2309],
 [155, 2309],
 [156, 2309],
 [172, 2309],
 ...
 [1912, 2313, 2311, 2309],
 [1982, 2313, 2311, 2309],
 [2014, 2313, 2311, 2309],
 [2028, 2313, 2311, 2309],
 [2071, 2313, 2311, 2309],
 ...
 [1577, 2337, 2314, 2310, 2309],
 [1585, 2337, 2314, 2310, 2309],
 [1591, 2337, 2314, 2310, 2309],
 [1910, 2337, 2314, 2310, 2309],
 [2188, 2337, 2314, 2310, 2309]]

有很多等效的方法可以得到相同的结果(例如,通过遍历 to_pandas() 生成的 pandas 数据帧),但是 networkx 是几乎任何事情的自然选择你可能想用 trees/DAGs/graphs.