3

NetworkX 主要用于图形分析,PyGraphviz 主要用于绘图,它们旨在协同工作。然而,至少在一个方面,NetworkX 的图形绘制(通过 MatPlotLib)优于 PyGraphviz 的图形绘制(通过 Graphviz),即 NetworkX 具有spring_layout专门针对有向图的弹簧布局算法(可通过函数访问),而 PyGraphviz 有几个弹簧布局算法(可通过neato程序和其他访问)将有向图布局为无向图。唯一真正处理图形方向的 Graphviz / PyGraphviz 布局程序是dot,但dot创建分层布局,而不是强制导向布局。

下面是一个示例,它显示了 NetworkX 和 PyGraphviz 对于有向图的弹簧布局的区别:

import networkx as nx
import pygraphviz as pgv
import matplotlib.pyplot as ppt

edgelist = [(1,2),(1,9),(3,2),(3,9),(4,5),(4,6),(4,9),(5,9),(7,8),(7,9)]

nxd = nx.DiGraph()
nxu = nx.Graph()
gvd = pgv.AGraph(directed=True)
gvu = pgv.AGraph()

nxd.add_edges_from(edgelist)
nxu.add_edges_from(edgelist)
gvd.add_edges_from(edgelist)
gvu.add_edges_from(edgelist)

pos1 = nx.spring_layout(nxd)
nx.draw_networkx(nxd,pos1)
ppt.savefig('1_networkx_directed.png')
ppt.clf()

pos2 = nx.spring_layout(nxu)
nx.draw_networkx(nxu,pos2)
ppt.savefig('2_networkx_undirected.png')
ppt.clf()

gvd.layout(prog='neato')
gvd.draw('3_pygraphviz_directed.png')

gvu.layout(prog='neato')
gvu.draw('4_pygraphviz_undirected.png')

1_networkx_directed.png:(http://farm9.staticflickr.com/8516/8521343506_0c5d62e013.jpg

2_networkx_undirected.png:( http://farm9.staticflickr.com/8246/8521343490_06ba1ec8e7.jpg )

3_pygraphviz_directed.png:( http://farm9.staticflickr.com/8365/8520231171_ef7784d983.jpg )

4_pygraphviz_undirected.png:(http://farm9.staticflickr.com/8093/8520231231_80c7eab443.jpg

绘制的第三和第四图基本相同,只是箭头(整个图已经旋转,但除此之外没有区别)。然而,第一个和第二个图形的布局不同——不仅仅是因为 NetworkX 的布局算法引入了随机性元素。

反复运行上面的代码说明这不是偶然发生的。NetworkX 的spring_layout函数显然是基于以下假设编写的:如果从一个节点到另一个节点存在弧,则第二个节点应该比第一个节点更靠近图的中心(即,如果中描述的图edgelist是有向图,则节点 2 应该是比节点 1 和 3 更接近节点 9,节点 6 应该比节点 4 更接近节点 9,节点 8 应该比节点 7 更接近节点 9;正如我们从节点中看到的那样,这并不总是完美的4 和 5 在上面的第一个图中,但与将 2 和 9 放在中心附近相比,这是一个小问题,而且从我的角度来看,“错误”非常轻微)。换句话说,NetworkXspring_layout既是分层的又是强制导向的。

这是一个很好的功能,因为它使核心/外围结构在有向图中更加明显(根据您正在使用的假设,即使没有传入弧的节点也可以被认为是外围的一部分数量很大出弧)。@skyebend 在下面解释了为什么大多数布局算法将有向图视为无向图,但上图显示 (a) NetworkX 以不同的方式处理它们,以及 (b) 它以有助于分析的原则方式这样做。

这可以使用 PyGraphviz / Graphviz 复制吗?

不幸的是,NetworkX 的(实际上)函数的文档和注释源代码没有提供任何关于 NetworkX 产生结果的线索。spring_layoutfruchterman_reingold_layout

这是使用 PyGraphviz 使用 NetworkXspring_layout函数绘制网络的结果(请参阅下面我自己对这个问题的回答)。5_pygraphviz_plus_networkx.png:(http://farm9.staticflickr.com/8378/8520231183_e7dfe21ab4.jpg

4

2 回答 2

5

好的,我想我想通了,所以我要回答我自己的问题。我不认为它可以在 PyGraphviz 本身中完成。但是,可以指示 PyGraphviz 从 NetworkX 获取节点位置,但将它们固定(使用!),以便neato阻止程序实际执行任何操作,除了用橡皮图章计算的节点位置spring_layout。在上面添加以下代码行:

for k,v in pos1.iteritems():
    gvd.get_node(k).attr['pos']='{},{}!'.format(v[0]*10,v[1]*10)

gvd.layout(prog='neato')
gvd.draw('5_pygraphviz_plus_networkx.png')

结果并不完美——我不得不将坐标乘以 10 以阻止节点被绘制在彼此之上,这(显然)是一个杂凑——但这是一种改进,即节点与0 度在外面(使用 NetworkX 布局的好处)并且有适当的箭头不会被节点本身吞噬(使用 PyGraphviz 绘图的好处)。

不过,我知道这并不是我严格要求的(即使用 PyGraphviz / Graphviz 本身的解决方案)。

如果有人可以提供更好的解决方案,我会很高兴!

编辑:没有人为上述问题提供更好的解决方案,因此我将接受我自己的答案以表明它确实有效。但是,我也投票赞成 skyebend 的答案,因为 - 尽管它不能解决问题 - 它对理解潜在问题非常有用。

于 2013-02-06T17:15:14.973 回答
4

Graphviz 还具有fdpsfdp布局模式,用于强制定向放置节点,类似于弹簧布局。我不熟悉 NetworkX,但它似乎gvu.layout(prog='fdp')可以工作?如果 NetworkX 允许向 Graphviz 传递额外的参数,那么您可以调整许多可配置的布局参数,这可能会让您的布局更接近您想要的。请参阅 Graphviz 文档:http ://www.graphviz.org/content/attrs

但是,fdp 布局将网络视为无向图。我知道的大多数“弹簧”布局也将网络视为无向的,因为它们必须将它们转换为距离对称的欧几里得空间(屏幕)。一个例外是“磁性”弹簧布局,它也试图对齐弧线,因此它们指向相似的方向以传达层次结构,作为一种整洁/点混合。

算法实现也可能不同,它们如何将有向网络中的网络距离转换为无向权重/距离以通过布局进行优化。如果您想更好地控制有向弧的解释方式,您可能需要自己明确地执行此步骤。

于 2013-02-22T16:38:10.227 回答