您正在寻找一种绘制分层图的算法。有许多不同的算法,您应该选择最适合您需求的算法(例如,查看 Gansner 等人的以下论文 A Technique for Drawing Directed Graphs。)。
其中许多算法已经在Graphviz(一个非常著名且功能强大的图形可视化软件)中实现。安装后,计算您正在寻找的结果非常简单(G 是您使用 networkx.DiGraph 构建的有向无环图):
from networkx.drawing.nx_agraph import graphviz_layout
def get_distance_nodes_map(G):
pos = graphviz_layout(G, prog='dot')
coor = sorted({y for k, (x, y) in pos.items()})
kmap = dict(zip(coor, range(len(coor))))
distance_nodes_map = {level: set() for level in kmap.values()}
for k, (x, y) in pos.items():
distance_nodes_map[kmap[y]].add(k)
return distance_nodes_map
以下是使用您提供的数据的几个示例:
>>> from networkx import DiGraph
>>> from pprint import PrettyPrinter
>>> pp = PrettyPrinter()
>>> G1 = DiGraph()
>>> G1.add_edges_from([('super', 'high-x'), ('high-x', 'mid-p'),
... ('mid-p', 'low-b'), ('mid-p', 'low-c'),
... ('low-c', 'base-zero'), ('low-c', 'base-one'),
... ('high-y', 'mid-p'), ('high-y', 'base-zero'),
... ('high-z', 'base-one'), ('high-z', 'mid-r'),
... ('high-z', 'mid-q'), ('mid-q', 'low-a'),
... ('low-a', 'base-one')])
>>> pp.pprint(get_distance_nodes_map(G1))
{0: {'base-one', 'base-zero'},
1: {'low-a', 'low-b', 'low-c'},
2: {'mid-p', 'mid-r', 'mid-q'},
3: {'high-y', 'high-x', 'high-z'},
4: {'super'}}
>>> G2 = DiGraph()
>>> G2.add_edges_from([('n10', 'n11'), ('n11', 'n12'), ('n12', 'n13'),
... ('n13', 'n14'), ('n20', 'n14'), ('n20', 'n21'),
... ('n21', 'n22'), ('n22', 'n23'), ('n30', 'n23'),
... ('n30', 'n31'), ('n31', 'n32')])
>>> pp.pprint(get_distance_nodes_map(G2))
{0: {'n32'},
1: {'n31', 'n23'},
2: {'n30', 'n22'},
3: {'n21', 'n14'},
4: {'n13', 'n20'},
5: {'n12'},
6: {'n11'},
7: {'n10'}}