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

Bokeh自定义JS回调无法更新Select组件过滤散点图问题排查

解决Bokeh+HoloViews下拉菜单过滤无效果的问题

嘿,我帮你排查出了代码里几个关键问题,正是这些问题导致下拉选择后图表没有变化,咱们一步步拆解修复:


核心问题1:HoloViews图表没绑定动态数据源

你用HoloViews生成的箱线图和散点图,是直接基于原始df创建的,根本没关联你定义的source_fill动态数据源。HoloViews渲染成Bokeh对象后,会自己生成独立的数据源,不会自动响应你手动创建的数据源变化,这是最主要的原因。

核心问题2:CustomJS里的语法错误

  • 你写的var String(unique_WW_5) = unique_WW_5;是完全无效的JavaScript语法,不能这么声明变量,要转换字符串直接用String(unique_WW_5)就行。
  • 还有f == String(%unique_WW_1)这种写法也不对,在CustomJS的代码块里,引用Python传过来的参数不需要加%,直接用变量名就行。

额外优化:冗余数据源没必要

你提前给每个周都创建了单独的ColumnDataSource,其实完全可以在JS里根据选中的周直接过滤原始数据,既省资源又更简洁。


修复后的完整代码

我把你的代码重构了,解决了所有问题,还优化了逻辑:

import pandas as pd
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CustomJS, Select
from bokeh.plotting import figure, show
import holoviews as hv
from holoviews import opts
import bokeh.models as bmo

hv.extension('bokeh')

# 构造原始数据
index = [0,1,2,3,4,5]
w = pd.Series([202045,202031,202001,202023,202024,202006], index=index)
s = pd.Series(['S1','S2','S3','S4','S5','S6'], index=index)
t = pd.Series([2,4,6,8,10,12], index=index)

df = pd.DataFrame(s, columns=["ABC"])
df["DFG"] = t
df["SWW"] = w

# 获取最近5周并生成下拉选项
unique_weeks = sorted(df['SWW'].unique())[-5:]
select_options = [str(week) for week in unique_weeks] + ['ALL']

# 创建唯一的动态数据源,所有图表都绑定它
source = ColumnDataSource(data=df)

# 创建下拉选择器
select = Select(title='Selected WEEK:', value='ALL', options=select_options)

# 编写过滤逻辑的CustomJS
update_callback = CustomJS(args=dict(source=source), code="""
    const original = source.data;
    const selected = cb_obj.value;
    
    if (selected === 'ALL') {
        // 选ALL时恢复原始数据
        source.data = original;
    } else {
        // 过滤选中周的数据
        const filtered = {};
        // 遍历每一列,筛选对应行
        for (const col in original) {
            filtered[col] = [];
            for (let i = 0; i < original.SWW.length; i++) {
                if (String(original.SWW[i]) === selected) {
                    filtered[col].push(original[col][i]);
                }
            }
        }
        source.data = filtered;
    }
    // 通知Bokeh更新图表
    source.change.emit();
""")

# 绑定下拉选择的事件
select.js_on_change('value', update_callback)

# 基于动态数据源创建HoloViews图表
boxwhisker = hv.BoxWhisker(source.data, ['ABC'], 'DFG', label='COMBINED').opts(show_legend=False, width=600)
hover = bmo.HoverTool(tooltips=[('ABC', '@ABC')])
scatter = hv.Points(source.data, ['ABC', 'DFG'], label='COMBINED').opts(size=7, color='black', show_grid=True, tools=[hover])

# 合并图表并转为Bokeh对象
combined_plot = boxwhisker * scatter
combined_plot.opts(show_legend=True, height=1000, width=1700, xrotation=90)
bokeh_plot = hv.render(combined_plot)

# 关键:替换HoloViews自动生成的数据源为我们的动态source
for renderer in bokeh_plot.renderers:
    if hasattr(renderer, 'data_source'):
        renderer.data_source = source

# 布局并显示
layout = column(select, bokeh_plot)
show(layout)

修复要点说明

  1. 绑定统一动态数据源:所有图表都用同一个source,JS修改数据源后,所有图表都会同步更新。
  2. 修复CustomJS语法:去掉无效的变量声明,直接在判断时转换字符串类型做比较,逻辑更清晰。
  3. 替换HoloViews数据源:HoloViews渲染时会生成自己的数据源,必须手动替换成我们的动态source,这样才能响应下拉选择的变化。
  4. 简化过滤逻辑:直接在JS里动态过滤原始数据,不需要提前创建多个冗余数据源,代码更易维护。

现在运行这段代码,选择不同的周数,图表就会自动过滤显示对应的数据啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:40:10