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

Bokeh中Select组件js_on_change事件无法更新网络图问题求解

问题原因

你当前代码不生效的核心原因是:你自定义的source变量和Bokeh网络图实际渲染用的数据源完全独立,你在JS回调里只更新了自己创建的source,没有同步修改网络图的边渲染数据源,所以界面不会有任何变化。另外你构建图G的时候仅过滤了初始选中列的有效边,切换选项时也没有重新过滤边集。

修复后的可运行代码

from bokeh.plotting import from_networkx, figure, show
from bokeh.models import Range1d, MultiLine, Circle, HoverTool, Select, Column, CustomJS
import networkx as nx
import pandas as pd

# 生成原始数据
d = {'weight': [1, 2,3,1,2], 'var': ["var1","var1","var1","var2", "var2"], 'source': ["a", "b","c","e","f"], 'target': ["c","g","e","a","h"]}
df1 = pd.DataFrame(data=d)
df2 = df1.pivot(index= ["source","target"], values = "weight", columns = "var").reset_index()
df2 = df2.fillna(0)

HOVER_TOOLTIPS = [("Search Term", "@index")]
title = "my title"
plot = figure(tooltips = HOVER_TOOLTIPS,
              tools="pan,wheel_zoom", 
              active_scroll='wheel_zoom',
              x_range=Range1d(-10.1, 10.1), 
              y_range=Range1d(-10.1, 10.1), 
              title=title, plot_width=1000
             )

category_default = "var1"
unique_categories = ["var1","var2"]

# 预先构建包含所有节点的图,固定布局避免切换列时节点位置变动
all_edges = df2[["source", "target"]].values.tolist()
G_full = nx.from_edgelist(all_edges)
pos = nx.spring_layout(G_full, scale=10, center=(0,0))
network_graph = from_networkx(G_full, pos)

# 准备全量边数据传给JS,拿到网络图的边渲染数据源
all_edge_data = df2.to_dict("list")
edge_renderer = network_graph.edge_renderer
edge_source = edge_renderer.data_source

select = Select(title='Category Selection', value=category_default, options=unique_categories)

callback = CustomJS(
    args={"all_edge_data": all_edge_data, "edge_source": edge_source},
    code=""" 
        const selected_var = cb_obj.value;
        const sources = all_edge_data.source;
        const targets = all_edge_data.target;
        const weights = all_edge_data[selected_var];
        
        // 过滤出weight>0的边
        const new_sources = [];
        const new_targets = [];
        const new_weights = [];
        for (let i=0; i<sources.length; i++) {
            if (weights[i] > 0) {
                new_sources.push(sources[i]);
                new_targets.push(targets[i]);
                new_weights.push(weights[i]);
            }
        }
        
        // 直接更新边渲染数据源
        edge_source.data = {
            start: new_sources,
            end: new_targets,
            weight: new_weights
        };
    """)

select.js_on_change("value", callback)

# 配置图元素样式
network_graph.edge_renderer.glyph = MultiLine(line_alpha=0.5, line_width="weight" )
network_graph.node_renderer.glyph = Circle(size=15, fill_color="#2f7ed8")

plot.renderers.append(network_graph)
show(Column(plot, select))

更简便的行过滤方案

你不需要把数据透视成宽表,直接用原始的长表df1处理逻辑更简单:

  • 存储的时候直接按var字段区分不同类别
  • 过滤的时候直接按var值筛选行即可,不需要处理多列映射,后续新增类别也不需要修改数据结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 00:45:04