NetworkX is powerful but I was trying to plot a graph which shows node labels by default and I was surprised how tedious this seemingly simple task could be for someone new to Networkx. There is an example which shows how to add labels to the plot.
https://networkx.github.io/documentation/latest/examples/drawing/labels_and_colors.html
The problem with this example is that it uses too many steps and methods when all I want to do is just show labels which are same as the node name while drawing the graph.
# Add nodes and edges
G.add_node("Node1")
G.add_node("Node2")
G.add_edge("Node1", "Node2")
nx.draw(G) # Doesn't draw labels. How to make it show labels Node1, Node2 along?
Is there a way to make nx.draw(G) show the default labels (Node1, Node2 in this case) inline in the graph?
tl/dr: just add with_labels=True to the nx.draw call.
The page you were looking at is somewhat complex because it shows how to set lots of different things as the labels, how to give different nodes different colors, and how to provide carefully control node positions. So there's a lot going on.
However, it appears you just want each node to use its own name, and you're happy with the default color and default position. So
import networkx as nx
import pylab as plt
G=nx.Graph()
# Add nodes and edges
G.add_edge("Node1", "Node2")
nx.draw(G, with_labels = True)
plt.savefig('labels.png')
If you wanted to do something so that the node labels were different you could send a dict as an argument. So for example,
labeldict = {}
labeldict["Node1"] = "shopkeeper"
labeldict["Node2"] = "angry man with parrot"
nx.draw(G, labels=labeldict, with_labels = True)
I feel a better answer is not to use networkx to draw. They explicitly warn you that graph visualization is hard and networkx is mainly meant for graph analysis (from https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout):
Drawing
NetworkX provides basic functionality for visualizing graphs, but its main goal is to enable graph analysis rather than perform graph visualization. In the future, graph visualization functionality may be removed from NetworkX or only available as an add-on package.
Proper graph visualization is hard, and we highly recommend that people visualize their graphs with tools dedicated to that task. Notable examples of dedicated and fully-featured graph visualization tools are Cytoscape, Gephi, Graphviz and, for LaTeX typesetting, PGF/TikZ. To use these and other such tools, you should export your NetworkX graph into a format that can be read by those tools. For example, Cytoscape can read the GraphML format, and so, networkx.write_graphml(G, path) might be an appropriate choice.
thus my suggestion is to transform the graph to some format that has dedicated software for graph visualization and then draw (e.g. pydot, pygraphviz, graphviz etc). My suspicion is that pydot and pygraphviz are the best for some reason since networkx only supports those two. It seems from the docs in pygraphviz that it has a similar api so it might be the easiest to use if you already want like to use networkx (https://pygraphviz.github.io/documentation/stable/tutorial.html):
The API is very similar to that of NetworkX. Much of the NetworkX tutorial at https://networkx.org/documentation/latest/tutorial.html is applicable to PyGraphviz. See http://pygraphviz.github.io/documentation/latest/reference/api_notes.html for major differences.
In addition, pydot as of now does not really have docs (which personally bothers me. Idk if it's that it doesn't look nice on my browser or that it makes me feel that project is not being taken seriously by it's developers idk something just doesn't feel right even if it has a higher set of users pydot 15k vs pygraphviz 4k) reference: https://github.com/pydot/pydot/pull/241.
Also it seems that pygraphviz has more granular control than regular graphviz ref: Graphviz vs PyGraphViz. In addition, I don't know how to convert a networkx directly to a graphviz obj (since graphviz has the best docs and highest user base ~19k so I did prefer that), so I will go with pygraphviz for those reasons. Pygravix also has docs which although small make me happy (though not as good as graphviz but idk how to make graphviz graphs from networkx). It's hard to make these decisions but I can't stay on this forever and this seems mindful enough. Also, networkx is nice because I can transform dgl graphs to networkx too (and the relabeling was simple).
Considering those reasons let me give you the example code I wrote that does what you want using pygraphviz (but you could do it with pydot if you figured out how, transforming to pydot obj is trivial using networkx see my previous link):
# https://stackoverflow.com/questions/28533111/plotting-networkx-graph-with-node-labels-defaulting-to-node-name
import dgl
import numpy as np
import torch
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from pathlib import Path
g = dgl.graph(([0, 0, 0, 0, 0], [1, 2, 3, 4, 5]), num_nodes=6)
print(f'{g=}')
print(f'{g.edges()=}')
# Since the actual graph is undirected, we convert it for visualization purpose.
g = g.to_networkx().to_undirected()
print(f'{g=}')
# relabel
int2label = {0: "app", 1: "cons", 2: "with", 3: "app3", 4: "app4", 5: "app5"}
g = nx.relabel_nodes(g, int2label)
# https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout
g = nx.nx_agraph.to_agraph(g)
print(f'{g=}')
print(f'{g.string()=}')
# draw
g.layout()
g.draw("file.png")
# https://stackoverflow.com/questions/20597088/display-a-png-image-from-python-on-mint-15-linux
img = mpimg.imread('file.png')
plt.imshow(img)
plt.show()
# remove file https://stackoverflow.com/questions/6996603/how-to-delete-a-file-or-folder
Path('./file.png').expanduser().unlink()
# import os
# os.remove('./file.png')
output:
g=Graph(num_nodes=6, num_edges=5,
ndata_schemes={}
edata_schemes={})
g.edges()=(tensor([0, 0, 0, 0, 0]), tensor([1, 2, 3, 4, 5]))
g=<networkx.classes.multigraph.MultiGraph object at 0x7f8443e94250>
g=<AGraph <Swig Object of type 'Agraph_t *' at 0x7f846117a930>>
g.string()='graph "" {\n\tapp -- cons [key=0,\n\tid=0];\napp -- with [key=0,\nid=1];\napp -- app3 [key=0,\nid=2];\napp -- app4 [key=0,\nid=3];\napp -- app5 [key=0,\nid=4];\n}\n'
though I want to leave this link about pydot visualization since it seems very useful in general: Display graph without saving using pydot and probably provides the pydot answer for others if they need it. Though, I'd love to see arguments in favour of pydot.
Edit1: if you want to plot by attributed and not by label, see this answer: NetworkX node attribute drawing note that relabeling the way I suggested does not always have the intended semantics (e.g. it might join two nodes that were NOT meant to be joined).
Edit2: if you want to plot the attribute instead without self loops happening by accident see this answer: Draw more information on graph\nodes using PyGraphviz
Related
I am visualizing various networks with networkx. Take a look at simple example
graph = nx.DiGraph()
graph.add_edge("a", "b")
graph.add_edge("a", "a")
nx.draw(graph)
plt.show()
Unfortunately, networkx plotting does not render self loops. I am aware of other feature rich packages like GraphViz and its implementation pygraphviz. However, such packages do not allow me to customize my plots (like subplots, annotations etc). I can do all of these with networkx as it can plot with matplotlib Axes. Which is very convenient for programmatic manipulations and heavy customizations. Is there way to get network plotting of GraphViz to matplotlib?
I can always embed PNG created by GraphViz into matplotlib plots using imshow. However, its results are horrible with little customization control.
It doesn’t seem like this exists yet. If you want to implement it yourself, there are a few options. If you want to leave the layout to Graphviz and just hand shapes and positions to Matplotlib, two options remain to get the data necessary for rendering:
You use the binaries (like dot) with the -Tjson option, which seems to emit all necessary data for drawing, but I didn’t find documentation for what e.g. the ops are.
You Use Graphviz as a Library. Section 2.3 (Rendering the graph) describes how to access the data.
You then feed this data into the Axes’ Artist (tutorial). Matplotlib also has a tutorial for how to draw paths.
I am not sure why you say networkx does not render self-loops. Isn't this what you are looking for?
import networkx as nx
from matplotlib import pyplot as plt
graph = nx.DiGraph()
graph.add_edges_from([("a", "b"), ("a", "a"), ("b", "b"), ("c", "b"), ("c", "c")])
nx.draw(graph, with_labels=True)
plt.show()
I'm trying to use Python to plot simple hierarchical tree. I'm using the networkx module. I declared a simple graph
G=networkx.DiGraph()
After adding nodes and edges into G, I tried using
nx.draw(G)
or
nx.draw_networkx(G)
to plot. The output plot hierarchy are all correct, but the position of the nodes appears all random on the graph. Even worse, each time I ran the script, the node position are different.
There is a solution provided in a similar question which requires graphviz package.
pos=nx.graphviz_layout(G,prog='dot')
nx.draw(G,pos,with_labels=False,arrows=False)
Unfortunately I'm not allowed to install graphviz. So I'm seeking alternative solution here.
From graphviz solution, it looks like it's only needed to compute position of the node. Is it correct to say that, as long as I specify a list of coordinates to the draw command, I would be able to plot node at the correct location?
Thanks
UPDATE (15 Apr 2015) Look at my answer here for code that I think will do what you're after.
So networkx doesn't make it particularly easy to use the graphviz layout if you don't have graphviz because it's a better idea to use graphviz's algorithm if you're trying to reproduce graphviz's layout.
But, if you're willing to put in the effort to calculate the position for each node this is straightforward (as you guessed). Create a dict saying where each node's position should be.
pos = {}
for node in G.nodes():
pos[node] = (xcoord,ycoord)
nx.draw(G,pos)
will do it where xcoord and ycoord are the coordinates you want to have.
The various plotting commands are described here
I am trying to visualize graphs generated from networkx, using d3py.
I used the example provided (https://github.com/mikedewar/d3py/blob/master/examples/d3py_graph.py) but all I get is the graph without node names, how do I plot node names as well?
Also, how do I change edge and node colors?
Here is a partial answer, but also read the caveat at the end.
The easiest thing is changing the node colour. If you run the example you pointed to with
with d3py.NetworkXFigure(G, width=500, height=500) as p:
p += d3py.ForceLayout()
p.css['.node'] = {'fill': 'blue', 'stroke': 'magenta'}
p.show()
then you will have blue nodes with a magenta outline (you can use any html colour you like).
For edge colour, there is a hardcoded stroke: black; in the file d3py/geoms/graph.py. You can comment it out and reinstall d3py
line = {
"stroke-width": "1px",
"stroke": "black",
}
self.css[".link"] = line
Then you can specify edge colour and width as follows:
with d3py.NetworkXFigure(G, width=500, height=500) as p:
p += d3py.ForceLayout()
p.css['.node'] = {'fill': 'blue', 'stroke': 'magenta'}
p.css['.link'] = {'stroke': 'red', 'stoke-width': '3px'}
p.show()
There does not seem to be any way to add node labels easily with d3py. Using just d3.js, it can be done relatively easily (see this example). Which leads us to the major caveat...
Caveat
As #flup already mentioned, d3py does not seem really mature (the code from their Github repository does not even run the NetworkX example, there is a missing host parameter in networkx_figure.py). There has been no activity in the repo since a couple of months, so I guess the project is maybe abandoned. You could probably reach your goal much more easily using d3.js directly.
Just in case anyone ends up here looking around at d3py. I made d3py a couple of years ago now to see what it would feel like to plot d3 graphics from Python. It was fun! But I didn't have the time to do it properly, which was a shame.
However, Rob Story did! He made vincent, which is available at https://github.com/wrobstory/vincent. It's a much better implementation, and is much better thought out. Please do check it out!
Drawing 'static' graphs with networkx.
I would suggest trying mathplotlib.
networkx itself recommends using matplotlib as a plotting supplement.
It is easy to use and draws really nice graphs.
Say you have a networkx Graph "G". You can just plot it in 3 lines of code.
>>> import matplotlib.pyplot as plt
>>> nx.draw(G)
>>> plt.show()
Further, there is a flexibility of choosing between different layouts.
>>> nx.draw_random(G)
>>> nx.draw_circular(G)
>>> nx.draw_spectral(G)
Using an nx.draw*(G) command does not render the graph, until you use the plt.show().
It is as a "plot in the mind" or an abstract representation which you could customize before really rendering it.
Drawing interactive javascript / html5 graphs-
matplotlib is useful to draw static graphs. If you want to plot interactive Javascript graphs, then here are some interesting libraries (including D3.js)
d3.js
arbor.js
cytoscape.js
raphael.js
This libraries are independent and their use with networkx is generally more useful in web applications where networkx code resides in the backend and the Javascript code lies in the front end.
I try to draw multigraph in Python using graphviz.
For now I can draw usual graphs in Python somehow like:
import pygraphviz as pgv
G=pgv.AGraph()
G.add_node('a')
G.add_node('b')
G.layout()
G.add_edge('a','b','first')
G.add_edge('a','b','second')
sorted(G.edges(keys=True))
G.draw('file.png')
And I get on the output:
But actually I want get multigraph, i.e.
But documentation stays that it should differentiate :
I have no idea about drawing multigraph but not just graph.
Thanks for any help.
Addition:
it seems that there are no yet such libraries in python that can do it, so I did it using Wolfram Mathematica. But question is still opened.
Addition
Now working code looks so:
import pygraphviz as pgv
G=pgv.AGraph(strict=False)
G.add_node('a')
G.add_node('b')
G.layout()
G.add_edge('a','b','first')
G.add_edge('a','b','second')
sorted(G.edges(keys=True))
G.draw('file.png')
As the documentation you quoted says, you need to specify strict=False when creating a multi-edge graph. Since you didn't do this your graph doesn't support parallel edges.
This is a very specific inforgraphic challange altough the fundemental question is how do you build archs between words using matplotlib, cario or an other python libary.
Given a the following data structure.
me, you, 7 |
me, apple, 9 |
apple, you, 1 |
bike, me, 5
Names would be displayed horizontally the names with the most relationships larger than the others and then there would be weighted archs between the names. A 10 weighted arch would be twice as thick as a 5 weighted arch.
Inspiration comes from: similar diverstiy. http://similardiversity.net/
Let the challange commence!
matplotlib isn't the right library here, since it's not a general purpose graphics library. What you need here is either something like Cairo, or much simpler, you can do with the graphics capabilities of any GUI toolkit, such as PyQt. Another feasible approach is PyGame, which has good drawing capabilities as well.
If you want an example, see this Cairo samples page, the first sample - arc. You just write the text words and then use the arc code for an arc of any width and color between them.
There are several libraries, at least one of which relies on Matplotlib, that will do what you want. I recommend Networkx (www.networkx.lanl.gov) to build your graph structure, and which you can then use to call the relevant Matplotlib methods to plot. Networkx and Matplotlib work very well together.
import networkx as NX
import matplotlib.pyplot as PLT
Gh = NX.Graph()
Gh.add_edge("You", "Bike", weight=1.0)
Gh.add_edge("Bike", "Apple", weight=0.9)
Gh.add_edge("Me", "Bike", weight=1.1)
all_nodes = Gh.nodes()
# to scale node size with degree:
scaled_node_size = lambda(node) : NX.degree(Gh, node) * 700
position = NX.spring_layout(Gh) # just choose a layout scheme
NX.draw_networkx_nodes(Gh, position, node_size=map(scaled_node_size, all_nodes))
NX.draw_network_edges(Gh, position, Gh.edges(), width=1.0, alpha=1.0, edge_color="red")
# now for the Matplotlib part:
PLT.axis("off")
PLT.show()
As you can see, you could scale the edges by applying a factor to vary the 'weight' parameter to any of the 'edge' methods, just the same way as i did it for node scaling.
I would also recommend pygraphviz (obviously using graphviz as its backend). It is very similar to Netwworkx (same lead developer).
I don't really see where the difficulty or challenge is. All you need is a graphics library that can draw text and half circles, which is possible in pretty much everything. There is no need for Bezier curves. For example you could simply create an SVG file (then you don't even need any library and can nicely embed this in a webpage and use some JavaScript to make it interactive).
The actual plotting is simple: the center of a circle is in the middle between the two words, the radius is half the distance between them. You can then adjust the stroke to reflect other quantities.