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
函数显然是假设如果从一个节点到另一个节点存在弧,则第二个节点应该比第一个节点更接近图的中心(即如果{{ 1}}被指示,节点2应该比节点1和3更接近节点9,节点6应该比节点4更接近节点9,并且节点8应该比节点7更接近节点9;这不是我们从上面第一个图中的节点4和5看到的总是完美地工作,但与在中心附近获得2和9以及从我的观点来看的“错误”非常轻微相比,这是一个小问题。换句话说,NetworkX的edgelist
既是分层的,也是强制导向的。
这是一个很好的功能,因为它使得核心/外围结构在有向图中变得更加明显(根据您正在使用的假设,没有入射弧的节点可以被认为是外围的一部分,即使它们是有大量的传出弧线)。 @skyebend在下面解释了为什么大多数布局算法将有向图看作是无向的,但上图显示(a)NetworkX以不同方式对待它们,以及(b)它以有原则的方式这样做有助于分析。
可以使用PyGraphviz / Graphviz复制吗?
不幸的是,对于NetworkX spring_layout
(实际上是spring_layout
)函数,documentation和注释source code没有提供关于NetworkX为什么会产生结果的线索。
这是使用PyGraphviz使用NetworkX fruchterman_reingold_layout
函数绘制网络的结果(请参阅下面我自己对此问题的回答)。
5_pygraphviz_plus_networkx.png:
(http://farm9.staticflickr.com/8378/8520231183_e7dfe21ab4.jpg)
答案 0 :(得分:5)
好吧,我想我已经明白了,所以我要回答我自己的问题。我不认为可以在PyGraphviz本身完成。但是,可以指示PyGraphviz从NetworkX获取节点位置但是将它们挂起(使用!
),以防止neato
程序实际执行任何操作,除了对由{{计算的节点位置进行橡皮图章标记1}}。将以下代码行添加到上面:
spring_layout
结果并不完美 - 我不得不将坐标乘以10以便阻止节点相互叠加,这显然是一个kludge - 但它是一个改进,即具有0 indegree的节点位于外部(使用NetworkX布局的好处)并且有适当的箭头不会被节点本身吞噬(使用PyGraphviz绘图的好处)。
我知道这不是我要求的严格要求(即使用PyGraphviz / Graphviz本身的解决方案)。
如果有人能提供更好的解决方案,我会很开心!
编辑:如上所述,没有人提供更好的解决问题的方案,所以我将接受我自己的答案,表明它确实有效。然而,我也投票赞成skyebend的答案,因为 - 虽然它没有解决问题 - 但它对理解潜在问题是一个非常有用的贡献。答案 1 :(得分:4)
Graphviz还具有 fdp 和 sfdp 布局模式,用于执行与弹簧布局类似的节点的强制定向放置。我不熟悉NetworkX,但似乎gvu.layout(prog='fdp')
可能有用吗?如果NetworkX允许将其他参数传递给Graphviz,则可以调整一些可配置的布局参数,这些参数可以为您提供更接近您想要的布局。请参阅Graphviz文档:http://www.graphviz.org/content/attrs
但是,fdp布局将网络视为无向图。我所知道的大多数“春天”布局也将网络视为无向,因为它们必须将它们转换为距离对称的欧几里德空间(屏幕)。一个例外是“磁性”弹簧布局,它也试图对齐弧线,使它们指向类似的方向以传达层次结构,作为一种排序的neato / dot混合。
算法实现方式也可能不同,它们如何将有向网络中的网络距离转换为无方向的权重/距离,以便通过布局进行优化。如果您想要更好地控制定向弧的解释方式,您可能希望自己明确地执行此步骤。