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

无JS经验:基于Bokeh JSCallback实现下拉框更新图表

Fixing Bokeh CustomJS Callback for Dropdown Selection in Static HTML

我明白你在做静态Bokeh HTML文件时卡在了下拉框回调上——不能用Bokeh Server确实得靠CustomJS硬刚,咱们一步步把你的代码理顺、修好。

先说说你现有代码里的核心问题:

  1. 数据源逻辑搞反了:你把空的true_source当数据源容器,却让图表用了全量数据的source,这完全搞反了分工;
  2. 回调逻辑错误:循环里的if(x[i]==f)完全没关联到学期和对应数据,变量也混淆了;
  3. 初始图表显示不对:默认加载了所有学期的叠加数据,不符合下拉框初始选中值的预期。

下面是修正后的完整可运行代码,我会逐段解释:

Step 1: 整理数据结构,准备合适的数据源

首先我们把数据按学期分组,把所有学期的完整数据存在一个ColumnDataSource里,方便JS直接调取;图表用的数据源只初始加载默认学期的数据:

import pandas as pd
from bokeh.models import ColumnDataSource, Select, CustomJS
from bokeh.plotting import figure, save
from bokeh.io import output_file
from bokeh.transform import factor_cmap
from bokeh.palettes import Category10_4

# 你的测试数据
data_test = {'Term': ['Fall 2018', 'Fall 2018', 'Fall 2018', 'Fall 2018', 'Spring 2018', 'Spring 2018', 'Spring 2018', 'Spring 2018', 'Fall 2017','Fall 2017','Fall 2017','Fall 2017'], 
             'Grade': ['A+','A','B','C','A+','A','B','C','A+','A','B','C'], 
             'grade_count': [4500, 500, 6000, 250, 103, 1345, 2503, 324, 684,230, 566,2344]}
df = pd.DataFrame(data_test)

# 按学期分组,把每个学期的Grade和grade_count整理成字典
grouped = df.groupby('Term')
all_data = ColumnDataSource(data={
    term: {
        'x': list(group['Grade']),
        'y': list(group['grade_count'])
    } for term, group in grouped
})

# 图表用的数据源,初始加载默认学期数据
default_term = 'Fall 2018'
source = ColumnDataSource(data=all_data.data[default_term])

Step 2: 修正下拉框和CustomJS回调

现在all_data里存了每个学期的完整数据,回调只需要根据选中的学期,把对应数据赋值给图表数据源即可,逻辑非常直接:

# 下拉框选项:提取所有学期
Terms = list(grouped.groups.keys())
dropdown = Select(title='Term Dropdown', value=default_term, options=Terms)

# 修正后的CustomJS回调
callback = CustomJS(args=dict(source=source, all_data=all_data), code="""
    // 获取当前选中的学期
    const selected_term = cb_obj.value;
    // 从全量数据里取出对应学期的x/y值
    const new_data = all_data.data[selected_term];
    // 更新图表数据源
    source.data = {
        x: new_data.x,
        y: new_data.y
    };
    // 触发图表更新
    source.change.emit();
""")

# 给下拉框绑定回调
dropdown.js_on_change('value', callback)

Step 3: 创建图表并输出静态HTML

最后把图表和下拉框组合(如果需要显示下拉框,记得用layout),输出静态HTML:

# 定义颜色映射(如果之前没定义的话)
grade_list = ['A+', 'A', 'B', 'C']
grade_cmap = factor_cmap('x', palette=Category10_4, factors=grade_list)

# 创建柱状图
p1 = figure(x_range=grade_list, title='Grade Distribution by Term', 
            x_axis_label='Grade', y_axis_label='Count')
p1.vbar(x='x', top='y', width=.75, bottom=0, 
        source=source, line_color=grade_cmap, fill_color=grade_cmap)

# (可选)如果需要在页面显示下拉框,用layout组合
from bokeh.layouts import column
layout = column(dropdown, p1)

# 输出静态HTML文件
output_file('grade_distribution.html')
save(layout)  # 或者用show(layout)直接在浏览器打开预览

为什么这样改能解决问题?

  • 数据源分工明确:all_data存全量的分组数据,source只存当前要显示的学期数据,避免了循环筛选的麻烦;
  • 回调逻辑简洁:直接根据选中的学期调取对应数据,没有多余的循环判断,减少出错概率;
  • 初始显示符合预期:默认加载下拉框选中的学期数据,不会出现所有学期叠加的混乱图表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:56:52