Networkx: Overlapping edges when visualizing MultiGraph - python

The following multigraph plots correctly (i.e. parallel edges do not overlap) using graphviz neato to generate a png (as shown in this answer)
import networkx as nx
nx.MultiGraph ([(1,2),(1,2),(1,2),(3,1),(3,2)])
nx.write_dot(Gm,'multi.dot')
!neato -T png multi.dot > multi.png
However using the draw function of Networkx doesn't do the trick
nx.draw_graphviz(Gm,prog='neato')
Is it possible to prevent overlapping edges using the draw methods from Networkx?
Thanks

Unfortunately not. It is technically possible to do but so far nobody has written the code.

You can use matplotlib directly using the node positions you have calculated.
G=nx.MultiGraph ([(1,2),(1,2),(1,2),(3,1),(3,2)])
pos = nx.random_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = 'r', node_size = 100, alpha = 1)
ax = plt.gca()
for e in G.edges:
ax.annotate("",
xy=pos[e[0]], xycoords='data',
xytext=pos[e[1]], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5",
shrinkA=5, shrinkB=5,
patchA=None, patchB=None,
connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
),
),
)
plt.axis('off')
plt.show()

Well I know its probably not what you're looking for, but I was facing a similar problem where I wanted to have a directed graph where the edge between two nodes had a different weight depending on the direction (whether it was going into or out of the node) and the work around I did was I used a different color for each edge and decreased the opacity for one of them so it would show even if they overlap.
I only needed two edges between my two nodes so it did the trick for me.
G = nx.DiGraph()
G.add_nodes_from([0,1])
pos = nx.circular_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = 'r', node_size = 100, alpha = 1)
nx.draw_networkx_edges(G, pos, edgelist = [(0,1)], width = 2, alpha = 0.5, edge_color='b')
nx.draw_networkx_edges(G, pos, edgelist= [(1,0)], width = 1, alpha = 1)
plt.axis('off')
plt.show()

An improvement to the answer above is adding the connectionstyle argument to nx.draw:
import networkx as nx
G = nx.DiGraph()
G.add_nodes_from([0,1])
pos = nx.circular_layout(G)
nx.draw_networkx_nodes(G, pos, connectionstyle='arc3, rad = 0.1', node_color = 'r', node_size = 100, alpha = 1)
nx.draw_networkx_edges(G, pos,connectionstyle='arc3, rad = 0.1', edgelist = [(0,1)], width = 2, alpha = 0.5, edge_color='b')
nx.draw_networkx_edges(G, pos,connectionstyle='arc3, rad = 0.1', edgelist= [(1,0)], width = 1, alpha = 1)
plt.axis('off')
plt.show()

Related

Curved edges using matplotlib and Networkx in Python 3.x

I want to draw curved edges using the Networkx framework and matplotlib.
Basically the same problem as linked below:
Networkx: Overlapping edges when visualizing MultiGraph
One answer is:
import networkx as nx
G = nx.DiGraph()
G.add_nodes_from([0,1])
pos = nx.circular_layout(G)
nx.draw_networkx_nodes(G, pos, connectionstyle='arc3, rad = 0.1', node_color = 'r', node_size = 100, alpha = 1)
nx.draw_networkx_edges(G, pos,connectionstyle='arc3, rad = 0.1', edgelist = [(0,1)], width = 2, alpha = 0.5, edge_color='b')
nx.draw_networkx_edges(G, pos,connectionstyle='arc3, rad = 0.1', edgelist= [(1,0)], width = 1, alpha = 1)
plt.axis('off')
plt.show()
But that produces:
In the end I want to produce something like this:
I don't think you can do this directly with networkx functions. But you can use matplotlib directly using the node positions you have calculated.
E.g. based on this:
import networkx as nx
G = nx.DiGraph()
G.add_nodes_from([0,1])
pos = nx.circular_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = 'r', node_size = 100, alpha = 1)
ax = plt.gca()
ax.annotate("",
xy=pos[0], xycoords='data',
xytext=pos[1], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5",
shrinkA=5, shrinkB=5,
patchA=None, patchB=None,
connectionstyle="arc3,rad=0.3",
),
)
plt.axis('off')
plt.show()
Gives:

