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

如何在Python的Bokeh中为交互式网络图添加自环边与有向箭头边

在Bokeh中实现状态转移图的自环边与有向箭头边

一、实现带箭头的有向边

Bokeh原生提供Arrow组件,可直接为状态转移边添加有向箭头,步骤如下:

  • 先确定所有节点的坐标(可通过布局算法计算或手动指定)
  • 遍历每条状态转移关系,创建Arrow对象,指定起点、终点,并配置箭头头部和箭身样式
  • 将所有箭头添加到绘图figure中

示例代码:

from bokeh.models import Arrow, NormalHead, Line, ColumnDataSource
from bokeh.plotting import figure, show

# 节点坐标与标签数据源
nodes = ColumnDataSource(data=dict(
    x=[1, 2, 3],
    y=[1, 2, 1],
    labels=['状态1', '状态2', '状态3']
))
# 转移边数据:起点节点索引、终点节点索引、转移概率
edges = ColumnDataSource(data=dict(
    start_idx=[0, 1, 2],
    end_idx=[1, 2, 0],
    prob=[0.3, 0.5, 0.2]
))

p = figure(tools="hover,pan,zoom", x_range=(0, 4), y_range=(0, 3))
# 绘制节点
p.circle('x', 'y', size=20, source=nodes, fill_color='lightblue')

# 批量生成有向边
for i in range(len(edges.data['start_idx'])):
    start_x = nodes.data['x'][edges.data['start_idx'][i]]
    start_y = nodes.data['y'][edges.data['start_idx'][i]]
    end_x = nodes.data['x'][edges.data['end_idx'][i]]
    end_y = nodes.data['y'][edges.data['end_idx'][i]]
    
    p.add_layout(Arrow(
        end=NormalHead(fill_color='black', size=8),
        line=Line(color='gray', width=2),
        x_start=start_x, y_start=start_y,
        x_end=end_x, y_end=end_y
    ))

show(p)

二、实现自环边

自环边需要绘制环绕节点的圆弧,可通过Bokeh的Arc组件实现,同时在圆弧末端添加箭头指向节点:

  • 针对有自环的节点,设置自环的半径(略大于节点尺寸)和圆弧角度范围(比如右侧半圆、上侧半圆)
  • 绘制圆弧后,在圆弧终点添加箭头,微调位置避免与节点重叠

示例代码(接续上述节点数据源):

import math
from bokeh.models import Arc

# 给状态2添加自环,转移概率0.4
self_loop_idx = 1
node_x = nodes.data['x'][self_loop_idx]
node_y = nodes.data['y'][self_loop_idx]
loop_radius = 0.3  # 自环半径

# 绘制右侧半圆自环(角度范围π/2到3π/2)
p.arc(
    x=node_x, y=node_y,
    radius=loop_radius,
    start_angle=math.pi/2,
    end_angle=3*math.pi/2,
    line_color='gray', line_width=2
)

# 添加自环箭头,指向节点中心
p.add_layout(Arrow(
    end=NormalHead(fill_color='black', size=8),
    line=Line(color='gray', width=2),
    x_start=node_x, y_start=node_y - loop_radius,
    x_end=node_x, y_end=node_y - loop_radius + 0.05  # 微调位置避免重叠
))

调整start_angle和end_angle可改变自环的位置(比如0到math.pi对应上侧半圆)。

三、结合悬停高亮交互

要实现悬停节点时高亮关联边和节点,可通过HoverTool配合CustomJS回调实现:

  • 给节点和边元素添加标识名称,方便JS定位
  • 编写回调代码,重置所有元素样式后,高亮当前节点的关联边

示例代码片段:

from bokeh.models import HoverTool, CustomJS

# 给节点添加名称标识
p.circle('x', 'y', size=20, source=nodes, fill_color='lightblue', name='state_nodes')

# 给边添加名称标识(创建Arrow时统一设置name)
# 这里需要将所有Arrow存入一个渲染器列表,或者用MultiLine替代Arrow实现批量边渲染
# 以下是简化的回调示例:
hover_callback = CustomJS(args=dict(nodes=nodes, edges=edges, p=p), code="""
    const hovered_idx = cb_data.index.indices[0];
    if (!hovered_idx) return;
    
    // 重置所有边样式
    p.select_all('Arrow').forEach(arrow => {
        arrow.line.line_color = 'gray';
        arrow.line.line_width = 2;
    });
    
    // 高亮关联边
    for (let i=0; i<edges.data.start_idx.length; i++) {
        const start = edges.data.start_idx[i];
        const end = edges.data.end_idx[i];
        if (start === hovered_idx || end === hovered_idx) {
            p.select_all('Arrow')[i].line.line_color = 'red';
            p.select_all('Arrow')[i].line.line_width = 3;
        }
    }
""")

hover_tool = HoverTool(
    tooltips=[("状态", "@labels"), ("转移概率", "@prob")],
    callback=hover_callback,
    renderers=[p.select(name='state_nodes')]
)
p.add_tools(hover_tool)

内容的提问来源于stack exchange,提问作者Shreyans Jain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:33:12