问题
How can I add statistical graphics (plots, axes, charts, etc) to an existing graphic implemented in python-igraph? I'm particularly interested in matplotlib because I have experience with this library.
In my case igraph is being used for the different layout options. My current network has the x dimension partially constrained while y is changed by the layout. I would like to add a bar chart along the bottom of the diagram to list frequencies of the value tied to the x coordinate of the network nodes.
(I'm not using scipy/ipython/pandas libraries, not yet anyway)
回答1:
This is not a complete answer, but it is too long to post as a comment, so I'm posting it as an answer instead. Feel free to extend/edit it.
I have experimented with combining python-igraph
and matplotlib
a while ago (well, more than five years ago), and the general conclusion was that combining the two is possible, but in a fairly convoluted way.
First of all, the combination would work only if you are using Cairo as the drawing backend for matplotlib
, because python-igraph
uses Cairo as the graph drawing backend (and it does not support any other drawing backend).
Next, the key trick is that you can extract the Cairo surface of a Matplotlib figure somehow, and then pass this surface to igraph's plot()
function as the drawing target - in this case, igraph will not create a separate figure but just start drawing on the given surface. However, at the time when I was experimenting with this, there was no public API in Matplotlib to extract the Cairo surface from a figure, so I had to resort to undocumented Matplotlib properties and functions, so the whole thing was very fragile and it depended heavily on the specific version of Matplotlib that I have used - but it worked.
The whole process is summarized in this thread on the igraph-help
mailing list. In the thread, I have provided the following Python script as a proof-of-concept, which I am copying it here for sake of completeness:
from matplotlib.artist import Artist
from igraph import BoundingBox, Graph, palettes
class GraphArtist(Artist):
"""Matplotlib artist class that draws igraph graphs.
Only Cairo-based backends are supported.
"""
def __init__(self, graph, bbox, palette=None, *args, **kwds):
"""Constructs a graph artist that draws the given graph within
the given bounding box.
`graph` must be an instance of `igraph.Graph`.
`bbox` must either be an instance of `igraph.drawing.BoundingBox`
or a 4-tuple (`left`, `top`, `width`, `height`). The tuple
will be passed on to the constructor of `BoundingBox`.
`palette` is an igraph palette that is used to transform
numeric color IDs to RGB values. If `None`, a default grayscale
palette is used from igraph.
All the remaining positional and keyword arguments are passed
on intact to `igraph.Graph.__plot__`.
"""
Artist.__init__(self)
if not isinstance(graph, Graph):
raise TypeError("expected igraph.Graph, got %r" % type(graph))
self.graph = graph
self.palette = palette or palettes["gray"]
self.bbox = BoundingBox(bbox)
self.args = args
self.kwds = kwds
def draw(self, renderer):
from matplotlib.backends.backend_cairo import RendererCairo
if not isinstance(renderer, RendererCairo):
raise TypeError("graph plotting is supported only on Cairo backends")
self.graph.__plot__(renderer.gc.ctx, self.bbox, self.palette, *self.args, **self.kwds)
def test():
import math
# Make Matplotlib use a Cairo backend
import matplotlib
matplotlib.use("cairo.pdf")
import matplotlib.pyplot as pyplot
# Create the figure
fig = pyplot.figure()
# Create a basic plot
axes = fig.add_subplot(111)
xs = range(200)
ys = [math.sin(x/10.) for x in xs]
axes.plot(xs, ys)
# Draw the graph over the plot
# Two points to note here:
# 1) we add the graph to the axes, not to the figure. This is because
# the axes are always drawn on top of everything in a matplotlib
# figure, and we want the graph to be on top of the axes.
# 2) we set the z-order of the graph to infinity to ensure that it is
# drawn above all the curves drawn by the axes object itself.
graph = Graph.GRG(100, 0.2)
graph_artist = GraphArtist(graph, (10, 10, 150, 150), layout="kk")
graph_artist.set_zorder(float('inf'))
axes.artists.append(graph_artist)
# Save the figure
fig.savefig("test.pdf")
print "Plot saved to test.pdf"
if __name__ == "__main__":
test()
A word of warning: I haven't tested the above code and I cannot test it now because I don't have Matplotlib on my machine right now. It used to work five years ago with the then-current Matplotlib version (0.99.3). It will probably not work now without major modifications, but it shows the general idea and hopefully it won't be too complicated to adapt.
If you have managed to make it work for you, feel free to edit my post.
来源:https://stackoverflow.com/questions/36135521/how-can-i-use-matplotlib-with-igraph