【问题标题】:adding tooltip for nodes in python networkx graph在 python networkx 图中为节点添加工具提示
【发布时间】:2020-05-05 02:18:07
【问题描述】:

我使用networkx.DiGraph 创建了一个有向图,然后使用networkx.draw_spring(graph) 绘制它,因此该图的所有节点都有一些详细信息存储在字典列表中。

如何添加诸如“工具提示”之类的内容以在鼠标悬停在每个节点上时查看这些详细信息?如果可能的话,如何使这个“工具提示”对所有节点始终可见,而不仅仅是通过悬停?

【问题讨论】:

    标签: python matplotlib graph tooltip networkx


    【解决方案1】:

    始终可见

    要标记所有节点,您只需要使用annotate。像这样的

    import matplotlib.pyplot as plt
    import networkx as nx
    
    G = nx.path_graph(5)
    attrs = {0: {'attr1': 20, 'attr2': 'nothing'}, 1: {'attr2': 3}, 2: {'attr1': 42}, 3: {'attr3': 'hello'}, 4: {'attr1': 54, 'attr3': '33'}}
    nx.set_node_attributes(G, attrs)
    nx.draw(G)
    
    for node in G.nodes:
        xy = pos[node]
        annot.xy = xy
        node_attr = G.nodes[node]
        text = '\n'.join(f'{k}: {v}' for k, v in G.nodes[node].items())
        text = f'node {node}\n' + text
        ax.annotate(text, xy=xy)
    

    悬停时

    这是一个在悬停时获取工具提示的工作示例。这是基于使用标准 matplotlib 图here 的工具提示。我使用draw_networkx_nodes 来获取用于悬停和显示工具提示的对象,而不是使用draw_spring。但是您可以使用spring_layout 手动定义位置。

    import matplotlib.pyplot as plt
    import networkx as nx
    
    G = nx.path_graph(5)
    attrs = {0: {'attr1': 20, 'attr2': 'nothing'}, 1: {'attr2': 3}, 2: {'attr1': 42}, 3: {'attr3': 'hello'}, 4: {'attr1': 54, 'attr3': '33'}}
    nx.set_node_attributes(G, attrs)
    
    fig, ax = plt.subplots()
    pos = nx.spring_layout(G)
    nodes = nx.draw_networkx_nodes(G, pos=pos, ax=ax)
    nx.draw_networkx_edges(G, pos=pos, ax=ax)
    
    annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
                        bbox=dict(boxstyle="round", fc="w"),
                        arrowprops=dict(arrowstyle="->"))
    annot.set_visible(False)
    
    def update_annot(ind):
        node = ind["ind"][0]
        xy = pos[node]
        annot.xy = xy
        node_attr = {'node': node}
        node_attr.update(G.nodes[node])
        text = '\n'.join(f'{k}: {v}' for k, v in node_attr.items())
        annot.set_text(text)
    
    def hover(event):
        vis = annot.get_visible()
        if event.inaxes == ax:
            cont, ind = nodes.contains(event)
            if cont:
                update_annot(ind)
                annot.set_visible(True)
                fig.canvas.draw_idle()
            else:
                if vis:
                    annot.set_visible(False)
                    fig.canvas.draw_idle()
    
    fig.canvas.mpl_connect("motion_notify_event", hover)
    
    plt.show()
    

    【讨论】:

    • 哇,这非常具体,非常棒并且非常有效:D,非常感谢!
    【解决方案2】:

    仅供参考@busybear @sudofix

    这仅适用于您的节点从 0 开始。

    如果你这样做:

    import matplotlib.pyplot as plt
    import networkx as nx
    
    nodes = list(range(5))
    
    edges = []
    for e1,e2 in zip(nodes[:-1],nodes[1:]):
        edges.append((e1,e2))
    
    G = nx.Graph()
    G.add_nodes_from(nodes)
    G.add_edges_from(edges)
    
    attrs = {}
    for node in G.nodes:
        attrs[node] = {'attr1': node, 'attr2': 'hello', 'attr3': 33}
    

    其余的保留为

    nx.set_node_attributes(G, attrs)
    
    fig, ax = plt.subplots()
    pos = nx.spring_layout(G)
    nodes = nx.draw_networkx_nodes(G, pos=pos, ax=ax)
    nx.draw_networkx_edges(G, pos=pos, ax=ax)
    
    annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
                        bbox=dict(boxstyle="round", fc="w"),
                        arrowprops=dict(arrowstyle="->"))
    annot.set_visible(False)
    
    def update_annot(ind):
        node = ind["ind"][0]
        xy = pos[node]
        annot.xy = xy
        node_attr = {'node': node}
        node_attr.update(G.nodes[node])
        text = '\n'.join(f'{k}: {v}' for k, v in node_attr.items())
        annot.set_text(text)
    
    def hover(event):
        vis = annot.get_visible()
        if event.inaxes == ax:
            cont, ind = nodes.contains(event)
            if cont:
                update_annot(ind)
                annot.set_visible(True)
                fig.canvas.draw_idle()
            else:
                if vis:
                    annot.set_visible(False)
                    fig.canvas.draw_idle()
    
    fig.canvas.mpl_connect("motion_notify_event", hover)
    
    plt.show()
    

    一切都很好。

    但如果你改变 nodes = list(range(5)) 成为 nodes = list(range(1,6)) 它不起作用,因为node = ind["ind"][0] 返回的是节点在G.nodes 中的位置,而不是节点的名称,所以访问pos[node]G.nodes[node] 会得到错误的位置(它移动了1)。

    解决方案是创建一个类似的映射

    idx_to_node_dict = {}
    for idx, node in enumerate(G.nodes):
        idx_to_node_dict[idx] = node
    

    并修复函数以将其用作:

    def update_annot(ind):
        node_idx = ind["ind"][0]
        node = idx_to_node_dict[node_idx]
    

    【讨论】:

      猜你喜欢
      • 2021-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-06
      相关资源
      最近更新 更多