I have the problem that I have a weighted adjacency matrix C of a directed graph, so C(j,i)=0, whenever there is no edge from j to i and if C(j,i)>0, then C(j,i) is the weight of the edge;
Now I want to plot the Directed Graph. There are many solutions when you manually add edges, see e.g. here:
Add edge-weights to plot output in networkx
But I want to plot edges and edge weights based on my matrix C; I started the following way:
def DrawGraph(C):
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph(C)
plt.figure(figsize=(8,8))
nx.draw(G, with_labels=True)
This plots a graph and there are labels on the vertices, but there are no edge weights - also I cannot adapt the technic from the upper link to make it work - so what could I do?
And how would I change node size and color?
There are various ways to do this using networkx - here is a solution which should fit your requirements:
Code:
# Set up weighted adjacency matrix
A = np.array([[0, 0, 0],
[2, 0, 3],
[5, 0, 0]])
# Create DiGraph from A
G = nx.from_numpy_matrix(A, create_using=nx.DiGraph)
# Use spring_layout to handle positioning of graph
layout = nx.spring_layout(G)
# Use a list for node_sizes
sizes = [1000,400,200]
# Use a list for node colours
color_map = ['g', 'b', 'r']
# Draw the graph using the layout - with_labels=True if you want node labels.
nx.draw(G, layout, with_labels=True, node_size=sizes, node_color=color_map)
# Get weights of each edge and assign to labels
labels = nx.get_edge_attributes(G, "weight")
# Draw edge labels using layout and list of labels
nx.draw_networkx_edge_labels(G, pos=layout, edge_labels=labels)
# Show plot
plt.show()
Result:
I am trying to run the following code to plot networkx graph but it gives me type error: list indices must be integers or slices, not str. May you help me with that?
pos = [[6.886709112999999, 50.50618938]]
nodes = ['866','144']
edges = [['866', '144']]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G, pos)
plt.show()
If you want to specify the positions for draw_networkx you should supply a dictionary of positions (x,y):
pos (dictionary, optional) – A dictionary with nodes as keys and positions as values. If not specified a spring layout positioning will be computed. See networkx.drawing.layout for functions that compute node positions.
You can take a look here (how to plot a networkx graph using the (x,y) coordinates of the points list?) for a recent example.
Or with your small example:
import networkx as nx
import matplotlib.pylab as plt
pos = {'866':[6.886709112999999, 0], '144': [50.50618938, 0]}
nodes = ['866','144']
edges = [['866', '144']]
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.draw_networkx(G, pos)
plt.show()
The type error you observed, is probably caused by networks trying to call your pos list with the nodes as index, e.g. pos['866'].
This answer demonstrates how to draw a graph with custom colors and edge thickness using the following code:
import networkx as nx
G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)
Suppose however, that I want to graph a multi graph like:
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='b',weight=6)
Calling something like draw should result in a total of three points. Point 1 and 2 should have both a red and blue line between them, similarly 2 and 3 should have both a red and blue line between them as well.
This fails to work for multigraphs because the multiple edges requires a different storage technique. Is there a relatively easy way around this?
Also, I believe this question and answer does not apply. The questioner uses the MultiGraph object, however, the actual graph is not a multigraph. The solution, is to chose the first (and, in his case, only) edge. In this case, however, both edges are needed in the drawing phase.
Is there a way to graph the multiple edges with different colors and weights in networkx?
You just need to access the edges of a multigraph in a different way
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=8)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='c',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = []
weight = []
for (u,v,attrib_dict) in list(G.edges.data()):
colors.append(attrib_dict['color'])
weight.append(attrib_dict['weight'])
nx.draw(G, pos, edges=edges, edge_color=colors, width=weight, )
plt.show()
Here is another combination of edges
G = nx.MultiGraph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(1,2,color='b',weight=3)
G.add_edge(2,3,color='r',weight=4)
G.add_edge(2,3,color='b',weight=6)
You can read more about accessing multigraph edges here.
I'm using networkx to draw a graph of a network with nodes at given positions on a field. The field is divided into a set amount of cells and the node positions correspond to a cell. When drawing the graph, I would like to show a grid in the background so the cells and distances in the graph are visible. Does networkx offer an option for that? I could not find one. I tried setting the grid via pyplot:
plt.xlim(0,14)
plt.ylim(0,10)
plt.xticks([x for x in xrange(0,14)])
plt.yticks([x for x in xrange(0,10)])
plt.grid(True)
which works by itself (see here) but when also calling
nx.draw(G, pos)
it shows the graph without the grid (see here). I'm guessing the grid is still in the background but networkx draws completely over it.
So is there any way to show the grid with plt or maybe a networkx command that I didn't find which allows something similar?
edit: Here is the complete code used. See the difference when (un)commenting nx.draw(G,pos)
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def quit_figure(event):
if event.key == 'q':
plt.close(event.canvas.figure)
nodes = [[1,1],[3,1],[6,1],[3,2],[13,1],[3,5]]
distance_array = [
[[],[],[1,3],[],[5],[],[],[],[],[],[],[],[],[],[]],
[[],[3],[0],[2],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[1,3],[5],[],[],[4],[],[],[],[],[],[]],
[[],[1],[0],[2,5],[],[],[],[],[],[],[10],[],[],[],[]],
[[],[],[],[],[],[],[],[2],[],[],[3,5],[],[],[],[]],
[[],[],[],[3],[0,2],[],[],[],[],[],[4],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],
[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]]]
current_transmission_range = 0
transmission_range = np.zeros((len(nodes)), dtype=int)
pos = {x:[nodes[x][0],nodes[x][1]] for x in xrange(len(nodes))}
graph_nodes = [x for x in xrange(len(nodes))]
current_transmission_range = 0
cid = plt.gcf().canvas.mpl_connect('key_press_event', quit_figure)
plt.xlim(0,14)
plt.ylim(0,10)
plt.xticks([x+0.5 for x in xrange(0,14)], [' '+str(x) for x in xrange(0,13)])
plt.yticks([x+0.5 for x in xrange(0,10)], ['\n\n'+str(x)+'\n\n\n\n' for x in xrange(0,9)])
plt.grid(True)
G = nx.Graph()
for node in graph_nodes:
G.add_node(node)
for node in graph_nodes:
for j in xrange(transmission_range[node]+1):
for k in distance_array[node][j]:
if(j <= current_transmission_range):
G.add_edge(node, k,weight=j)
edge_weight=dict([((u,v,),int(d['weight'])) for u,v,d in G.edges(data=True)])
nx.draw_networkx_edge_labels(G, pos,edge_labels=edge_weight)
# draw graph
# nx.draw(G, pos)
plt.show()
Use plt.grid('on')
import networkx as nx
import matplotlib.pyplot as plt
G = nx.path_graph(4)
nx.draw_networkx(G)
plt.grid('on')
plt.show()
If you want to use nx.draw(G) instead of nx.draw_networkx(G) you must also turn on the axis with plt.axis('on').
NetworkX's draw explicitly turns off axis.
You can avoid using draw and call the drawing subfunctions instead:
#...
nx.draw_networkx_nodes(G,pos)
nx.draw_networkx_edges(G,pos)
plt.show()
Or use draw_networkx, as Aric suggested.
That approach would be recommended if you're looking for long-term control over the drawing (as the draw implementation may change).
Of course, if it doesn't worry you, you can simply do plt.axes().set_axis_on() right after calling draw
The above is the output of my current graph. However, I have yet to manage what I am trying to achieve. I need to output my graph in a larger size so that each node/edge can be viewed with ease.
I've tried nx.draw(G, node_size=size), but that only increases the size of the nodes, not the distance between nodes and edges.
You could try either smaller nodes/fonts or larger canvas. Here is a way to do both:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.cycle_graph(80)
pos = nx.circular_layout(G)
# default
plt.figure(1)
nx.draw(G,pos)
# smaller nodes and fonts
plt.figure(2)
nx.draw(G,pos,node_size=60,font_size=8)
# larger figure size
plt.figure(3,figsize=(12,12))
nx.draw(G,pos)
plt.show()
Since it seems that your network layout is too "messy", you might want to try different graph layout algorithms and see which one suits you best.
nx.draw(G)
nx.draw_random(G)
nx.draw_circular(G)
nx.draw_spectral(G)
nx.draw_spring(G)
Also, if you have too many nodes (let's say some thousands) visualizing your graph can be a problem.
you can increase the size of the plot as well as set the dpi.
If dpi is lowered that nodes would spread out more.
G = nx.Graph()
# Add edges
fig = plt.figure(1, figsize=(200, 80), dpi=60)
nx.draw(G, with_labels=True, font_weight='normal')