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.
Related
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
I am using bokeh for plotting. With my current settings, bokeh shows some text and lines kind of blurred (anti-aliased?). This is not really noticeable on my monitor, but on some projectors, especially when doing screenshots and inserting them into presentations, it looks weird.
As requested, a minimum working example:
from bokeh.plotting import figure, show
p = figure(plot_width=1000, plot_height=600,
title="TestTitle", x_axis_label = "Length [cm]", y_axis_label="Height [m]")
p.xaxis.axis_label_text_font_style = "normal"
p.xaxis.axis_label_text_font_size = "12pt"
show(p)
I am using Windows 7 and tried this in the current versions of Chrome and IE. Python 3.6, bokeh version 0.13.0. The first image is taken from the example, the second is taken from my real code:
I tried different figure sizes, but the problem persists - maybe I am trying the wrong ones? Anything I can do about this, except simply trying different sizes until something "works"? Setting px instead of pts seems to make it worse...
Regarding the text, it is rendered on to a raster HTML canvas, and the details of how this is done are entirely dependent on the browser canvas implementation. (FWIW things look better on any browser on OSX than the above image.) There's not anything we can to change how a specific browser renders text, and not much I can suggest except to make much bigger canvas sizes if you need to show something extremely magnified.
Regarding the aliasing of the axes and tick marks, I can't reproduce anything like that on any OSX browser (Safari, FF, or Chrome). It definitely appears to be a Windows-specific issue. It's possible there are issues that could be addressed, e.g a different HiDPI setting, or half-pixel offsets, but it would take investigation to try and determine what can be done. A Github issue with details would be appropriate, but I can't speculate when it might be addressed (we are under-resourced and no core contributors are regular windows users).
I am required to use Python for engineering project. Need to create many graphs, including surface plots. In the past I used Matlab for plotting and really liked it. I was wandering if there is a module/package/extension which bring similar capability into Python.
To be more specific, I need piloting for 2 different reasons.
To understand how functions behave. Something quick and dirty would do it.
Publication/presentation. Ability to add labels, legend, grid, customise colour, axis properties etc.
Try matplotlib, it's pretty extensive and has a shell similar to MATLAB / Mathematica.
I suspect matplotlib.pyplot would be right up your alley.
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.
I need to plot some data in various forms. Currently I'm using Matplotlib and I'm fairly happy with the plots I'm able to produce.
This question is on how to plot the last one. The data is similar to the "distance table", like this (just bigger, my table is 128x128 and still have 3 or more number per element).
Now, my data is much better "structured" than a distance table (my data doesn't varies "randomly" like in a alphabetically sorted distance table), thus a 3D barchart, or maybe 3 of them, would be perfect. My understanding is that such a chart is missing in Matplotlib.
I could use a (colored) Countor3d like these or something in 2D like imshow, but it isn't really well representative of what the data is (the data has meaning just in my 128 points, there isn't anything between two points). And the height of bars is more readable than color, IMO.
Thus the questions:
is it possible to create 3D barchart in Matplotlib? It should be clear that I mean with a 2D domain, not just a 2D barchart with a "fake" 3D rendering for aesthetics purposes
if the answer to the previous question is no, then is there some other library able to do that? I strongly prefer something Python-based, but I'm OK with other Linux-friendly possibilities
if the answer to the previous question is no, then do you have any suggestions on how to show that data? E.g. create a table with the values, superimposed to the imshow or other colored way?
For some time now, matplotlib had no 3D support, but it has been added back recently. You will need to use the svn version, since no release has been made since, and the documentation is a little sparse (see examples/mplot3d/demo.py). I don't know if mplot3d supports real 3D bar charts, but one of the demos looks a little like it could be extended to something like that.
Edit: The source code for the demo is in the examples but for some reason the result is not. I mean the test_polys function, and here's how it looks like:
example figure http://www.iki.fi/jks/tmp/poly3d.png
The test_bar2D function would be even better, but it's commented out in the demo as it causes an error with the current svn version. Might be some trivial problem, or something that's harder to fix.
MyavaVi2 can make 3D barcharts (scroll down a bit). Once you have MayaVi/VTK/ETS/etc. installed it all works beautifully, but it can be some work getting it all installed. Ubuntu has all of it packaged, but they're the only Linux distribution I know that does.
One more possibility is Gnuplot, which can draw some kind of pseudo 3D bar charts, and gnuplot.py allows interfacing to Gnuplot from Python. I have not tried it myself, though.
This is my code for a simple Bar-3d using matplotlib.
import mpl_toolkits
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
%matplotlib inline
## The value you want to plot
zval=[0.020752244,0.078514652,0.170302899,0.29543857,0.45358061,0.021255922,0.079022499,\
0.171294169,0.29749654,0.457114286,0.020009631,0.073154019,0.158043498,0.273889264,0.419618287]
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111,projection='3d')
col=["#ccebc5","#b3cde3","#fbb4ae"]*5
xpos=[1,2,3]*5
ypos=range(1,6,1)*5
zpos=[0]*15
dx=[0.4]*15
dy=[0.5]*15
dz=zval
for i in range(0,15,1):
ax.bar3d(ypos[i], xpos[i], zpos[i], dx[i], dy[i], dz[i],
color=col[i],alpha=0.75)
ax.view_init(azim=120)
plt.show()
http://i8.tietuku.com/ea79b55837914ab2.png
You might check out Chart Director:
http://www.advsofteng.com
It has a pretty wide variety of charts and graphs and has a nice Python (and several other languages) API.
There are two editions: The free version puts a blurb on the generated image, and the
pay version is pretty reasonably priced.
Here's one of the more interesting looking 3d stacked bar charts:
(source: advsofteng.com)