jak narysować skierowane wykresy za pomocą networkx w Pythonie?

101

Mam kilka węzłów pochodzących ze skryptu, który chcę odwzorować na wykresie. Poniżej chcę użyć strzałki, aby przejść od A do D i prawdopodobnie zabarwić krawędź (na czerwono lub coś w tym stylu).

Jest to w zasadzie jak ścieżka od A do D, gdy wszystkie inne węzły są obecne. możesz wyobrazić sobie każdy węzeł jako miasta, a podróż z punktu A do D wymaga wskazówek (z grotami strzałek).

Poniższy kod tworzy wykres

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

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

val_map = {'A': 1.0,
           'D': 0.5714285714285714,
           'H': 0.0}

values = [val_map.get(node, 0.25) for node in G.nodes()]

nx.draw(G, cmap = plt.get_cmap('jet'), node_color = values)
plt.show()

ale chcę coś takiego, jak pokazano na obrazku.wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Groty strzałek pierwszego obrazu i krawędzie w kolorze czerwonym na drugim obrazie.

burza mózgów
źródło

Odpowiedzi:

86

W pełni rozwinięty przykład ze strzałkami tylko dla czerwonych krawędzi:

import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
G.add_edges_from(
    [('A', 'B'), ('A', 'C'), ('D', 'B'), ('E', 'C'), ('E', 'F'),
     ('B', 'H'), ('B', 'G'), ('B', 'F'), ('C', 'G')])

val_map = {'A': 1.0,
           'D': 0.5714285714285714,
           'H': 0.0}

values = [val_map.get(node, 0.25) for node in G.nodes()]

# Specify the edges you want here
red_edges = [('A', 'C'), ('E', 'C')]
edge_colours = ['black' if not edge in red_edges else 'red'
                for edge in G.edges()]
black_edges = [edge for edge in G.edges() if edge not in red_edges]

# Need to create a layout when doing
# separate calls to draw nodes and edges
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'), 
                       node_color = values, node_size = 500)
nx.draw_networkx_labels(G, pos)
nx.draw_networkx_edges(G, pos, edgelist=red_edges, edge_color='r', arrows=True)
nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False)
plt.show()

Czerwone krawędzie

Marius
źródło
1
to szalone, jak różne są nasze dwa zaktualizowane obrazy. +1 za ustalenie kolorów krawędzi!
mdml
dlaczego twoja krawędź (C, E) nie jest czerwona, chociaż zgodnie z kodem powyżej musi być czerwona?
burza mózgów
czy nie można mieć tych grotów strzałek tylko na interesujących nas krawędziach? na przykład (A, C) i (C, E)
burza mózgów
@ user1988876: Ach, przepraszam, (C, E)nie jest czerwony, ponieważ wybrałem krawędzie, red_edgeskiedy nadal pracowałem z twoim nieukierunkowanym wykresem, po prostu wybierając losowo z krawędzi zwróconych przez G.edges(). Tak powinno być red_edges = [('A', 'C'), ('E', 'C')].
Marius
@ user1988876: Posiadanie strzałek tylko na niektórych krawędziach jest możliwe przy oddzielnych wywołaniach do draw_networkx_edges(). Wyczyściłem kod i naprawiłem problemy z DiGraph.
Marius
47

Podaję to tylko dla kompletności. Wiele się nauczyłem od Mariusa i MDML. Oto ciężary krawędzi. Przepraszam za strzały. Wygląda na to, że nie tylko ja mówię, że nie można temu zaradzić. Nie mogłem tego wyrenderować na notebooku ipython, musiałem przejść bezpośrednio z Pythona, co było problemem z szybszym wprowadzeniem moich obciążeń krawędzi.

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

G = nx.DiGraph()

G.add_edges_from([('A', 'B'),('C','D'),('G','D')], weight=1)
G.add_edges_from([('D','A'),('D','E'),('B','D'),('D','E')], weight=2)
G.add_edges_from([('B','C'),('E','F')], weight=3)
G.add_edges_from([('C','F')], weight=4)


val_map = {'A': 1.0,
                   'D': 0.5714285714285714,
                              'H': 0.0}

values = [val_map.get(node, 0.45) for node in G.nodes()]
edge_labels=dict([((u,v,),d['weight'])
                 for u,v,d in G.edges(data=True)])
red_edges = [('C','D'),('D','A')]
edge_colors = ['black' if not edge in red_edges else 'red' for edge in G.edges()]

pos=nx.spring_layout(G)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels)
nx.draw(G,pos, node_color = values, node_size=1500,edge_color=edge_colors,edge_cmap=plt.cm.Reds)
pylab.show()

