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)
