如何使用Matplotlib或Graphviz在NetworkX中绘制多重图

28

当我将多图 numpy 邻接矩阵传递给 networkx(使用 from_numpy_matrix 函数),然后尝试使用 matplotlib 绘制图形时,它会忽略多个边缘。

如何让它也绘制多个边缘?


1
相关问题:https://dev59.com/1Wkv5IYBdhLWcg3wvDXq 和 https://dev59.com/WGUp5IYBdhLWcg3wx5tu - 0 _
太遗憾了,networkx中没有实现这个功能! - famargar
5个回答

23
Graphviz在绘制平行边方面做得很好。您可以通过编写dot文件并使用Graphviz处理(例如下面的neato布局)来与NetworkX一起使用。除了NetworkX之外,您还需要pydot或pygraphviz。
In [1]: import networkx as nx

In [2]: G=nx.MultiGraph()

In [3]: G.add_edge(1,2)

In [4]: G.add_edge(1,2)

In [5]: nx.write_dot(G,'multi.dot')

In [6]: !neato -T png multi.dot > multi.png

enter image description here

在NetworkX 1.11及更高版本中,nx.write_dot不再像networkx github上的问题所述那样工作。解决方法是使用以下方式调用write_dotfrom networkx.drawing.nx_pydot import write_dot 或者 from networkx.drawing.nx_agraph import write_dot

你正在使用哪些版本的networkx、pygraphviz和graphviz? - o17t H1H' S'k
这是我所拥有的。但是最近版本应该会给出相同的结果。$ python -c "import pygraphviz; print pygraphviz.version" 1.2.dev1990$ dot -V dot - graphviz version 2.29.20120625.0446 (20120625.0446)$ python -c "import networkx; print networkx.version" 1.8.dev_20130108070258 - Aric
我写了相同的代码,使用neato生成图像,但它是一个有向图(而不是无向图),只显示一条边(1,2),但没有边(2,1)。为什么不是无向的????为什么没有另一条边??请帮忙! - Stefano C.
1
@Aric你知道是否可以将边缘标签和节点标签添加到dot图中吗?在我的情况下,我想为每条有向边都有一个不同的标签。 - davidA
3
2018年至今,这仍然是最好的方法吗?(我只关心最多有几十个节点的小图。) - Szabolcs

17

您可以直接使用计算出的节点位置来使用Matplotlib。

G=nx.MultiGraph ([(1,2),(1,2),(1,2),(3,1),(3,2)])
pos = nx.random_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = 'r', node_size = 100, alpha = 1)
ax = plt.gca()
for e in G.edges:
    ax.annotate("",
                xy=pos[e[0]], xycoords='data',
                xytext=pos[e[1]], textcoords='data',
                arrowprops=dict(arrowstyle="->", color="0.5",
                                shrinkA=5, shrinkB=5,
                                patchA=None, patchB=None,
                                connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
                                ),
                                ),
                )
plt.axis('off')
plt.show()

在此输入图片描述


