Search code examples
pythongraphnetworkxbipartite

Draw edges differentially between two node sets


The goal is to draw bijective graphs with dashed connections between G1 and G2.

goal

I have two node sets g1 and g2.

g1.nodes = NodeView((0, 1, 2))
g2.nodes = NodeView(('a', 'b', 'c', 'd', 'e'))

They are connected by edges:

g1.edges = EdgeView([(0, 1), (0, 2), (1, 2)])
g2.edges = EdgeView([('a', 'b'), ('a', 'c'), ('a', 'e'), ('b', 'c'), ('b', 'd'), ('b', 'e'), ('c', 'd'), ('c', 'e')])

Here is code to get to the above step:

import string
import networkx as nx
import numpy as np

g1, g2 = nx.Graph(), nx.Graph()

g1.add_nodes_from([0, 1, 2])

tmp = [i for i in range(0, 5)]
tmp = np.array(list(string.ascii_lowercase))[tmp]

g2.add_nodes_from(tmp)

g1.add_edges_from([(0, 1), (0, 2), (1, 2)])

g2.add_edges_from([('a', 'b'), ('a', 'c'), ('a', 'e'), ('b', 'c'), ('b', 'd'), ('b', 'e'), ('c', 'd'), ('c', 'e')])

We can visualize:

figure(figsize = (10, 10))
G = nx.grid_2d_graph(4, 4)
pos = nx.spring_layout(G, iterations=100, seed=39775)


plt.subplot(221)
draw(g1, with_labels = True)

plt.subplot(222)
draw(g2, with_labels = True

pre-bijective

I want to draw dashed edges between selected u in G1 to selected v in G2, while keeping the bipartite layout and connections of respective G1 and G2.

Here is my attempt so far:

# create new graph H
H = nx.Graph()
# add existing nodes and edges
H.add_nodes_from(g1.nodes, bipartite = 0)
H.add_nodes_from(g2.nodes, bipartite = 1)

new = [(0, 'd'), (1, 'c'), (2, 'b')]
H.add_edges_from(new, style = 'dashed')
draw_networkx(H, pos = nx.drawing.layout.bipartite_layout(H, g1.nodes), width = 2, style = 'dashed')

initial_bijective

Keeping G1 and G2 structures, similar to the first set of plots above, is the issue. Adding pre-existing edges produces the following unexpecting output.

H.add_edges_from(g1.edges)
H.add_edges_from(g2.edges)
draw_networkx(H, pos = nx.drawing.layout.bipartite_layout(H, g1.nodes), width = 2, style = 'dashed')

unexpecting

What I want is the dashed lines between [(0, 'd'), (1, 'c'), (2, 'b')], while keeping the G1 and G2 bipartite layout, and respective edge structures, from the first image.


Solution

  • You need to retrieve the pos dict for each graph and then can move it freely around (shifting the coordinates). Then you can easily add the dashed edges:

    import string
    import networkx as nx
    import numpy as np
    import matplotlib.pylab as plt
    
    g1, g2 = nx.Graph(), nx.Graph()
    
    g1.add_nodes_from([0, 1, 2])
    
    tmp = [i for i in range(0, 5)]
    tmp = np.array(list(string.ascii_lowercase))[tmp]
    
    g2.add_nodes_from(tmp)
    
    g1.add_edges_from([(0, 1), (0, 2), (1, 2)])
    
    g2.add_edges_from([('a', 'b'), ('a', 'c'), ('a', 'e'), ('b', 'c'), ('b', 'd'), ('b', 'e'), ('c', 'd'), ('c', 'e')])
    
    pos_1 = nx.spring_layout(g1)
    pos_2 = nx.spring_layout(g2)
    
    # shift pos_2
    print(pos_2)
    for node in g2:
        pos_2[node][0] += 3
    
    # draw original graphs
    nx.draw(g1, pos_1, with_labels=True)
    nx.draw(g2, pos_2, with_labels=True)
    
    # add dashed lines
    H = nx.Graph()
    new = [(0, 'd'), (1, 'c'), (2, 'b')]
    H.add_edges_from(new, style='dashed')
    nx.draw_networkx(H, pos=dict(pos_1, **pos_2), width=2, style='dashed')
    
    plt.show()
    

    Result:

    desired output