Graphviz/PyGraphviz中有向图的NetworkX风格弹簧模型布局

Graphviz/PyGraphviz中有向图的NetworkX风格弹簧模型布局,graph,visualization,graphviz,networkx,pygraphviz,Graph,Visualization,Graphviz,Networkx,Pygraphviz,NetworkX主要用于图形分析,PyGraphviz主要用于绘图,它们被设计为协同工作。然而,NetworkX的图形绘制(通过MatPlotLib)至少在一个方面优于PyGraphviz的图形绘制(通过Graphviz),即NetworkX有一个专门针对有向图的spring布局算法(可通过spring\u布局函数访问),而PyGraphviz有几个spring布局算法(可通过neato程序和其他程序访问)将有向图作为无向图进行布局。唯一真正处理图中方向的Graphviz/PyGraphviz布

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

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

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:()

2_networkx_undirected.png:()

3_pygraphviz_directed.png:()

4_pygraphviz_undirected.png:()

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

反复运行上面的代码表明这不是偶然发生的。NetworkX的
spring\u layout
函数显然是基于这样的假设编写的:如果从一个节点到另一个节点有一条弧,那么第二个节点应该比第一个节点更靠近图的中心(即,如果
edgelist
中描述的图是有方向的,那么节点2应该比节点1和3更接近节点9,节点6应该比节点4更接近节点9,节点8应该比节点7更接近节点9;这并不总是像我们在上图中从节点4和5看到的那样完美工作,但这是一个小问题与2和9都靠近中心相比,我认为“错误”非常轻微)。换句话说,NetworkX的
spring\u布局既有层次性又有强制导向性

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

是否可以使用PyGraphviz/Graphviz复制

不幸的是,NetworkX的
spring\u layout
(实际上是
fruchterman\u reingold\u layout
)函数和注释后的for NetworkX没有提供任何线索,说明NetworkX为什么会产生这样的结果

这是使用PyGraphviz使用NetworkX
spring_layout
函数绘制网络的结果(请参见下面我自己对这个问题的回答)。 5_pygraphviz_plus_networkx.png:
()

好的,我想我已经弄明白了,所以我要回答我自己的问题。我认为PyGraphviz本身无法完成。但是,可以指示PyGraphviz从NetworkX获取节点位置,但要将它们固定(使用
)这样,
neato
程序就不能实际执行任何操作,只能对由
spring\u 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的答案,因为尽管它不能解决问题,但它对理解潜在问题是一个非常有用的贡献。

Graphviz也有一个fdpsfdp布局模式,用于执行节点的强制定向放置,类似于spring布局。我不熟悉NetworkX,但它似乎是
gvu.layout(prog='fdp')
是否可行?如果NetworkX允许向Graphviz传递其他参数,则可以调整许多可配置的布局参数,使布局更接近所需布局。请参阅Graphviz文档:

然而,fdp布局将网络视为无向图。我知道的大多数“spring”布局也将网络视为无向图,因为它们必须将网络转换为欧几里德空间(屏幕)其中距离是对称的。一个例外是“磁性”弹簧布局,它也尝试对齐圆弧,使它们指向相似的方向,以传达层次结构,作为排序neato/点混合


算法实现在如何将有向网络中的网络距离转换为无向权重/距离方面也可能有所不同。如果您想更好地控制有向圆弧的解释方式,您可能希望自己明确执行此步骤。

感谢您解释为什么大多数spring布局都会网络是无向的,但您提供的解决方案(即使用
fdp
s