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

使用滑块与CustomJS实时更新Bokeh ColumnDataSource遇阻求助

解决Bokeh滑块实时更新颜色和数据表格下载的问题

我帮你排查了代码里的几个关键问题,这就是你的CustomJS未生效的核心原因,下面是问题分析和修正后的完整方案:

核心问题梳理

  1. 字段不匹配:你的颜色映射依赖position字段,但回调里错误地修改了不存在的color字段,Bokeh无法识别这个字段来更新标记颜色。
  2. 循环逻辑错误:回调中没有遍历数组的每个元素(缺少[i]索引),而且直接给整个字段赋值单个字符串,导致无法逐个更新数据点的状态。
  3. 回调未关联滑块:你编写了更新逻辑,但没有将回调绑定到三个滑块的value变化事件上,滑块拖动时根本不会触发颜色更新。
  4. 缺少下载功能:原代码没有实现数据表格的下载按钮,需要添加DownloadButton配合CustomJS来导出数据。

修正后的完整代码

from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import (HoverTool, Span, Slider, CustomJS, CategoricalColorMapper,
                          TableColumn, DataTable, DownloadButton)
from bokeh.layouts import row, column
import numpy as np

# 初始化数据源,确保position字段存在
source = ColumnDataSource(
    data=dict(
        x=[0.154228, 0.118865, 0.071774, 0.230292, 0.070180, 0.010010, 0.141877, 0.085712, 0.166331, 0.101799],
        y=[1.358454, 2.736966, 1.556393, 1.286304, 1.785875, 0.168123, 2.120294, 2.556393, 5.058894, 6.671531],
        accession=[158489.0, 442616758.0, 349669.0, 320544370.0, 161077523.0, 24648576.0, 320545678.0,
                   17137000.0, 442625663.0, 17647529.0],
        position=["normal"] * 10  # 初始化所有点为normal状态
    )
)

# 颜色映射保持使用position字段
color_mapper = CategoricalColorMapper(factors=["up", "normal", "down"], palette=['yellow', 'green', 'blue'])

hover = HoverTool(tooltips=[
    ("accession", "@accession"),
    ("log(fc)", "@x"),
    ("-log(pvalue)", "@y")
])

TOOLS = ",pan,wheel_zoom,box_zoom,reset,box_select,lasso_select"
p = figure(
    x_axis_label='log(fc)',
    y_axis_label='-log(pvalue)',
    tools=TOOLS,
)
p.add_tools(hover)

# 初始化滑块和参考线
h_slider = Slider(start=0, end=5, value=1, step=.1, title="variation of log(pvalue)")
v_slider_right = Slider(start=0.1, end=0.2, value=0.15, step=.01, title="right fold change")
v_slider_left = Slider(start=0, end=0.1, value=0.05, step=.01, title="left log fold change")

hline = Span(location=h_slider.value, dimension='width', line_color='green', line_width=2)
vline1 = Span(location=v_slider_right.value, dimension='height', line_color='blue', line_width=2)
vline2 = Span(location=v_slider_left.value, dimension='height', line_color='black', line_width=2)
p.renderers.extend([vline1, vline2, hline])

# 绘制圆形标记,绑定position字段的颜色映射
p.circle('x', 'y', source=source, color=dict(field='position', transform=color_mapper), legend='position')

# 1. 参考线更新的回调
h_slider.callback = CustomJS(args=dict(span=hline, slider=h_slider), code="""
    span.location = slider.value;
""")
v_slider_right.callback = CustomJS(args=dict(span=vline1, slider=v_slider_right), code="""
    span.location = slider.value;
""")
v_slider_left.callback = CustomJS(args=dict(span=vline2, slider=v_slider_left), code="""
    span.location = slider.value;
""")

# 2. 颜色更新的回调:修正循环逻辑,修改position字段的每个元素
color_callback = CustomJS(args=dict(source=source, v_left=v_slider_left, h=h_slider, v_right=v_slider_right), code="""
    const data = source.data;
    const low = v_left.value;
    const up = v_right.value;
    const back_val = h.value;
    const x = data['x'];
    const y = data['y'];
    const pos = data['position'];
    
    // 遍历每个数据点,更新position状态
    for (let i = 0; i < x.length; i++) {
        if (x[i] < low && y[i] > back_val) {
            pos[i] = 'down';
        } else if (x[i] > up && y[i] > back_val) {
            pos[i] = 'up';
        } else {
            pos[i] = 'normal';
        }
    }
    
    // 通知数据源更新
    source.change.emit();
""")

# 将颜色回调绑定到三个滑块的value变化事件
v_slider_left.js_on_change('value', color_callback)
v_slider_right.js_on_change('value', color_callback)
h_slider.js_on_change('value', color_callback)

# 3. 数据表格与下载功能
columns = [
    TableColumn(field="accession", title="numero d'accession"),
    TableColumn(field="x", title="log(fc)"),
    TableColumn(field="y", title="-log(pvalue)"),
    TableColumn(field="position", title="status")  # 添加状态列展示当前分类
]
data_table = DataTable(source=source, columns=columns, width=500, height=300)

# 下载按钮:将数据源转换为CSV格式下载
download_button = DownloadButton(label="Download Data", button_type="success")
download_callback = CustomJS(args=dict(source=source), code="""
    const data = source.data;
    const headers = Object.keys(data).join(',');
    const rows = [];
    
    // 构建CSV行
    for (let i = 0; i < data.x.length; i++) {
        const row = Object.values(data).map(col => col[i]).join(',');
        rows.push(row);
    }
    
    // 创建CSV文件并下载
    const csv = headers + '\\n' + rows.join('\\n');
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'data_points.csv';
    a.click();
    URL.revokeObjectURL(url);
""")
download_button.js_on_click(download_callback)

# 布局:表格和下载按钮放在一列
widget_col = column(v_slider_left, v_slider_right, h_slider, data_table, download_button)
layout = row(p, widget_col)

show(layout)

关键修改说明

  • 字段统一:所有颜色相关逻辑都使用position字段,和颜色映射保持一致,避免字段不匹配的问题。
  • 循环修正:使用x[i]和y[i]遍历每个数据点,给pos[i]逐个赋值,确保每个点的状态正确更新。
  • 回调绑定:用js_on_change('value', color_callback)将颜色更新回调绑定到三个滑块上,滑块拖动时立即触发状态更新。
  • 下载功能:添加DownloadButton,通过CustomJS将数据源转换为CSV格式,实现一键下载所有数据。

现在拖动滑块,你会看到:

  • 参考线实时跟随滑块位置移动
  • 数据点的颜色根据滑块阈值自动切换(up/normal/down)
  • 数据表格实时展示每个点的状态,点击下载按钮可以导出所有数据为CSV文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:44:29