Legend based on edge color in networkx

Is there a way to create a legend in networkx based on edge color (as opposed to by node color)?
This is my graph:
plt.figure(figsize = (15, 10))
G = nx.from_pandas_dataframe(df, 'From', 'To', ['Order', 'Colors'])
edge_labels = nx.get_edge_attributes(G, 'Order')
nx.draw_networkx(G, with_labels = False, node_color = 'black', alpha = 0.5, node_size = 3, linewidths = 1, edge_color = df['Colors'], edge_cmap =
plt.cm.Set2)
plt.show()
In this, ['Order'] is a descriptor of the edge and ['Color'] is a unique integer mapped to each value in ['Order'], which is working to create the edge colors based on the Set2 colormap.
I can get the edge labels with something like:
edge_labels = nx.get_edge_attributes(G, 'Order')
but how do I put this into a legend?
I'm happy to share the data and complete code if helpful!
One way you can do it is in the spirit of this SO answer which uses proxies for each member of a LineCollection in the legend.
You can get the LineCollection by using the step-by-step graph drawing functions, drawing the nodes and edges separately (e.g. draw_networkx_nodes doc.)
import matplotlib.pyplot as plt
import networkx as nx
from matplotlib.lines import Line2D
# make a test graph
n = 7 # nodes
m = 5 # edges
G = nx.gnm_random_graph(n, m)
# and define some color strings (you'll get this array from the dataframe)
_c = 'rgbcmky' * m # way too many colors, trim after
clrs = [c for c in _c[:m]]
plt.ion()
plt.figure(figsize = (9, 7), num=1); plt.clf()
# draw the graph in several steps
pos = nx.spring_layout(G)
h1 = nx.draw_networkx_nodes(G, pos=pos, node_color = 'black',
alpha = 0.9, node_size = 300, linewidths=6)
# we need the LineCollection of the edges to produce the legend (h2)
h2 = nx.draw_networkx_edges(G, pos=pos, width=6, edge_color=clrs)
# and just show the node labels to check the labels are right!
h3 = nx.draw_networkx_labels(G, pos=pos, font_size=20, font_color='c')
#https://stackoverflow.com/questions/19877666/add-legends-to-linecollection-plot - uses plotted data to define the color but here we already have colors defined, so just need a Line2D object.
def make_proxy(clr, mappable, **kwargs):
return Line2D([0, 1], [0, 1], color=clr, **kwargs)
# generate proxies with the above function
proxies = [make_proxy(clr, h2, lw=5) for clr in clrs]
# and some text for the legend -- you should use something from df.
labels = ["{}->{}".format(fr, to) for (fr, to) in G.edges()]
plt.legend(proxies, labels)
plt.show()
This produces something like:

How do I customize the display of edge labels in networkx?

I create a graph with edge attributions (say r, such as, r=23).
How do display edge labels only with the values, 23 instead of {'r':'23'}.
Related source codes are below:
# build a graph
G.add_edge(u, v, r=value)
# plot the graph
pos = nx.spring_layout(G, scale=2)
nx.draw(G, pos)
edge_labels = nx.get_edge_attributes(G,'r')
nx.draw_networkx_edge_labels(G, pos, labels = edge_labels)
plt.savefig(out_file)
The command draw_networkx_edge_labels needs the argument edge_labels rather than labels.
So you need to change nx.draw_networkx_edge_labels(G, pos, labels = edge_labels) to nx.draw_networkx_edge_labels(G, pos, edge_labels = edge_labels)
This way it worked on my :
grafo_labels = nx.get_edge_attributes(G,'weight')
edges_label = nx.draw_networkx_edge_labels(G, pos, edge_labels = grafo_labels)

How to remove an attribute from the edge label in a networkx graph?

