首页 > 解决方案 > Bokeh & Custom JS - 使用滑块更新多线图

问题描述

我正在使用 Bokeh 生成交互式时间序列图。可以同时显示 n 个系列。每个系列将显示从 t= 0 到 t = x,其中 x 是滑块创建的值。

我使用 ColumnDataSource 来包含所有内容,使用 MultiLine 字形显示系列,使用 Slider 用于滑块,使用 CustomJS 来控制更新交互。

from bokeh.models import CustomJS, ColumnDataSource, Slider, Plot
from bokeh.models.glyph import MultiLine
from bokeh.io import show
from bokeh.layouts import column

data_dict = {'lons':[[-1.0, -1.1, -1.2, -1.3, -1.4], [-1.0, -1.1, -1.25, -1.35, -1.45]], 'lats':[[53.0, 53.1, 53.2, 53.3, 53.4], [53.05, 53.15, 53.25, 53.35, 53.45]]}

source = ColumnDataSource(data_dict)
p = Plot(title = None, plot_width = 400, plot_height = 400)
glyph = MultiLine(xs = 'lons', ys = 'lats')
p.add_glyph(source, glyph)

callback = CustomJS(args = dict(source = source), code = """
    var data = source.data;
    var time = time.value;
    var lons = data['lons']
    var lats = data['lats']
    var runners = lons.length()
    var new_lons = []
    var new_lats = []

    for(i=0; i<runners; i++{
        var runner_lons = lons[i].slice(0, time)
        var runner_lats = lats[i].slice(0, time)
        new_lons.push(runner_lons)
        new_lats.push(runner_lats)
    }
    lons = new_lons
    lats = new_lats

    source.change.emit();
    """)

slider = Slider(start = 0, , end = 5, value = 0, step = 1, callback = callback)
layout = column(p, slider)
callback.args["time"] = slider

show(layout)

此代码呈现图形,绘制的两条线覆盖source.data.

移动滑块将按预期更新lons&中的数据lats,但图形显示不会更新。

非常感谢您的指点,建议,建议,解释!

标签: javascriptpythonbokeh

解决方案


其他答案都是部分正确的,但不完整或存在各种问题。主要缺失的部分是,如果每次滑块移动时都对原始数据源进行切片,那么在第一个滑块移动之后,您现在不再对原始数据进行切片,所以事情将无法正常工作。您需要单独发送完整的原始数据,并始终从原始数据中复制您想要的子部分。这是一个完整的工作脚本:

from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure

data_dict = {
    'lons':[[-1.0, -1.1, -1.2, -1.3, -1.4], [-1.0, -1.1, -1.25, -1.35, -1.45]],
    'lats':[[53.0, 53.1, 53.2, 53.3, 53.4], [53.05, 53.15, 53.25, 53.35, 53.45]]
}

full_source = ColumnDataSource(data_dict)
source = ColumnDataSource(data_dict)

p = figure(plot_width=400, plot_height=400, tools="")
p.multi_line(xs='lons', ys='lats', source=source)

callback = CustomJS(args = dict(source=source, full_source=full_source), code = """
    const time = cb_obj.value;
    const full_lons = full_source.data['lons']
    const full_lats = full_source.data['lats']

    for(i=0; i<full_lons.length; i++) {
        source.data['lons'][i] = full_lons[i].slice(0, time)
        source.data['lats'][i] = full_lats[i].slice(0, time)
    }
    // only need this because source.data is being updated "in place"
    source.change.emit()
    """)

slider = Slider(start = 0, end = 5, value = 0, step = 1, callback = callback)
slider.js_on_change('value', callback)
layout = column(p, slider)

show(layout)

我已经更新了使用figurefrom的代码,bokeh.plotting使其更简单,并且还获得了默认轴等。还值得注意的是,滑块值 0 可能没有意义,带有它的图将(正确地)为空。


推荐阅读