可以使用python 3从networkx获取分层图吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29586520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 04:45:34  来源:igfitidea点击:

Can one get hierarchical graphs from networkx with python 3?

pythonnetworkxpygraphviz

提问by NickDanger66

I am trying to display a tree graph of my class hierarchy using networkx.I have it all graphed correctly, and it displays fine. But as a circular graph with crossing edges, it is a pure hierarchy, and it seems I ought to be able to display it as a tree.

我试图显示我的类层次结构的树图,我networkx.已经正确地绘制了它,并且显示正常。但是作为一个有交叉边的圆形图,它是一个纯粹的层次结构,看起来我应该可以将它显示为树。

I have googled this extensively, and every solution offered involves using pygraphviz... but PyGraphviz does not work with Python 3 (documentation from the pygraphviz site).

我已经广泛地搜索了这个,并且提供的每个解决方案都涉及使用pygraphviz... 但是PyGraphviz 不适用于 Python 3(来自 pygraphviz 站点的文档)

Has anyone been able to get a tree graph display in Python 3?

有没有人能够在 Python 3 中获得树形图显示?

采纳答案by Joel

[scroll down a bit to see what kind of output the code produces]

[向下滚动一点,看看代码产生什么样的输出]

edit (7 Nov 2019)I've put a more refined version of this into a package I've been writing: https://epidemicsonnetworks.readthedocs.io/en/latest/_modules/EoN/auxiliary.html#hierarchy_pos. The main difference between the code here and the version there is that the code here gives all children of a given node the same horizontal space, while the code following that link also considers how many descendants a node has when deciding how much space to allocate it.

编辑(2019 年 11 月 7 日)我已将更完善的版本放入我一直在编写的包中:https: //epidemicsonnetworks.readthedocs.io/en/latest/_modules/EoN/auxiliary.html#hierarchy_pos。这里的代码和那里的版本之间的主要区别在于,这里的代码为给定节点的所有子节点提供了相同的水平空间,而该链接后面的代码在决定分配多少空间时还会考虑节点有多少后代.

