问题描述
如何向在python-igraph中实现的现有图形添加统计图形(图形,坐标轴,图表等)?我对matplotlib特别感兴趣,因为我有使用该库的经验.
在我的情况下,igraph用于不同的布局选项.我的当前网络的x尺寸受到部分约束,而y由布局更改.我想在图的底部添加一个条形图,以列出与网络节点的x坐标相关的值的频率.
(我还没有使用scipy/ipython/pandas库)
这不是一个完整的答案,但是作为注释发布太长了,因此我将其作为答案发布.随时扩展/编辑它.
我曾尝试过将python-igraph
和matplotlib
组合在一起(很久以前,五年多以前),并且总体结论是可以将两者组合在一起,但是以相当复杂的方式进行.
首先,仅当您将Cairo用作matplotlib
的图形后端时,该组合才有效,因为python-igraph
使用Cairo作为图形图形后端(并且它不支持任何其他图形后端). /p>
接下来,关键是要以某种方式提取Matplotlib图形的Cairo表面,然后将该表面传递给igraph的plot()
函数作为绘制目标-在这种情况下,igraph不会创建单独的图形,而是刚开始在给定的表面上绘制但是,当我进行此实验时,Matplotlib中没有公共API可以从图形中提取Cairo曲面,因此我不得不诉诸未记录的Matplotlib属性和函数,因此整个过程非常脆弱,并且取决于在很大程度上使用了我使用过的Matplotlib的特定版本-但却奏效了.
此主题中概述了整个过程igraph-help
邮件列表中的a>.在该线程中,我提供了以下Python脚本作为概念验证,出于完整性考虑,我将其复制到此处:
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()
一个警告:我还没有测试上面的代码,现在无法测试,因为我的机器上现在没有Matplotlib.它在五年前与当时的Matplotlib版本(0.99.3)一起使用.如果不进行重大修改,它现在可能无法正常工作,但是它显示了总体思路,希望它适应起来不会太复杂.
如果您设法使其适合您,请随时编辑我的信息.
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)
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.
这篇关于如何在igraph中使用matplotlib?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!