【问题标题】:How to add permanent name labels (not interactive ones) on nodes for a networkx graph in bokeh?如何在散景中的networkx图节点上添加永久名称标签(非交互式标签)?
【发布时间】:2018-09-25 16:48:54
【问题描述】:

我正在尝试使用 spring_layout 和 bokeh 库在 networkx 图的节点上添加永久标签。我希望这个标签在图形缩放时重新定位或像字符串布局一样刷新,在图形缩放或刷新时重新定位节点。

我尝试创建图形和布局,然后从 string_layout 获取 pos。但是,当我调用pos=nx.spring_layout(G) 时,它将为图 G 中的节点生成一组位置,我可以将其坐标放入 LabelSet。但是,我必须调用graph = from_networkx(G, spring_layout, scale=2, center=(0,0)) 来绘制网络图。这将为节点创建一组新的位置。因此,节点和标签的位置不会相同。

如何解决这个问题?

【问题讨论】:

    标签: graph label visualization networkx bokeh


    【解决方案1】:

    与@bigreddot 类似的解决方案。

        #Libraries for this solution
        from bokeh.plotting import figure ColumnDataSource
        from bokeh.models import LabelSet
    
        #Remove randomness
        import numpy as np
        np.random.seed(1337)
    
        #Load positions
        pos = nx.spring_layout(G)
    
        #Dict to df
        labels_df = pd.DataFrame.from_dict(pos).T
    
        #Reset index + column names
        labels_df = labels_df.reset_index()
        labels_df.columns = ["names", "x", "y"]
    
        graph_renderer = from_networkx(G, pos, center=(0,0))
        .
        .
        .
        plot.renderers.append(graph_renderer)
    
        #Set labels
        labels = LabelSet(x='x', y='y', text='names', source=ColumnDataSource(labels_df))
    
        #Add labels
        plot.add_layout(labels)
    

    【讨论】:

      【解决方案2】:

      感谢您提出这个问题。通过它,我意识到它目前的工作量超过了应有的工作量。我强烈建议您打开GitHub issue,以便我们讨论哪些改进可以最好地让用户更轻松地完成这类事情。

      这是一个完整的例子:

      import networkx as nx
      
      from bokeh.io import output_file, show
      from bokeh.models import CustomJSTransform, LabelSet
      from bokeh.models.graphs import from_networkx
      
      from bokeh.plotting import figure
      
      G=nx.karate_club_graph()
      
      p = figure(x_range=(-3,3), y_range=(-3,3))
      p.grid.grid_line_color = None
      
      r = from_networkx(G, nx.spring_layout, scale=3, center=(0,0))
      r.node_renderer.glyph.size=15
      r.edge_renderer.glyph.line_alpha=0.2
      
      p.renderers.append(r)
      

      到目前为止,这都是相当正常的 Bokeh 图形布局代码。这是您需要为每个节点添加永久标签的附加部分:

      from bokeh.transform import transform    
      
      # add the labels to the node renderer data source
      source = r.node_renderer.data_source
      source.data['names'] = [str(x*10) for x in source.data['index']]
      
      # create a transform that can extract the actual x,y positions
      code = """
          var result = new Float64Array(xs.length)
          for (var i = 0; i < xs.length; i++) {
              result[i] = provider.graph_layout[xs[i]][%s]
          }
          return result
      """
      xcoord = CustomJSTransform(v_func=code % "0", args=dict(provider=r.layout_provider))
      ycoord = CustomJSTransform(v_func=code % "1", args=dict(provider=r.layout_provider))
      
      # Use the transforms to supply coords to a LabelSet 
      labels = LabelSet(x=transform('index', xcoord),
                        y=transform('index', ycoord),
                        text='names', text_font_size="12px",
                        x_offset=5, y_offset=5,
                        source=source, render_mode='canvas')
      
      p.add_layout(labels)
      
      show(p)
      

      基本上,由于 Bokeh(可能)在浏览器中计算布局,实际节点位置只能通过“布局提供程序”获得,目前访问起来有点乏味。正如我所说,请打开一个 GitHub 问题以建议为用户提供更好的服务。我们可以做一些非常快速和简单的事情来让用户更简单。

      上面的代码导致:

      【讨论】:

      • 我应该补充一下,当然还有其他方法可以做到这一点。由于nx 图形生成静态布局,您还可以提取(x,y) 坐标in Python 并将它们放入LabelSet 的数据源中。这样可以避免 JS 转换,但我认为上面的方法是最通用和“正确的”。
      • x, y = zip(*graph_renderer.layout_provider.graph_layout.values()) 和索引或 G.nodes.keys() 之间的缺失链接在哪里?我将打开一个问题,我找不到它。
      • @pierre 请对这个新存在的问题发表评论(在此处链接以供参考)github.com/bokeh/bokeh/issues/9955 也就是说,您上面的内容通常不可行,仅在某些特殊情况下。正如我所提到的,布局可能只能在在浏览器中,在 JavaScript 中计算,在这种情况下,在 Python 端根本不可用。
      【解决方案3】:

      固定节点位置

      来自networkx.spring_layout() 文档:您可以添加具有固定位置的节点列表作为参数。

      import networkx as nx
      import matplotlib.pyplot as plt
      
      g = nx.Graph()
      g.add_edges_from([(0,1),(1,2),(0,2),(1,3)])
      
      pos = nx.spring_layout(g)
      nx.draw(g,pos)
      plt.show()
      

      然后你可以在一个固定的位置绘制节点:

      pos = nx.spring_layout(g, pos=pos, fixed=[0,1,2,3])
      nx.draw(g,pos)
      plt.show()
      

      【讨论】:

      • 问题是如何使用 Bokeh 而非 Matplotlib 来实现这一点。还有关于如何向节点添加文本标签,这根本不做。
      • 散景函数from_networkx 使用来自networkxspring_layout 函数来定义节点位置。所以在spring_layout 函数中定义位置对我来说似乎更容易。
      • 虽然题主是关于文本标签的,但是仔细阅读题主会发现具体问题是关于节点位置的。
      • “我正在尝试使用 spring_layout 和 bokeh 库在 networkx 图形的节点上添加一个永久标签。我希望在图形缩放或刷新时重新定位此标签字符串布局的作用,在图形缩放或刷新时重新定位节点。” 这个答案并没有提供,特别是,在任何情况下与动态(客户端)保持同步都无济于事。生成)布局更改。
      • 如果您阅读了问题的其余部分,显然是关于 spring_layout 的使用情况。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多