Pygraphviz / NetworkX设置节点层级或层次

7
我有一个数据集,表示一种家谱树。每个节点都有两个父母(除了第一代,他们没有父母)。对于给定的节点,它的父母可以来自任何以前的一代。例如,第n代中的一个节点可以有一个来自n-1代的父亲和另一个来自n-5代的父亲。一个节点可以是多个其他节点的父节点。
因此,对于每个节点,我知道它的代数和它的父母。
我正在尝试表示这个图形,将相同代数的节点放在同一行。每一代都有10个节点,除了第一代。
到目前为止,我正在尝试“点”布局。当我只输入两个世代时,它做得很好,但当我输入三个世代时,它“过于聪明”,并以某种方式重新排列节点。
例如,以下数据表示3个世代:
[(ObjectId('530b2ad783a1a15e695c9711'), ObjectId('530b804883a1a15e695c972b')), (ObjectId('530b2ad783a1a15e695c971c'), ObjectId('530b804883a1a15e695c972b')), (ObjectId('530b2ad783a1a15e695c9709'), ObjectId('530b804883a1a15e695c9727')), (ObjectId('530b2ad783a1a15e695c970a'), ObjectId('530b804883a1a15e695c9727')), (ObjectId('530b2ad783a1a15e695c970b'), ObjectId('530b804883a1a15e695c9724')), (ObjectId('530b2ad783a1a15e695c970f'), ObjectId('530b804883a1a15e695c9724')), (ObjectId('530b2ad783a1a15e695c9714'), ObjectId('530b804883a1a15e695c9729')), (ObjectId('530b2ad783a1a15e695c970e'), ObjectId('530b804883a1a15e695c9729')), (ObjectId('530b2ad783a1a15e695c9711'), ObjectId('530b804883a1a15e695c9723')), (ObjectId('530b2ad783a1a15e695c970e'), ObjectId('530b804883a1a15e695c9723')), (ObjectId('530b2ad783a1a15e695c971c'), ObjectId('530b804883a1a15e695c9728')), (ObjectId('530b2ad783a1a15e695c9719'), ObjectId('530b804883a1a15e695c9728')), (ObjectId('530b2ad783a1a15e695c9714'), ObjectId('530b804883a1a15e695c9726')), (ObjectId('530b2ad783a1a15e695c9713'), ObjectId('530b804883a1a15e695c9726')), (ObjectId('530b2ad783a1a15e695c9716'), ObjectId('530b804883a1a15e695c9722')), (ObjectId('530b2ad783a1a15e695c9719'), ObjectId('530b804883a1a15e695c9722')), (ObjectId('530b2ad783a1a15e695c970d'), ObjectId('530b804883a1a15e695c9725')), (ObjectId('530b2ad783a1a15e695c9715'), ObjectId('530b804883a1a15e695c9725')), (ObjectId('530b804883a1a15e695c9724'), ObjectId('530ba27c83a1a15e695c972d')), (ObjectId('530b2ad783a1a15e695c9713'), ObjectId('530ba27c83a1a15e695c972d')), (ObjectId('530b804883a1a15e695c9724'), ObjectId('530ba27c83a1a15e695c972e')), (ObjectId('530b2ad783a1a15e695c9709'), ObjectId('530ba27c83a1a15e695c972e')), (ObjectId('530b804883a1a15e695c9727'), ObjectId('530ba27c83a1a15e695c972f')), (ObjectId('530b2ad783a1a15e695c9709'), ObjectId('530ba27c83a1a15e695c972f')), (ObjectId('530b2ad783a1a15e695c9713'), ObjectId('530ba27c83a1a15e695c9730')), (ObjectId('530b2ad783a1a15e695c9709'), ObjectId('530ba27c83a1a15e695c9730')), (ObjectId('530b2ad783a1a15e695c9713'), ObjectId('530ba27c83a1a15e695c9731')), (ObjectId('530b804883a1a15e695c9725'), ObjectId('530ba27c83a1a15e695c9731')), (ObjectId('530b804883a1a15e695c9722'), ObjectId('530ba27c83a1a15e695c9732')), (ObjectId('530b2ad783

产生的结果:enter image description here

所有没有接收边缘的“根”节点都应该放在第一行,但实际上它把其中一些节点放在了第二层。

当我尝试制作更大的图表时,例如包含10代的图表,整个层级结构就毫无意义了。

是否有任何参数或方法来指定节点的级别或层?在我提供的数据中没有这些信息,但我可以轻松地生成它,问题是我不知道如何将那些信息发送到pygraphviz或networkx。

我还想使用此数据生成类似于http://networkx.github.io/documentation/latest/examples/drawing/lanl_routes.html这样的twopi图表,其中各层以圆形方式表示。

3个回答

9

使用Graphviz子图,属性为rank=same。

例如:

import networkx as nx
import pygraphviz as pgv # pygraphviz should be available

G = nx.DiGraph()
G.add_edge('a','aa')
G.add_edge('a','ab')
G.add_edge('a','bbc')
G.add_edge('b','ab')
G.add_edge('b','bb')
G.add_edge('c','bbc')
G.add_edge('bb','bba')
G.add_edge('bb','bbc')
A = nx.to_agraph(G)
one = A.add_subgraph(['a','b','c'],rank='same')
two = A.add_subgraph(['aa','ab','bb'],rank='same')
three = A.add_subgraph(['bba','bbc'],rank='same')
A.draw('example.png', prog='dot')

enter image description here


谢谢,这个很好用。我看到的问题是,“pygraphviz”做出来的图比networkx丑(即使networkx使用graphviz制作),而且更难指定节点大小与边相比。 - Dr Sokoban
如果一个子图中只有部分节点共享相同的排名,该怎么办? - W.Z.Hai

5

自2017年起,函数to_agraph不再在nx.level中公开。 现在您必须调用nx.nx_agraph.to_agraph()


1

这个链接会对你有所帮助。rank_same.py

import graphviz

d = graphviz.Digraph(filename='rank_same.gv')

with d.subgraph() as s:
    s.attr(rank='same')
    s.node('A')
    s.node('X')

d.node('C')

with d.subgraph() as s:
    s.attr(rank='same')
    s.node('B')
    s.node('D')
    s.node('Y')

d.edges(['AB', 'AC', 'CD', 'XY'])

d.view()

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接