使用Python3中的NetworkX创建曲线边界

22

我想使用networkx(如果您知道更好的框架,我也可以考虑其他框架)来创建一个节点位置固定且边不重叠的图。

我的以前的代码看起来像这样:

#!/usr/bin/env python3

import networkx as nx
import matplotlib.pyplot as plt

# Graph data
names = ['A', 'B', 'C', 'D', 'E']
positions = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1, 1)]
edges = [('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('D', 'A')]

# Matplotlib figure
plt.figure('My graph problem')

# Create graph
G = nx.MultiDiGraph(format='png', directed=True)

for index, name in enumerate(names):
    G.add_node(name, pos=positions[index])

labels = {}
for edge in edges:
    G.add_edge(edge[0], edge[1])
    labels[(edge[0], edge[1])] = '{} -> {}'.format(edge[0], edge[1])

layout = dict((n, G.node[n]["pos"]) for n in G.nodes())
nx.draw(G, pos=layout, with_labels=True, node_size=300)
nx.draw_networkx_edge_labels(G, layout, edge_labels=labels)

plt.show()

并给出以下结果

在这里输入图片描述

我如何确保边缘是“圆形”的,以便它们不重叠?


抱歉,无法提供帮助;您的代码与以下setup.py导致了segfault。cairocffi==0.9.0 cffi==1.11.5 cycler==0.10.0 decorator==4.3.0 kiwisolver==1.0.1 matplotlib==3.0.0 networkx==2.2 numpy==1.15.2 pgi==0.0.11.2 pycparser==2.19 pyparsing==2.2.2 python-dateutil==2.7.3 six==1.11.0你能分享一下你的吗? - Aiyion.Prime
只需执行 pip install networkx && pip install matplotlib - g3n35i5
谢谢,我想你是好心。但这不是我需要的。我之前尝试了你建议的方法,但缺少几个依赖项。在安装它们后,我遇到了段错误。 这不是我想要调试的东西,所以如果你能够复制你正在成功使用的setup.py文件,有人可能会帮助你而不会遇到和我一样的问题。顺便问一下,你使用的是哪个具体的Python版本?3.5、3.6还是3.7? - Aiyion.Prime
抱歉,我可能误解了问题。我没有setup.py文件,但是代码在Python 3.6.6下在虚拟环境中运行没有任何问题。以下步骤的设置适用于我:mkdir graph_stack_tmp python3 -m venv graph_stack_tmp cd graph_stack_tmp source bin/activate pip install networkx matplotlib <现在执行我的代码> - g3n35i5
1
据我所知,目前使用networkx无法绘制带有曲线边缘的图形,尽管修改源代码以实现这一点并不太困难。networkxdraw_networkx_edges中使用matplotlib的FancyArrowPatch类来绘制箭头(由draw包装)。FancyArrowPatch支持一个connectionstyle参数,但draw_networkx_edges没有设置它;默认是一条直线,这就是你目前得到的结果。鉴于您提供的MWE的质量,我猜测您将能够克隆networkx的github存储库,并修补draw_networkx_edges - Paul Brodersen
显示剩余2条评论
4个回答

31
在其他 NetworkX 新闻中,你现在可以为 `nx.draw_networkx_edges` 指定一个 `connectionstyle` 参数。例如,如果我想要一个带有曲线边缘的网络,我可以编写:
# Compute position of nodes
pos = nx.kamada_kawai_layout(G)

# Draw nodes and edges
nx.draw_networkx_nodes(G, pos)
nx.draw_networkx_edges(
    G, pos,
    connectionstyle="arc3,rad=0.1"  # <-- THIS IS IT
)
为了使边缘更加圆润,只需增加 "rad=x" 中的 x 值。
注意:该代码不会产生所有颜色和箭头的图形,需要更多的代码来实现。

2
我还应该说的是,由于某种原因,这似乎只在边缘有方向时才有效。这肯定是一个 bug,并且可能会在一些体面的人提出问题并在 https://github.com/networkx/networkx 上做出请求时得到解决。 - Ulf Aslak
如果G是有向的(至少在v.2.5中),它也适用于G.draw - arash
这个代码片段在我使用一个无向多图G时无法正常工作。 - Sinusx
@Sinusx提供更多信息。你收到了什么错误消息?你正在使用哪个版本的networkx? - Ulf Aslak

5

图的边缘不应该重叠

使用connectionstyle参数绘制具有预定曲率的弧线,不能减少边缘与节点边缘的重叠。

对于直线边缘,与其他绘图元素的重叠通常是不可避免的。但是,如果我们可以绘制曲线边缘,就可以使每个边缘避开其他节点和边缘。这可以通过在每个边缘中插入控制点来实现,从而将每个边缘分割成短的子边缘。然后,我们可以使用标准的Fruchterman-Reingold算法(在networkx中称为spring_layout)将图形模拟为弹簧系统,其中所有节点和控制点相互斥,但连接的节点和控制点也相互吸引。保持(原始)节点的位置固定,同时让边缘控制点的位置退火到平衡位置。

我有一个实现这种方法的代码示例。该代码是Python网络绘图库netgraph的一部分,我是其作者。netgraph与networkx和igraph Graph对象完全兼容,因此生成外观良好的图形应该很容易也很快。

enter image description here

#!/usr/bin/env python
import matplotlib.pyplot as plt
import networkx as nx

from netgraph import Graph # pip install netgraph

g = nx.florentine_families_graph()
Graph(g, edge_layout='curved')
plt.show()

2
很棒的库!精美的文档。期待使用它。 - Ulf Aslak

4

我认为你不能直接使用networkx函数来完成这个任务。但是,你可以使用已经计算好的节点位置,直接使用matplotlib进行操作。

调整你的代码:

 import networkx as nx
import matplotlib.pyplot as plt

# Graph data
names = ['A', 'B', 'C', 'D', 'E']
positions = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1, 1)]
edges = [('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('D', 'A')]

# Matplotlib figure
plt.figure('My graph problem')

# Create graph
G = nx.MultiDiGraph(format='png', directed=True)

for index, name in enumerate(names):
    G.add_node(name, pos=positions[index])

labels = {}




layout = dict((n, G.node[n]["pos"]) for n in G.nodes())
nx.draw(G, pos=layout, with_labels=True, node_size=300)
ax = plt.gca()
for edge in edges:
    ax.annotate("",
                xy=layout[edge[0]], xycoords='data',
                xytext=layout[edge[1]], textcoords='data',
                arrowprops=dict(arrowstyle="->", color="0.5",
                                shrinkA=5, shrinkB=5,
                                patchA=None, patchB=None,
                                connectionstyle="arc3,rad=-0.3",
                                ),
                )
plt.show()

给出:

enter image description here

另请参见这里

刚试了一下Colab,结果发现MultiDiGraph的实现方式已经改变了。在以下这行代码中,我不得不使用nodes(复数)而不是nodelayout = dict((n, G.nodes[n]["pos"]) for n in G.nodes())(<-- 已经更正的代码行;当前测试的networkx版本:2.4)。 - Daniel B.

1
如Paul所述,现在有一个选项可以在draw_networkx_edges中使用FancyArrowPatch,但它只适用于有向图,并且速度非常慢。
值得一提的是,我打包了一些旧代码,使用bezier包从NetworkX图(或任何边缘列表)生成漂亮的曲线边缘并绘制它们。它可能很有用:https://github.com/beyondbeneath/bezier-curved-edges-networkx 样例图片,使用SNAP Facebook数据集和ForceAtlas2布局:

enter image description here


12
刚刚尝试将connectionstyle='arc3, rad=0.1'传递给nx.draw(),在一个较小的图上它非常适用,效果非常好。 - undercat

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