You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bokeh中为NetworkX图节点添加可随缩放刷新的非交互式永久标签?

Got it, let's work through this together! You’ve already got your node positions from nx.spring_layout(G)—the trick here is tying those labels directly to your nodes in Bokeh so they stick with them through zooms, pans, or refreshes. Here's a step-by-step solution to add non-interactive, permanent labels that move with your nodes:

实现NetworkX+Bokeh图的永久跟随标签

The core tool you need here is Bokeh's LabelSet—it lets you create batch labels that are bound to your node data source, so they automatically adjust position whenever the plot is zoomed or refreshed.

1. Prep your graph data and positions

First, convert your NetworkX graph into Bokeh-compatible data structures while preserving the spring layout positions:

import networkx as nx
from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource, LabelSet, Circle, MultiLine

# Replace this with your actual graph
G = nx.karate_club_graph()
# Get spring layout positions
pos = nx.spring_layout(G)

# Extract node positions and labels into lists
node_x = [pos[node][0] for node in G.nodes()]
node_y = [pos[node][1] for node in G.nodes()]
# Use node IDs as labels, or replace with your custom node attributes (e.g., G.nodes[node]['name'])
node_labels = [str(node) for node in G.nodes()]

# Create a Bokeh data source for nodes (this ties positions and labels together)
node_source = ColumnDataSource(data=dict(
    x=node_x,
    y=node_y,
    labels=node_labels
))

# Prep edge data for Bokeh
start_x, start_y, end_x, end_y = [], [], [], []
for u, v in G.edges():
    start_x.append(pos[u][0])
    start_y.append(pos[u][1])
    end_x.append(pos[v][0])
    end_y.append(pos[v][1])

edge_source = ColumnDataSource(data=dict(
    start_x=start_x,
    start_y=start_y,
    end_x=end_x,
    end_y=end_y
))

2. Build the base plot

Initialize your Bokeh figure with the tools you need (like zoom and pan to test label behavior):

p = figure(
    title="Spring Layout with Permanent Node Labels",
    tools="pan,wheel_zoom,reset,save",
    match_aspect=True  # Keeps aspect ratio to prevent label-node misalignment on zoom
)

# Add edges first so they sit behind nodes
p.multi_line(
    xs='start_x', ys='start_y', x2s='end_x', y2s='end_y',
    source=edge_source, color='gray', line_width=1
)

# Add nodes
p.circle(
    x='x', y='y', source=node_source,
    size=12, color='cornflowerblue', alpha=0.8
)

3. Add the permanent labels

Use LabelSet to bind labels directly to your node data source. Adjust offsets to avoid overlapping with nodes:

# Create label set tied to the node data source
labels = LabelSet(
    x='x', y='y', text='labels',
    source=node_source,
    x_offset=6, y_offset=6,  # Shift labels slightly away from nodes
    text_font_size='10pt',
    text_color='darkslategray',
    interaction_policy='none'  # Ensures labels are non-interactive
)

# Add labels to the plot
p.add_layout(labels)

4. Test the plot

Save and show your plot to verify labels stay with nodes during zoom/pan:

output_file("network_labels.html")
show(p)

Key Notes

  • Since LabelSet is linked to the same data source as your nodes, any changes to the plot (like zooming) will automatically update label positions to match nodes.
  • To use custom labels (instead of node IDs), just modify the node_labels list to pull from your node attributes (e.g., node_labels = [G.nodes[node]['description'] for node in G.nodes()]).
  • The interaction_policy='none' ensures labels don’t respond to mouse clicks or hovers, keeping them strictly non-interactive as you requested.

内容的提问来源于stack exchange,提问作者Ly Harriet Bui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:03:21