算法主要在这个函数中。这个算法也是从外网上抄的。
这个算法的输出是一个布局字典,画出图后是一个树。
例子:
布局字典通过一个数学方法处理后将其变成环状发散图。
def hierarchy_pos(self,G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
"""
分散树型画法
:param G:
:param root:
:param width:
:param vert_gap:
:param vert_loc:
:param xcenter:
:return:
"""
'''
From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
Licensed under Creative Commons Attribution-Share Alike
If the graph is a tree this will return the positions to plot this in a
hierarchical layout.
G: the graph (must be a tree)
root: the root node of current branch
- if the tree is directed and this is not given,
the root will be found and used
- if the tree is directed and this is given, then
the positions will be just for the descendants of this node.
- if the tree is undirected and not given,
then a random choice will be used.
width: horizontal space allocated for this branch - avoids overlap with other branches
vert_gap: gap between levels of hierarchy
vert_loc: vertical location of root
xcenter: horizontal location of root
'''
# if not nx.is_tree(G):
# raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
# for i in iter(nx.topological_sort(G)):
# print(i)
# print(nx.topological_sort(G))
root = next(iter(nx.topological_sort(G))) # allows back compatibility with nx version 1.11
else:
root = random.choice(list(G.nodes))
print(root)
def _hierarchy_pos(G, root, width=1, vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None):
'''
see hierarchy_pos docstring for most arguments
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch. - only affects it if non-directed
'''
if pos is None:
pos = {root: (xcenter, vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
print(pos)
children = list(G.neighbors(root))
if not isinstance(G, nx.DiGraph) and parent is not None:
children.remove(parent)
if len(children) != 0:
dx = width / len(children)
#原 nextx = xcenter - width / 2 - dx /4
nextx = xcenter - width / 2 - dx /2
for child in children:
nextx += dx
#xcenter水平
#vert_loc竖直
pos = _hierarchy_pos(G, child, width=dx, vert_gap=vert_gap,
vert_loc=vert_loc - vert_gap, xcenter=nextx,
pos=pos, parent=root)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
这是源地址:可以使用python 3从networkx获取分层图吗?- 堆栈溢出 (stackoverflow.com)
这个函数是一个布局函数。也就是说要先将图的节点和边都添加进去,再把图交给这个函数处理。
1.newwork布局逻辑
为了解释这个函数,首先要解释newwork的布局逻辑:
network的节点布局的底层很简单,整个图上的节点位置用一个字典表示。
字典中项的格式为:
key:点的名字(注意在network中点的名字是add_node()时设置的名字,在图上显示时默认显示点的label的名字。但如果没有设置label,label默认为点的名字。当然,你可以在draw()时用 with_labels设定是否显示label)
value:value为一个2元元组。元组为点的坐标。(注意在newwork中的点的坐标是相对坐标,即在图中初始不存在坐标系,点的坐标虽然也是坐标系的形式,但更多的是表示点的相对位置,即如果点的相对位置没有变,点的坐标无论如何变(比如从(0,0)到(100,100)),对图像显示没有任何影响)
每个点占用一个键值对。用一个字典将每个点的位置表示出来。
{'32': (0.0, 0.0), '3203': (0.1414213562373095, 0.14142135623730953), '3204': (-0.14142135623730953, 0.14142135623730953), '3201': (-0.14142135623730953, -0.14142135623730953), '3202': (0.1414213562373095, -0.14142135623730953)}
这个字典其实就是draw中的pos。把这个字典穿给draw()或其他draw系列的函数就可以画出图了。
2.算法原理
下面是对这个算法的分析。
2.1hierarchy_pos函数
hierarchy_pos函数的输入有这些东西。
G:图
root:根节点的位置
width:分配给这个分支的水平空间——避免与其他分支重叠。(即父节点的子节点的这一行所占的水平宽度)
vert_gap:父节点和子节点之间的竖直距离。
vert_loc:根节点的竖直位置
xcenter:根节点的水平位置
hierarchy_pos的作用:如果图是有向图且没有给root,会自动找到root。如果是无向图且没有给root,会随机选择一个点作为root。选定root后调用_hierarchy_pos()函数。
hierarchy_pos函数的输出:图的布局字典。
2.2 _hierarchy_pos函数
这是个递归函数。
_hierarchy_pos()函数的输入有这些东西:
G,root,width,vert_gap,vert_loc,xcenter与上面一样
pos:图的布局字典,作为参数的原因是使函数在分配点的位置时都是对同一个字典修改
parent:告知当前root的父节点是哪个,主要用于无向图。
hierarchy_pos函数的输出:图的布局字典。
3._hierarchy_pos函数的作用详解
第一步:
先定下根节点的坐标
第二步:
找到根节点的所有子节点。注意:对于无向图,G.neighbors(root)是找所有和root相连的节点,这也是为什么要删去root的父节点。对于有向图,是找的被指向的节点。
第三步:
安排每个子节点的位置,dx是按子节点个数将width分开的一份的长度。
nextx是子节点开始的位置,从开始位置放一个加dx的码过去。
而水平位置为根节点位置减高度差。
然后不断的递归。
4.数学处理
由于我的width设置为2pi。 所以我在下一行用cos转化时,生成的位置一定是在根节点的周围一圈。
而乘r是为了将根节点周围的一圈的点扩散出去。这样就完成了发散,并且每次扩散都是相对于父节点的扩散。当然,由于乘的特殊性,不要把根节点y坐标设为在减几次(比树的级数小)vert_gap后会到0的值。
显示个处理结果:
当然这个算法有很多可以改进的地方,很多地方可以设置为手动设置,并以此建立一个图谱绘制程序。