Graphviz/PyGraphviz中有向图的NetworkX风格弹簧模型布局
NetworkX主要用于图形分析,PyGraphviz主要用于绘图,它们被设计为协同工作。然而,NetworkX的图形绘制(通过MatPlotLib)至少在一个方面优于PyGraphviz的图形绘制(通过Graphviz),即NetworkX有一个专门针对有向图的spring布局算法(可通过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布
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使用NetworkXspring_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也有一个fdp和sfdp布局模式,用于执行节点的强制定向放置,类似于spring布局。我不熟悉NetworkX,但它似乎是gvu.layout(prog='fdp')
是否可行?如果NetworkX允许向Graphviz传递其他参数,则可以调整许多可配置的布局参数,使布局更接近所需布局。请参阅Graphviz文档:
然而,fdp布局将网络视为无向图。我知道的大多数“spring”布局也将网络视为无向图,因为它们必须将网络转换为欧几里德空间(屏幕)其中距离是对称的。一个例外是“磁性”弹簧布局,它也尝试对齐圆弧,使它们指向相似的方向,以传达层次结构,作为排序neato/点混合
算法实现在如何将有向网络中的网络距离转换为无向权重/距离方面也可能有所不同。如果您想更好地控制有向圆弧的解释方式,您可能希望自己明确执行此步骤。感谢您解释为什么大多数spring布局都会网络是无向的,但您提供的解决方案(即使用fdp
或s