How to remove an attribute from the edge label in a networkx graph?
The following example
edgelabels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edges(G, pos, label_pos=0.5, alpha=0.5, edge_color='k')
nx.draw_networkx_edge_labels(G, pos, labels = edgelabels)
prints {'weight': n} on each edge label: what I want is 'n' alone. This would make the graph far more legible.
Here is how to draw just the number for a 'weight' attribute.
import matplotlib.pyplot as plt
import networkx as nx
G = nx.Graph()
G.add_edge(1,2,weight=7)
G.add_edge(2,3,weight=42)
labels = {}
for u,v,data in G.edges(data=True):
labels[(u,v)] = data['weight']
pos = nx.spring_layout(G)
nx.draw(G,pos)
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
plt.show()
If you want to have 'n' as an edge label, you must use the parameter 'edge_labels' with draw_networkx_edge_labels, instead of 'labels'.
nx.draw_networkx_edge_labels(G, pos, edge_labels = edgelabels)

Colorbar for edges in networkx

I am trying to get a colorbar for edges in a networkx graph. Here is a code snippet
import networkx as nx
import matplotlib.colors as colors
import matplotlib.cm as cmx
n = 12 # Number of clusters
w = 21 # Number of weeks
m = Basemap(
projection='merc',
ellps = 'WGS84',
llcrnrlon=-98.5,
llcrnrlat=25,
urcrnrlon=-60,
urcrnrlat=50,
lat_ts=0,
resolution='i',
suppress_ticks=True)
mx, my = m(list(ccentroids['lon']), list(ccentroids['lat']))
# The NetworkX part
# put map projection coordinates in pos dictionary
G = nx.DiGraph()
G.add_nodes_from(range(n))
for i in range(n):
for j in range(n):
if P_opt[i,j] > 0.5 and i != j:
G.add_edge(i,j, weight = P_opt[i,j])
pos = {i : (mx[i], my[i]) for i in range(n)}
# Add a color_map for the edges
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=np.max(P_opt))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
colorList = []
weights_list = []
for i in G.edges():
a, b = i
colorVal = scalarMap.to_rgba(G.edge[a][b]['weight'])
colorList.append(colorVal)
weights_list.append(G.edge[a][b]['weight'])
plt.clf()
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='w', frame_on=False)
fig.set_size_inches(18.5, 10.5)
# draw the network
nodes = nx.draw_networkx_nodes(G, pos, node_size=100, node_color=q[:,t], cmap = plt.cm.jet,
font_size=8, with_labels=False, label='Cluster centroids')
edges = nx.draw_networkx_edges(G, pos, edge_color=colorList)
m.drawcountries()
m.bluemarble()
This gives me the following image:
Now I want to add a colorbar for the edges. I tried doing something like,
plt.sci(edges)
edges.set_array(np.array(weights_list))
plt.colorbar(shrink = 0.8)
This gives me an image like:
The colours of the arrows and edges seem to differ. How can I correct that? Thanks.
EDIT: I tried to use the following code by modifying the edge line:
edges = nx.draw_networkx_edges(G, pos, edge_color=colorList, edge_cmap = plt.cm.jet)
plt.colorbar(edges)
This gives me an error TypeError: You must first set_array for mappable
Changing the edge_color to be the weights_list I get the following picture:
It seems you are getting the colomap/colorbar for the nodes. Here is how to set a colormap and draw a colorbar for edge colors:
![import networkx as nx
import matplotlib.pyplot as plt
G = nx.star_graph(20)
pos = nx.spring_layout(G)
colors = range(20)
nodes = nx.draw_networkx_nodes(G,pos,node_color='k', with_labels=False)
edges = nx.draw_networkx_edges(G,pos,edge_color=colors,width=4,
edge_cmap=plt.cm.Blues)
plt.colorbar(edges)
plt.axis('off')
I know this is an old one, but I spent some time figuring this out, and maybe it is still useful for someone.
nx.draw_networkx_edges
returns
matplotlib.collection.LineCollection
if there are no arrows.
returns
list of matplotlib.patches.FancyArrowPatch
if there are arrows.
docs
For my specific case I could get a colorbar like in Aric's answer only if I set arrows to False:
edges = nx.draw_networkx_edges(G, edge_color=colors, arrows=False)
plt.colorbar(edges)

Categories