Spring_layout doesn’t works properly with weights

I’m trying to use weights to define the distance between the nodes but it’s not working. I even tried to use normalized values without success.

import networkx as nx
import matplotlib.pyplot as plt

G = nx.Graph()

raw_values = [(1,2, {'weight':70000000000}), (2,3,{'weight':700000}), 
                  (1,4,{'weight':1000000}), (2,4,{'weight':50000000000})]

normalized_values = []
for l in list1:
    norm_value = np.log10(l[2]['weight'])
    normalized_values.append((l[0], l[1], {'weight': norm_value}))

G.add_edges_from(raw_values)

pos = nx.spring_layout(G)

nx.draw_networkx(G,pos)
edge_labels = nx.draw_networkx_edge_labels(G, pos)

plt.show()

Here is the result I get, as you can see the nodes are very close even though the values are very different:

enter image description here

Answer

spring_layout implements the Fruchterman-Reingold algorithm, which models the graph as a system in which nodes repel each other. The repulsion is counteracted by springs that are placed between connected nodes.

The implementation in networkx has some issues, specifically with the repulsion term. This is evident in your example figure, where node 3 should never be at the center given that it is the least connected node.

However, your main grievance is with the attraction term. Basically, you defined extremely strong springs that are overcoming any repulsion term. As a consequence, the nodes are all bunched onto one point. When networkx returns these essentially random positions, the positions are rescaled to the bounding box implied by the scale and center parameters.

Your problem can be somewhat ameliorated by normalizing weights by their mean:

enter image description here

Note, however, that the small weight for edge (1, 4) (i.e. repulsion > attraction between 1 and 4) prevents nodes 1 and 4 being close to node 2, even though the weights for (2, 4) and (1, 2) are very large. In other words, the resulting layout is also constrained by the triangle inequality, and no normalization could possibly change that. Therefor, spring_layout will, in general, not be able to reflect all weights (the exception being trees and graphs where weights are actual geometric distances).

#!/usr/bin/env python
"""
https://stackoverflow.com/q/67116565/2912349
"""
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx

if __name__ == '__main__':

    edge_weights = {
        (1, 2) : 7_000_000_000,
        (2, 3) : 700_000,
        (1, 4) : 1_000_000,
        (2, 4) : 5_000_000_000,
    }

    G1 = nx.Graph([(source, target, {'weight' : w}) for (source, target), w in edge_weights.items()])
    pos1 = nx.spring_layout(G1)

    mean = np.mean(list(edge_weights.values()))
    G2 = nx.Graph([(source, target, {'weight' : w / mean}) for (source, target), w in edge_weights.items()])
    pos2 = nx.spring_layout(G2)

    fig, (ax1, ax2) = plt.subplots(1, 2)
    nx.draw_networkx(G1, pos1, ax=ax1)
    nx.draw_networkx(G2, pos2, ax=ax2)
    ax1.set_title('Raw')
    ax2.set_title('Normalized')
    ax1.axis('off')
    ax2.axis('off')

    plt.show()