pythonnetworkxgraphvizpydot

Can I horizontally align a pydot graph by distance from head?


I've created a graph as shown below. However, I'd like to arrange the graph hierarchically by shortest distance from the head (A). In other words: C, B, D, E should all be in the same level and horizontally aligned, since they're each all 1 edge (shortest path) away from A. Then, F, G, H should be in the next level since they're each 2 edges away, etc.

I like the way the graph looks, so the solution would ideally keep this visualization style.

enter image description here

import matplotlib.pyplot as plt
import networkx as nx
import pydot
from networkx.drawing.nx_pydot import graphviz_layout
from IPython.display import Image, display

G=nx.Graph()
G.add_edges_from([
    ('A','B'),
    ('A','C'),
    ('A','E'),
    ('A','D'),
    ('B','C'),
    ('B','F'),
    ('C','F'),
    ('D','H'),
    ('D','G'),
    ('E','H'),
    ('F','I'),
    ('G','I'),
    ('G','J'),
    ('H','J'),
    ('I','K'),
    ('J','K')
])


pdot = nx.drawing.nx_pydot.to_pydot(G)
graph = Image(pdot.create_png())
display(graph)

Solution

  • There is a built-in function that allows calculating the length of the shortest paths to a given node. The lengths can be used to specify one coordinate. The other coordinate is calculated based on the number of nodes that are at the same distance to A:

    from collections import Counter
    
    distances = list(nx.single_target_shortest_path_length(G, 'A'))
    
    counts = Counter(a[1] for a in distances)
    
    for c in counts:
        counts[c] /= 2 # divide by 2 to get symmetrical distances from the y-axis
    
    # generate dictionary that holds node positions:
    pos = {}
    for (node, d) in distances:
        pos[node] = (counts[d], -d) # x-position determined by number of counts, y position determined by distance. 
        counts[d] -= 1 # decrement count to draw nodes at shifted position
        
    nx.draw_networkx(G, pos=pos) # i dont have pydot, but the basic logic should work. just use the pos argument in the pydot function. 
    

    enter image description here