如何在bokeh中为networkx图上的节点添加永久名称标签(而不是交互式标签)?

3
我正在尝试使用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))来绘制网络图。这将为节点创建一个新的位置集。因此,节点和标签的位置将不同。
如何解决这些问题?
3个回答

3
感谢您提出这个问题。在解决它的过程中,我意识到目前要做的事情比应该做的要多。我强烈建议您打开GitHub问题,以便我们可以讨论如何改进,使这种操作更容易为用户所用。
下面是一个完整的示例:
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 问题建议为用户改进此功能。我们可能可以做一些非常快速和简单的事情,使用户更容易使用。
上面的代码结果如下: enter image description here

我应该补充,肯定还有其他方法可以实现这个。由于nx图生成静态布局,您也可以在Python中提取(x,y)坐标,并将它们放入LabelSet的数据源中。这将避免使用JS转换,但我认为上述方法是最通用和“正确”的方法。 - bigreddot
x, y = zip(*graph_renderer.layout_provider.graph_layout.values()) 和 index 或 G.nodes.keys() 之间缺少什么联系?我将会开一个 issue,因为我找不到它。 - user5128720
@pierre,请在此新存在的问题上发表任何评论(链接在此处供参考)https://github.com/bokeh/bokeh/issues/9955。话虽如此,你上面所说的并不是一般情况下可行的,只有在某些特殊情况下才可以。正如我所提到的,布局可能只能在*浏览器中的JavaScript*中计算,在这种情况下根本无法在Python端使用。 - bigreddot

-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

固定节点位置

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()

initial_graph

然后您可以在固定位置绘制节点:

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

fixed_nodes


问题是如何使用Bokeh实现这一点,而不是Matplotlib。还有关于如何向节点添加文本标签,但这个方法并没有做到这一点。 - bigreddot
Bokeh函数from_networkx使用了networkx中的spring_layout函数来定义节点位置。因此,我认为在spring_layout函数中定义位置更容易些。 - michaelg
尽管问题的标题是关于文本标签的,但如果您仔细阅读问题的正文,您会发现具体的问题是关于节点位置的。 - michaelg
我正在尝试使用spring_layout和bokeh库在networkx图上为节点添加永久标签。我希望这些标签能够像字符串布局一样重新定位,随着图形的缩放或刷新而重新定位节点。但是,这个答案并没有提供这种功能,特别是在动态(客户端生成的)布局更改的情况下,无论如何都无法保持同步。 - bigreddot
如果你继续阅读问题的其余部分,显然是关于spring_layout的使用。 - michaelg
不,实际上并不是这样。使用Bokeh的最终目标是完成特定的事情,而这个答案并没有提供这方面的信息。 - bigreddot

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