Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Networkx: how to specify multiple roots for plotting multiple trees at once?

In networkx there is a function to plot a tree using a radial layout (graphviz's "twopi"):

import pydot
from networkx.drawing.nx_pydot import graphviz_layout

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

One can specify the root node with the argument root (which is added to args under the hood as args += f" -Groot={root}").

But how do you specify multiple roots when the graph consists of multiple disconnected components? I.e. a forest of trees.

I get the following plot without providing the root parameter:

enter image description here

As you can see visually, while it has correctly chosen the true root node for 10 of the trees, for 12 it has picked a child of the true root node as the centre (and hence some branches seem shallower than they actually are, relative to other branches).

How do you manually specify the roots for multiple trees?

like image 1000
iacob Avatar asked May 01 '20 16:05

iacob


People also ask

Can a tree have multiple root nodes?

In any tree, there must be only one root node. We never have multiple root nodes in a tree.

How many nodes can NetworkX handle?

For NetworkX, a graph with more than 100K nodes may be too large. I'll demonstrate that it can handle a network with 187K nodes in this post, but the centrality calculations were prolonged. Luckily, there are some other packages available to help us with even larger graphs.

What is spring layout NetworkX?

By default, the layout of the nodes and edges is automatically determined by the Fruchterman-Reingold force-directed algorithm [62] (called “spring layout” in NetworkX), which conducts a pseudo-physics simulation of the movements of the nodes, assuming that each edge is a spring with a fixed equilibrium distance.

How do you draw a directed graph using NetworkX in Python?

1. How to draw directed graphs using NetworkX in Python? ​ By using the base class for directed graphs, DiGraph(), we are able to draw a directed graph with arrows to indicate the direction of edges.


1 Answers

I don't think there is a way to do this in a single graph with graphviz's twopi layout. Twopi should do a good job in general in setting the root nodes of each subgraph, since as mentioned in the docs it will randomly pick one of the nodes that are furthest from a leaf node as root, so in the case of having a single root node, this should lead to the expected topological arrangement. Though if that is not the case, and you want to set manually the roots for each subgraph, the way I'd approach this is by iterating over the graphs connected component subgraphs, and plotting each component into an separate axis in a subplot graph, creating a custom graphviz_layout for each.

Here's how this could be done using the following example graph:

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)

In order to iterate over the existing subgraphs we must create a copy of the current graph as an undirected graph if it isn't one already, and create a list of subgraphs using nx.connected_component_subgraphs:

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

Let's say we know that we want the root nodes of the different components to be the nodes 'plant', 'animal' and 'mushroom', we can now create a set of subplots, and iterate over the respective axes, along with the subgraph objects and the list of roots (making sure they are in the same order), creating a new layout for each subgraph setting the corresponding root nodes:

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

like image 177
yatu Avatar answered Oct 09 '22 01:10

yatu