edit (19 Jan 2019)I have updated the code to be more robust: It now works for directed and undirected graphs without any modification, no longer requires the user to specify the root, and it tests that the graph is a tree before it runs (without the test it would have infinite recursion - see user2479115's answer for a way to handle non-trees).

编辑(2019 年 1 月 19 日)我更新了代码以使其更加健壮:它现在无需任何修改即可用于有向图和无向图,不再需要用户指定根,并且在运行之前测试该图是否为树(如果没有测试,它将具有无限递归 - 有关处理非树的方法,请参阅 user2479115 的答案)。

edit (27 Aug 2018)If you want to create a plot with the nodes appearing as rings around the root node, the code right at the bottom shows a simple modification to do this

编辑(2018 年 8 月 27 日)如果要创建一个节点显示为围绕根节点的环的图,底部右侧的代码显示了一个简单的修改来执行此操作

edit (17 Sept 2017)I believe the trouble with pygraphviz that OP was having should be fixed by now. So pygraphviz is likely to be a better solution that what I've got below.

编辑(2017 年 9 月 17 日)我相信现在应该解决 OP 遇到的 pygraphviz 问题。所以 pygraphviz 可能是比我在下面得到的更好的解决方案。



Here is a simple recursive program to define the positions. The recursion happens in _hierarchy_pos, which is called by hierarchy_pos. The main role of hierarcy_posis to do a bit of testing to make sure the graph is appropriate before entering the recursion:

这是一个定义位置的简单递归程序。递归发生在 中_hierarchy_pos,由 调用hierarchy_pos。的主要作用hierarcy_pos是在进入递归之前做一些测试以确保图是合适的:

import networkx as nx
import random


def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):

    '''
    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):
            root = next(iter(nx.topological_sort(G)))  #allows back compatibility with nx version 1.11
        else:
            root = random.choice(list(G.nodes))

    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)
        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/2
            for child in children:
                nextx += dx
                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)

and an example usage:

和一个示例用法:

import matplotlib.pyplot as plt
import networkx as nx
G=nx.Graph()
G.add_edges_from([(1,2), (1,3), (1,4), (2,5), (2,6), (2,7), (3,8), (3,9), (4,10),
                  (5,11), (5,12), (6,13)])
pos = hierarchy_pos(G,1)    
nx.draw(G, pos=pos, with_labels=True)
plt.savefig('hierarchy.png')

enter image description here

在此处输入图片说明

Ideally this should rescale the horizontal separation based on how wide things will be beneath it. I'm not attempting that now.

理想情况下,这应该根据其下方的事物的宽度重新调整水平间距。我现在不尝试。

Radial expansion

径向膨胀

Let's say you want the plot to look like:

假设您希望情节如下所示:

enter image description here

在此处输入图片说明

Here's the code for that:

这是代码:

pos = hierarchy_pos(G, 0, width = 2*math.pi, xcenter=0)
new_pos = {u:(r*math.cos(theta),r*math.sin(theta)) for u, (theta, r) in pos.items()}
nx.draw(G, pos=new_pos, node_size = 50)
nx.draw_networkx_nodes(G, pos=new_pos, nodelist = [0], node_color = 'blue', node_size = 200)


edit- thanks to Deepak Saini for noting an error that used to appear in directed graphs

编辑- 感谢 Deepak Saini 指出曾经出现在有向图中的错误

回答by user2479115

I modified slightly so that it would not infinitely recurse.

我稍微修改了一下,这样它就不会无限递归了。

import networkx as nx

def hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5 ):
    '''If there is a cycle that is reachable from root, then result will not be a hierarchy.

       G: the graph
       root: the root node of current branch
       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
    '''

    def h_recur(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, 
                  pos = None, parent = None, parsed = [] ):
        if(root not in parsed):
            parsed.append(root)
            if pos == None:
                pos = {root:(xcenter,vert_loc)}
            else:
                pos[root] = (xcenter, vert_loc)
            neighbors = G.neighbors(root)
            if parent != None:
                neighbors.remove(parent)
            if len(neighbors)!=0:
                dx = width/len(neighbors) 
                nextx = xcenter - width/2 - dx/2
                for neighbor in neighbors:
                    nextx += dx
                    pos = h_recur(G,neighbor, width = dx, vert_gap = vert_gap, 
                                        vert_loc = vert_loc-vert_gap, xcenter=nextx, pos=pos, 
                                        parent = root, parsed = parsed)
        return pos

    return h_recur(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5)

回答by Deepak Saini

For a directed graph, Since neighbors(x) include only the succesors(x), so you have to remove the lines:

对于有向图,由于neighbors(x) 仅包含succesors(x),因此您必须删除这些线:

if parent != None:
        neighbors.remove(parent)

Also, a better option would be this:

此外,更好的选择是:

pos=nx.graphviz_layout(G,prog='dot')

回答by Westcroft_to_Apse

The simplest way to get a nice-looking tree graph display in Python 2 or 3 without PyGraphviz is to use PyDot (https://pypi.python.org/pypi/pydot). Whereas PyGraphviz provides an interface to the whole of Graphviz, PyDot only provides an interface to Graphviz's Dot tool, which is the only one you need if what you're after is a hierarchical graph / a tree. If you want to create your graph in NetworkX rather than PyDot, you can use NetworkX to export a PyDot graph, as in the following:

在没有 PyGraphviz 的情况下,在 Python 2 或 3 中获得漂亮的树形图显示的最简单方法是使用 PyDot ( https://pypi.python.org/pypi/pydot)。PyGraphviz 提供了一个到整个 Graphviz 的接口,而 PyDot 只提供了一个到 Graphviz 的 Dot 工具的接口,如果你想要的是一个层次图/一棵树,这是你唯一需要的。如果要在 NetworkX 而不是 PyDot 中创建图形,可以使用 NetworkX 导出 PyDot 图,如下所示:

import networkx as nx

g=nx.DiGraph()
g.add_edges_from([(1,2), (1,3), (1,4), (2,5), (2,6), (2,7), (3,8), (3,9),
                  (4,10), (5,11), (5,12), (6,13)])
p=nx.drawing.nx_pydot.to_pydot(g)
p.write_png('example.png')

Note that Graphviz and PyDot need to be installed for the above to work correctly.

请注意,需要安装 Graphviz 和 PyDot 才能使上述内容正常工作。

enter image description here

在此处输入图片说明

Warning: I have experienced problems when using PyDot to draw graphs with node attribute dictionaries exported from NetworkX - sometimes the dictionaries seem to be exported with quotation marks missing from strings, which causes the writemethod to crash. This can be avoided by leaving out the dictionaries.

警告:我在使用 PyDot 绘制带有从 NetworkX 导出的节点属性词典的图形时遇到问题 - 有时导出的词典似乎在字符串中缺少引号,这会导致write方法崩溃。这可以通过省略字典来避免。

回答by burubum

Here is a solution for large trees. It is a modification of Joel's recursive approach that evenly spaces nodes at each level.

这是大树的解决方案。它是对 Joel 递归方法的一种修改,该方法在每个级别均匀分布节点。

def hierarchy_pos(G, root, levels=None, width=1., height=1.):
    '''If there is a cycle that is reachable from root, then this will see infinite recursion.
       G: the graph
       root: the root node
       levels: a dictionary
               key: level number (starting from 0)
               value: number of nodes in this level
       width: horizontal space allocated for drawing
       height: vertical space allocated for drawing'''
    TOTAL = "total"
    CURRENT = "current"
    def make_levels(levels, node=root, currentLevel=0, parent=None):
        """Compute the number of nodes for each level
        """
        if not currentLevel in levels:
            levels[currentLevel] = {TOTAL : 0, CURRENT : 0}
        levels[currentLevel][TOTAL] += 1
        neighbors = G.neighbors(node)
        for neighbor in neighbors:
            if not neighbor == parent:
                levels =  make_levels(levels, neighbor, currentLevel + 1, node)
        return levels

    def make_pos(pos, node=root, currentLevel=0, parent=None, vert_loc=0):
        dx = 1/levels[currentLevel][TOTAL]
        left = dx/2
        pos[node] = ((left + dx*levels[currentLevel][CURRENT])*width, vert_loc)
        levels[currentLevel][CURRENT] += 1
        neighbors = G.neighbors(node)
        for neighbor in neighbors:
            if not neighbor == parent:
                pos = make_pos(pos, neighbor, currentLevel + 1, node, vert_loc-vert_gap)
        return pos
    if levels is None:
        levels = make_levels({})
    else:
        levels = {l:{TOTAL: levels[l], CURRENT:0} for l in levels}
    vert_gap = height / (max([l for l in levels])+1)
    return make_pos({})

Joel's example will look like this: enter image description here

Joel 的示例如下所示: 在此处输入图片说明

And this is a more complex graph (rendered using plotly):enter image description here

这是一个更复杂的图(使用 plotly 渲染):在此处输入图片说明