Scipy dendrogram для json для визуализации дерева d3.js

Я пытаюсь преобразовать результаты scipy иерархической кластеризации в json для отображения в d3.js здесь пример

В следующих кодах создается дендрограмма с 6 ветвями.

import pandas as pd 
import scipy.spatial
import scipy.cluster

d = {'employee' : ['A', 'B', 'C', 'D', 'E', 'F'],
 'skillX': [2,8,3,6,8,10],
 'skillY': [8,15,6,9,7,10]}

d1 = pd.DataFrame(d)

distMat = xPairWiseDist = scipy.spatial.distance.pdist(np.array(d1[['skillX', 'skillY']]), 'euclidean')
clusters = scipy.cluster.hierarchy.linkage(distMat, method='single')
dendo  = scipy.cluster.hierarchy.dendrogram(clusters, labels = list(d1.employee), orientation = 'right')

dendo

мой вопрос Как я могу представить данные в json файле в формате, который d3.js понимает

{'name': 'Root1’, 
      'children':[{'name' : 'B'},
                  {'name': 'E-D-F-C-A',
                           'children' : [{'name': 'C-A',
                                         'children' : {'name': 'A'}, 
                                                      {'name' : 'C'}]
                                                 }
                   }
                   ]
}

Смущающая правда заключается в том, что я не знаю, могу ли я извлечь эту информацию из дендограммы или из матрицы связей и , как

Я благодарен за любую помощь, которую я могу получить.

ИЗМЕНИТЬ, ЧТОБЫ ЗАКРЫТЬ

До сих пор я пытался использовать метод totree, но с трудом понимал его структуру (да, я читал документацию).

a = scipy.cluster.hierarchy.to_tree(clusters , rd=True)

for x in a[1]:
 #print x.get_id()
 if x.is_leaf() != True :
     print  x.get_left().get_id(), x.get_right().get_id(), x.get_count()

Ответ 1

Вы можете сделать это в три этапа:

  • Рекурсивно построить вложенный словарь, который представляет дерево, возвращаемое методом Scipy to_tree.
  • Перейдите через вложенный словарь, чтобы пометить каждый внутренний node листами в его поддереве.
  • dump полученный вложенный словарь в JSON и загрузить в d3.

Построить вложенный словарь, представляющий дендрограмму

Для первого шага важно вызвать to_tree с помощью rd=False, чтобы возвращался корень дендрограммы. Из этого корня вы можете построить вложенный словарь следующим образом:

# Create a nested dictionary from the ClusterNode returned by SciPy
def add_node(node, parent ):
    # First create the new node and append it to its parent children
    newNode = dict( node_id=node.id, children=[] )
    parent["children"].append( newNode )

    # Recursively add the current node children
    if node.left: add_node( node.left, newNode )
    if node.right: add_node( node.right, newNode )

T = scipy.cluster.hierarchy.to_tree( clusters , rd=False )
d3Dendro = dict(children=[], name="Root1")
add_node( T, d3Dendro )
# Output: => {'name': 'Root1', 'children': [{'node_id': 10, 'children': [{'node_id': 1, 'children': []}, {'node_id': 9, 'children': [{'node_id': 6, 'children': [{'node_id': 0, 'children': []}, {'node_id': 2, 'children': []}]}, {'node_id': 8, 'children': [{'node_id': 5, 'children': []}, {'node_id': 7, 'children': [{'node_id': 3, 'children': []}, {'node_id': 4, 'children': []}]}]}]}]}]}

Основная идея - начать с node не в дендрограмме, которая будет служить корнем всей дендрограммы. Затем мы рекурсивно добавляем левого и правого детей в этот словарь, пока не дойдем до листьев. На данный момент у нас нет меток для узлов, поэтому я просто помещаю узлы их идентификатором clusterNode.

Обозначьте дендрограмму

Далее, нам нужно использовать node_ids для обозначения дендрограммы. Комментарии должны быть достаточными для объяснения того, как это работает.

# Label each node with the names of each leaf in its subtree
def label_tree( n ):
    # If the node is a leaf, then we have its name
    if len(n["children"]) == 0:
        leafNames = [ id2name[n["node_id"]] ]

    # If not, flatten all the leaves in the node subtree
    else:
        leafNames = reduce(lambda ls, c: ls + label_tree(c), n["children"], [])

    # Delete the node id since we don't need it anymore and
    # it makes for cleaner JSON
    del n["node_id"]

    # Labeling convention: "-"-separated leaf names
    n["name"] = name = "-".join(sorted(map(str, leafNames)))

    return leafNames

label_tree( d3Dendro["children"][0] )

Дамп для JSON и загрузка в D3

Наконец, после того, как дендрограмма была помечена, нам просто нужно вывести ее в JSON и загрузить в D3. Я просто вставляю код Python, чтобы свалить его на JSON здесь для полноты.

# Output to JSON
json.dump(d3Dendro, open("d3-dendrogram.json", "w"), sort_keys=True, indent=4)

Выход

Я создал версии Dendrogram из Scipy и D3 ниже. Для версии D3 я просто подключил файл JSON, который я выводил ('d3-dendrogram.json'), в этот Gist.

SciPy dendrogram

The dendrogram output by SciPy.

D3 dendrogram

The dendrogram output by d3