Próbuję użyć wagi, aby zdefiniować odległość między węzłami, ale nie działa. Próbowałem nawet użyć znormalizowanych wartości bez powodzenia.

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()

Oto wynik, jaki otrzymuję, jak widać węzły są bardzo blisko, chociaż wartości są bardzo różne:

enter image description here

0
Felipe Augusto 16 kwiecień 2021, 01:02

1 odpowiedź

Najlepsza odpowiedź

spring_layout wdraża algorytm Fruchtermerb-Reingold, który modele wykresu jako system, w którym węzły nawiązują się nawzajem. Repulscyjny przeciwdziała sprężynach, które są umieszczone między podłączonymi węzłami.

Wdrożenie w NetworkX ma pewne problemy, w szczególności z terminem odpychania. Jest to oczywiste w postaci przykładowej, gdzie węzeł 3 nigdy nie powinien być w środku, biorąc pod uwagę, że jest najmniej podłączony węzeł.

Jednak twoja główna karta jest z kadencją atrakcji. Zasadniczo zdefiniowałeś niezwykle silne źródła, które pokonują dowolny okres odpychania. W konsekwencji węzły są spięte na jeden punkt. Gdy Networkx zwraca te zasadniczo losowe pozycje, pozycje zostaną przesadniane do skrzynki ograniczającej przez parametry scale i center.

Twój problem może być nieco polepszył, normalizując ciężary według ich średniej:

enter image description here

Należy jednak pamiętać, że niewielka waga do krawędzi (1, 4) (tj. Repulsja i GT; przyciąganie między 1 a 4) zapobiega powstawaniu węzłów 1 i 4 w pobliżu węzła 2, nawet jeśli ciężary dla (2, 4) i (1 , 2) są bardzo duże. Innymi słowy, powstały układ jest również ograniczony przez nierówność trójkąta, a nie normalizacja nie mogłaby tego zmienić. Dlatego, spring_layout w ogóle nie będzie w stanie odzwierciedlać wszystkich ciężarów (wyjątek będący drzewami i wykresami, w których ciężary są rzeczywiste odległości geometryczne).

#!/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()
2
Paul Brodersen 16 kwiecień 2021, 14:36