Networkx: как указать несколько корней для построения нескольких деревьев одновременно? - PullRequest
6 голосов
/ 01 мая 2020

В networkx есть функция для построения дерева с использованием радиальной компоновки ("twopi"):

import pydot
from networkx.drawing.nx_pydot import graphviz_layout

pos = graphviz_layout(G, prog='twopi', root=root, args='')

Можно указать узел root с аргументом root (который добавлено в args под капотом как args += f" -Groot={root}").

Но как указать несколько корней, когда граф состоит из нескольких отсоединенных компонентов? Т.е. лес деревьев.

Я получаю следующий график без указания параметра root:

enter image description here

Как вы можете видеть визуально, хотя он правильно выбрал истинный root узел для 10 деревьев, для 12 он выбрал потомок истинного root узла в качестве центра (и, следовательно, некоторые ветви кажутся более мелкими, чем они на самом деле, относительно других отраслей).

Как вручную указать корни для нескольких деревьев?

1 Ответ

3 голосов
/ 01 мая 2020

Я не думаю, что есть способ сделать это на одном графике с макетом graphviz twopi . Twopi должен в целом хорошо выполнять настройку root узлов каждого подграфа, поскольку, как указано в документах , он случайным образом выберет один из узлов, наиболее удаленных от конечный узел как root, поэтому в случае наличия одного root узла это должно привести к ожидаемому топологическому расположению. Хотя, если это не так, и вы хотите установить вручную корни для каждого подграфа, я бы подошел к этому, перебирая графы, связанные с подграфами компонентов, и отображая каждый компонент в отдельности. ось на графике подзаговора, создавая для каждого из них пользовательский graphviz_layout.

Вот как это можно сделать, используя следующий пример графика:

from matplotlib import pyplot as plt
import pygraphviz
from networkx.drawing.nx_agraph import graphviz_layout

result_set = {('plant','tree'), ('tree','oak'), ('flower', 'rose'), ('flower','daisy'), ('plant','flower'), ('tree','pine'), ('plant','roots'), ('animal','fish'),('animal','bird'), ('bird','robin'), ('bird','falcon'), ('animal', 'homo'),('homo','homo-sapiens'), ('animal','reptile'), ('reptile','snake'),('fungi','mushroom'), ('fungi','mold'), ('fungi','toadstool'),('reptile','crocodile'), ('mushroom','Portabello'), ('mushroom','Shiitake'),('pine','roig'),('pine','pinyer'), ('tree','eucaliptus'),('rose','Floribunda'),('rose','grandiflora')}
G=nx.from_edgelist(result_set, create_using=nx.DiGraph)

чтобы перебрать существующие подграфы, мы должны создать копию текущего графа в виде неориентированного графа, если он еще не создан, и создать список подграфов, используя nx.connected_component_subgraphs:

UG = G.to_undirected()
subgraphs = list(nx.connected_component_subgraphs(UG))

Допустим, мы знаем, что мы хотим, чтобы узлы root различных компонентов были узлами 'plant', 'animal' и 'mushroom', теперь мы можем создать набор вспомогательных участков и выполнять итерации по соответствующим осям. вместе с объектами подграфа и списком корней (следя за тем, чтобы они были в одинаковом порядке), создавая новый макет для каждого подграфа, устанавливая соответствующие root узлы:

n_cols = 2
roots = ['plant','animal','mushroom']
fig, axes = plt.subplots(nrows=int(np.ceil(len(subgraphs)/n_cols)), 
                         ncols=n_cols, 
                         figsize=(15,10))
plt.box(False)

for subgraph, root, ax in zip(subgraphs, roots, axes.flatten()):
    pos = graphviz_layout(G,  prog='twopi', args=f"-Groot={root}")
    nx.draw(subgraph, pos=pos, with_labels=True, 
            node_color='lightblue', node_size=500, ax=ax)


enter image description here

...