wprowadź opis obrazu tutaj

Back2Basics
źródło
9
Uruchomiłem to i nie otrzymałem etykiet węzłów. Trzeba dodać te: node_labels = {node:node for node in G.nodes()}; nx.draw_networkx_labels(G, pos, labels=node_labels).
MadeOfAir
A co, jeśli masz już niekierowany wykres i chcesz odtworzyć jego skierowaną kopię? Czy istnieje sposób ustawienia G.add_edges_from()linii bez konieczności ręcznego wprowadzania punktu początkowego i końcowego? Może dodanie krawędzi z dict?
FaCoffee
Pierwsza linia kodu w tej sekcji (inna niż linie importu) określa, jaki to typ wykresu i jakie krawędzie akceptuje. Możesz przejść od dwuznaku (więcej informacji) do wykresu (mniej informacji), ale nie możesz przejść od wykresu (mniej informacji) do dwuznaku (więcej informacji) bez informacji lub sposobu na skonstruowanie brakujących informacji. Proponuję postawić twoje pytanie na przykładzie innego pytania o przepełnienie stosu.
Back2Basics
1
Czy można dostać prawdziwe strzały na krawędziach? Nie podoba mi się tylko grubszy koniec.
Wikunia
1
Rysowanie grotów strzałek w matplotlib jest trudne i obecnie nie jest obsługiwane w NetworkX. Żądania pull są akceptowane.
Back2Basics
33

Zamiast zwykłego nx.draw możesz użyć:

nx.draw_networkx(G[, pos, arrows, with_labels])

Na przykład:

nx.draw_networkx(G, arrows=True, **options)

Możesz dodać opcje, inicjując tę ​​** zmienną w następujący sposób:

options = {
    'node_color': 'blue',
    'node_size': 100,
    'width': 3,
    'arrowstyle': '-|>',
    'arrowsize': 12,
}

Również niektóre funkcje obsługują directed=True parameter W tym przypadku ten stan jest domyślny:

G = nx.DiGraph(directed=True)

Dokumentacja networkx znajduje się tutaj .

Wykres z obrazem strzałki

Raz
źródło
21

Musisz użyć wykresu skierowanego zamiast wykresu, tj

G = nx.DiGraph()

Następnie utwórz listę kolorów krawędzi, których chcesz użyć, i przekaż je nx.draw (jak pokazuje @Marius).

Podsumowując to wszystko, otrzymuję obraz poniżej. Wciąż nie jest to inne zdjęcie, które pokazujesz (nie wiem, skąd pochodzą twoje ciężary krawędzi), ale znacznie bliżej! Jeśli chcesz mieć większą kontrolę nad wyglądem wykresu wyjściowego (np. Uzyskać groty strzałek, które wyglądają jak strzałki), wypróbuj NetworkX z Graphviz .

wprowadź opis obrazu tutaj

mdml
źródło
Och, na zdrowie, nie mogłem zrozumieć, dlaczego strzały nie działają, ponieważ widziałem argumenty za nimi w dokumentacji.
Marius
9
import networkx as nx
import matplotlib.pyplot as plt

g = nx.DiGraph()
g.add_nodes_from([1,2,3,4,5])
g.add_edge(1,2)
g.add_edge(4,2)
g.add_edge(3,5)
g.add_edge(2,3)
g.add_edge(5,4)

nx.draw(g,with_labels=True)
plt.draw()
plt.show()

To jest po prostu proste, jak narysować skierowany wykres za pomocą Pythona 3.x przy użyciu networkx. tylko prosta reprezentacja i może być modyfikowana, kolorowana itp. Zobacz wygenerowany wykres tutaj .

Uwaga: to tylko prosta reprezentacja. Można dodać krawędzie ważone, np

g.add_edges_from([(1,2),(2,5)], weight=2)

i dlatego ponownie wykreślił.

Padmalochan Panda
źródło
1
import networkx as nx
import matplotlib.pyplot as plt

G = nx.DiGraph()
G.add_node("A")
G.add_node("B")
G.add_node("C")
G.add_node("D")
G.add_node("E")
G.add_node("F")
G.add_node("G")
G.add_edge("A","B")
G.add_edge("B","C")
G.add_edge("C","E")
G.add_edge("C","F")
G.add_edge("D","E")
G.add_edge("F","G")

print(G.nodes())
print(G.edges())

pos = nx.spring_layout(G)

nx.draw_networkx_nodes(G, pos)
nx.draw_networkx_labels(G, pos)
nx.draw_networkx_edges(G, pos, edge_color='r', arrows = True)

plt.show()
Sachin Rawat
źródło