como desenhar gráficos direcionados usando networkx em python?

101

Tenho alguns nós provenientes de um script que desejo mapear em um gráfico. Abaixo, quero usar a seta para ir de A para D e provavelmente ter a borda colorida também (vermelha ou algo assim).

É basicamente como um caminho de A a D quando todos os outros nós estão presentes. você pode imaginar cada nó como cidades e viajar de A para D requer direções (com pontas de flechas).

Este código abaixo constrói o gráfico

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

mas eu quero algo como mostrado na imagem.insira a descrição da imagem aqui insira a descrição da imagem aqui

Pontas de seta da primeira imagem e as bordas em cor vermelha na segunda imagem.

chuva de ideias
fonte

Respostas:

86

Exemplo totalmente desenvolvido com setas apenas para as bordas vermelhas:

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

Bordas vermelhas

Marius
fonte
1
é incrível como nossas duas imagens atualizadas são diferentes. 1 para descobrir as cores das bordas!
mdml
por que sua borda (C, E) não é vermelha, embora deva ser vermelha de acordo com seu código acima?
tempestade de cérebros de
não é possível ter essas pontas de flecha apenas nas bordas de interesse? por exemplo (A, C) e (C, E)
tempestade de cérebros
@ user1988876: Ah, desculpe, (C, E)não é vermelho porque eu escolhi as arestas para red_edgesquando eu ainda estava trabalhando com seu gráfico não direcionado, apenas escolhendo aleatoriamente das arestas retornadas por G.edges(). Deve ser red_edges = [('A', 'C'), ('E', 'C')].
Marius
@ user1988876: Ter setas em apenas algumas das bordas é possível com chamadas separadas para draw_networkx_edges(). Limpei o código e consertei os problemas do DiGraph.
Marius
47

Eu só coloquei isso para ser completo. Aprendi muito com o marius e o mdml. Aqui estão os pesos das arestas. Desculpe pelas flechas. Parece que não sou o único a dizer que não há nada a fazer. Não consegui renderizar isso com o notebook ipython. Tive que ir direto do python, que era o problema em obter meus pesos de borda mais cedo.

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

insira a descrição da imagem aqui

Back2Basics
fonte
9
Eu executei isso e não obtive os rótulos dos nós. Você precisa adicionar estes: node_labels = {node:node for node in G.nodes()}; nx.draw_networkx_labels(G, pos, labels=node_labels).
MadeOfAir
E se você já tiver um gráfico não direcionado e quiser reproduzir uma cópia direcionada dele? Existe alguma maneira de definir a G.add_edges_from()linha sem ter que inserir manualmente o ponto inicial e final? Talvez adicionando arestas de um dict?
FaCoffee
A primeira linha de código nesta seção (além das linhas de importação) define que tipo de gráfico ele é e que tipo de arestas ele aceita. Você pode ir de um dígrafo (mais informações) a um gráfico (menos informações), mas não pode ir de um gráfico (menos informações) a um dígrafo (mais informações) sem a informação ou uma maneira de construir a informação que falta. Eu sugeriria colocar sua pergunta com um exemplo em outra pergunta de estouro de pilha.
Back2Basics
1
É possível obter flechas reais nas bordas? Eu não gosto apenas da extremidade mais grossa.
Wikunia
1
Desenhar pontas de seta em matplotlib é complicado e atualmente não é compatível com NetworkX. Solicitações pull são aceitas.
Back2Basics
33

Em vez de nx.draw regular, você pode querer usar:

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

Por exemplo:

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

Você pode adicionar opções inicializando essa ** variável assim:

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

Além disso, algumas funções suportam o directed=True parameter Neste caso, este estado é o padrão:

G = nx.DiGraph(directed=True)

A referência networkx é encontrada aqui .

Gráfico com imagem de setas

Raz
fonte
21

Você precisa usar um gráfico direcionado em vez de um gráfico, ou seja,

G = nx.DiGraph()

Em seguida, crie uma lista das cores das bordas que deseja usar e passe-as para nx.draw (como mostrado por @Marius).

Juntando tudo isso, obtenho a imagem abaixo. Ainda não é exatamente a outra imagem que você mostra (não sei de onde vêm seus pesos de aresta), mas muito mais perto! Se você quiser mais controle da aparência do seu gráfico de saída (por exemplo, obter pontas de flechas que se parecem com setas), eu verificaria NetworkX com Graphviz .

insira a descrição da imagem aqui

mdml
fonte
Ah, alegria, não consegui descobrir por que as flechas não estavam funcionando, pois pude ver os argumentos para elas na documentação.
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()

É simples desenhar um gráfico direcionado usando python 3.x usando networkx. apenas representação simples e pode ser modificada e colorida etc. Veja o gráfico gerado aqui .

Nota: é apenas uma representação simples. Bordas ponderadas podem ser adicionadas como

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

e, portanto, plotado novamente.

Panda Padmalochan
fonte
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
fonte