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:
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?
In any tree, there must be only one root node. We never have multiple root nodes in a tree.
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.
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.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With