graphnetworkx

Graph layout disconnected subgraphs


I am using networkx to represent a large set of data. What I am trying to do should be relatively simple, but all answers I found online do not seem to be helping (or otherwise are often too old).

The graphs I am working with are often partially disconnected, meaning that given an initial starting node, there may be no way of reaching all other nodes.

It would then be helpful to plot the total graph such that disconnected "subgraphs" are not overlapping, but instead are visibly separated from eachother.

A simple example could be the graph described by the edges [0,2], [0,3], [1,4]. If I plotted this graph I would often times get the [1,4] edge overlapping with the others, which could make it difficult to tell that the 1 and 4 nodes are actually disconnected from the nodes 0,2,3 (this is especially true in the case where the number of nodes was much higher). It would then be useful to be able to plot this graph such that each disconnected subgraph was clearly distinguishable.

I also include a code that generates a random graph where this problem becomes apparent.

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx

dim = 100

np.random.seed(0)
nodes = np.arange(dim) # list of nodes IDs
edges = [np.random.choice(dim, size = 2) for _ in range(dim)] 

G = nx.Graph(edges) # initializing edges
G.add_nodes_from(nodes) # ensuring that nodes without edges are included


fig, ax = plt.subplots()

layout = nx.circular_layout(G)

a = nx.draw_networkx_edges(G, layout, edgelist = dict(G.edges).keys())
b = nx.draw_networkx_nodes(G, layout)
c = nx.draw_networkx_labels(G, layout)

When running the above code we get a plot from which it is impossible to tell whether the graph is formed by multiple disconnected subgraphs. I would then need a way to tell whether that was the case, and then plot accordingly. Is there any way to do this?


Solution

  • The exact expected output is unclear, but you could use a cluster layout after determining the connected_components:

    cc = list(nx.connected_components(G))
    
    G2 = nx.cycle_graph(len(cc))
    # assign a weigth inversely proportional to the subgraph size
    nx.set_node_attributes(G2, {i: 1/l for i,l in enumerate(map(len, cc))},
                           name='weight')
    superpos = nx.circular_layout(G2, scale=5)
    pos = {}
    
    for center, c in zip(superpos.values(), cc):
        pos.update(nx.spring_layout(nx.subgraph(G, c), center=center))
    
    nx.draw_networkx_nodes(G, pos=pos)
    nx.draw_networkx_edges(G, pos=pos)
    nx.draw_networkx_labels(G, pos=pos)
    

    Output:

    enter image description here

    You can inverse which of the circular_layout/spring_layout is used for the cluster/subgraphs:

    # ...
    superpos = nx.spring_layout(G2, scale=5)
    # ...
    for center, c in zip(superpos.values(), cc):
        pos.update(nx.circular_layout(nx.subgraph(G, c), center=center))
    # ...
    

    enter image description here

    Last but not least, I like to use graphviz (see the code here) in this case, this makes really nice graphs (although limited to a reasonable number of nodes):

    enter image description here