很好的回答!但是我该如何给边缘和节点添加标签呢?例如,我想给每条边赋予不同的权重。 - MAK
1
如果这是一个有向图,则xy应该为pos [e [1]],而xytext应该为[pos [e [0]],以使箭头指向正确的方向。或者您可以将箭头样式反转为“<-”。 - gijswijs

2
您可以使用pyvis包。我只是从我的Jupyter笔记本中的实际项目中复制粘贴了这段代码。
from pyvis import network as pvnet

def plot_g_pyviz(G, name='out.html', height='300px', width='500px'):
    g = G.copy() # some attributes added to nodes
    net = pvnet.Network(notebook=True, directed=True, height=height, width=width)
    opts = '''
        var options = {
          "physics": {
            "forceAtlas2Based": {
              "gravitationalConstant": -100,
              "centralGravity": 0.11,
              "springLength": 100,
              "springConstant": 0.09,
              "avoidOverlap": 1
            },
            "minVelocity": 0.75,
            "solver": "forceAtlas2Based",
            "timestep": 0.22
          }
        }
    '''

    net.set_options(opts)
    # uncomment this to play with layout
    # net.show_buttons(filter_=['physics'])
    net.from_nx(g)
    return net.show(name)

G = nx.MultiDiGraph()
[G.add_node(n) for n in range(5)]
G.add_edge(0, 1, label=1)
G.add_edge(0, 1, label=11)
G.add_edge(0, 2, label=2)
G.add_edge(0, 3, label=3)
G.add_edge(3, 4, label=34)

plot_g_pyviz(G)


This translates to: "

result

"

欢迎来到StackOverflow!请阅读stackoverflow回答指南https://stackoverflow.com/help/how-to-answer。最好对答案进行一些解释。 - Kalesh Kaladharan
很棒的答案!通过注释掉 net.setoptions(opts)来获得节点的固定位置。如何在未启用重力的情况下弯曲边缘? - ged
@ged,您可以在opts变量中使用JS。只需取消注释字符串net.show_buttons(filter_=['physics'])并复制粘贴JS代码即可。使用配置UI动态调整网络设置 - cnstntn.kndrtv

1

请参考atomh33ls的答案

import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import random as rd

column_from = 'from_here'
column_to = 'to_there'
column_attribute = 'edges_count'

# test data
pdf = pd.DataFrame([
                   ['a', 'b', 3], 
                   ['b', 'a', 1], 
                   ['a', 'c', 1], 
                   ['b', 'c', 1],
                   ['a', 'd', 1],
                   ['e', 'b', 2],
                   ['c', 'f', 1],
                   ['f', 'g', 1]],
                  columns=[column_from, column_to, column_attribute])
with pd.option_context('display.max_rows', None, 'display.max_columns', None):  # more options can be specified also
    print(pdf)

def get_adjacency_matrix(pdf):
    id_set = set(pdf[column_from].drop_duplicates().values.tolist() + 
                 pdf[column_to].drop_duplicates().values.tolist())
    id_dict_kv = {k : v for k, v in enumerate(id_set)}
    id_dict_vk = {v : k for k, v in id_dict_kv.items()}
    count = len(id_set)

    adjacency_matrix = np.zeros([count, count], dtype='int32')

    for row in pdf.itertuples():
        index_from = id_dict_vk[getattr(row, column_from)]
        index_to = id_dict_vk[getattr(row, column_to)]
        adjacency_matrix[index_from, index_to] += getattr(row, column_attribute)
    label_mapping = id_dict_kv
    return adjacency_matrix, label_mapping


def pdf_to_MDG(pdf):
    adjacency_matrix, label_mapping = get_adjacency_matrix(pdf)
    G = nx.from_numpy_matrix(adjacency_matrix, parallel_edges=True, create_using=nx.MultiDiGraph())
    G = nx.relabel_nodes(G, label_mapping)
    return G

MDG = pdf_to_MDG(pdf)

edges_data = MDG.edges.data(column_weight)
print(edges_data)

#—————————————————————————————just see the below: draw MultiDiGraph—————————————————————————————————

pos = nx.spring_layout(MDG, seed = 1)
nx.draw(MDG, pos, with_labels=True, edge_color = (1,1,1))
for e in MDG.edges:
    plt.gca().annotate("",
                       xy=pos[e[1]], 
                       xycoords='data',
                       xytext=pos[e[0]], 
                       textcoords='data',
                       arrowprops=dict(arrowstyle="->", color="0",
                                       shrinkA=15, shrinkB=15,
                                       patchA=None, patchB=None,
                                       connectionstyle="arc3,rad=rrr".replace('rrr',str(rd.random()*0.5+0.1)))
                      )
plt.axis('off')
plt.show()

输出:

  from_here to_there  edges_count
0         a        b            3
1         b        a            1
2         a        c            1
3         b        c            1
4         a        d            1
5         e        b            2
6         c        f            1
7         f        g            1
[('c', 'f', 1), ('e', 'b', 1), ('e', 'b', 1), ('b', 'c', 1), ('b', 'a', 1), ('f', 'g', 1), ('a', 'c', 1), ('a', 'd', 1), ('a', 'b', 1), ('a', 'b', 1), ('a', 'b', 1)]

输出图片


如果您删除了所有(不相关的)测试数据生成,那么这与您引用的现有答案有什么不同? - snakecharmerb
@snakecharmerb,您可以将下面的图形进行比较,其中有两个主要的区别:1、添加标签;2、随机边。 - givedrug
@snakecharmerb 第三个区别:箭头方向。 - givedrug

0
非常感谢@Lee的回答,但是我想给节点加上标签,并且希望箭头指向正确的方向,所以我得出了以下结论:
from matplotlib import pyplot as plt

G = <YOUR MULTIGRAPH HERE>
pos = nx.random_layout(G)
names = {name: name for name in G.nodes}
nx.draw_networkx_nodes(G, pos, node_color = 'b', node_size = 250, alpha = 1)
nx.draw_networkx_labels(G,pos,names,font_size=12,font_color='w')
ax = plt.gca()
for e in G.edges:
    ax.annotate("",
                xy=pos[e[1]], xycoords='data',
                xytext=pos[e[0]], textcoords='data',
                arrowprops=dict(arrowstyle="->", color="0",
                                shrinkA=10, shrinkB=10,
                                patchA=None, patchB=None,
                                connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
                                ),
                                ),
                )
plt.axis('off')
plt.show()

示例输出:三节点有向多图


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