使用散景滑块小部件更新绘图数据

updating plot data with bokeh slider widget

我有一个一般性问题,即当小部件触发回调时,应如何使用 Bokeh 中的 CustomJS 脚本更新数据。

下面的代码适合我

import numpy as np

from bokeh.layouts import column, row
from bokeh.models import CustomJS, Slider
from bokeh.plotting import ColumnDataSource, figure, output_file, output_notebook, show
output_notebook()

# Define a slider
my_slider = Slider(start=0, end=2, value=0, step=1, title="Amplitude")

# Produce a list of data to be scrolled with the slider
xy_list = []
for a in range(0,3):
    x_list = np.linspace(0, 10, 500)
    y_list = float(a) * np.sin(x_list)
    xy_list.append(
        ColumnDataSource(data=dict(x=x_list, y=y_list))
    )

# Produce the initial data to be displayed
# NOTE: this is like a hard-coded deepcopy,
# since deepcopy doesn't seem to work well 
# with Bokeh objects
xy_current = ColumnDataSource(
    data=dict(
        x=np.linspace(0, 10, 500), 
        y=0.0*np.linspace(0, 10, 500)
    )
)

# Produce a plot
plot = figure(y_range=(-10, 10), plot_width=200, plot_height=200)
plot.line('x', 'y', source=xy_current, line_width=3, line_alpha=0.6)

# Define a callback for the slider
callback = CustomJS(
    args=dict(
#         source_0=xy_source_0, # An instance of ColumnDataSource
        source_curr=xy_current, # An instance of ColumnDataSource
        source_list=xy_list, # A list, with entries of type ColumnDataSource
    ),
    code="""
    var data_curr = source_curr.data; // This is an instance of bokeh.core.property.wrappers.PropertyValueColumnData
    var plot_i = cb_obj.value // This is an int
    var old_x = data_curr['x'] // This is a numpy.ndarray
    var old_y = data_curr['y'] // This is a numpy.ndarray
    var new_x = source_list[plot_i].data['x'] // This is a numpy.ndarray
    var new_y = source_list[plot_i].data['y'] // This is a numpy.ndarray
    // Now update the y-values for each x, based on the slider value
    for (var i = 0; i < old_x.length; i++) {
        old_x[i] = new_x[i];
        old_y[i] = new_y[i];
    }
    source_curr.change.emit();
""")

# Implement the callback
my_slider.js_on_change('value', callback)

# Show 
layout = row(
    plot,
    my_slider,
)

show(layout)

但是,如果可以替换

,它会更有用(对于与此代码无关的更大项目)
for (var i = 0; i < old_x.length; i++) {
        old_x[i] = new_x[i];
        old_y[i] = new_y[i];
    }

像这样

old_x = new_x
old_y = new_y

我试过这样做,但数据没有得到更新。有人可以解释为什么以及如何实现这种更高级别的数据更改(即不必逐个更改列表的值)吗?


编辑:在 bigreddot 回答后,我将回调脚本更新为以下形式,它使用较少的 ocal 变量。

callback = CustomJS(
    args=dict(
        source_curr=xy_current, # An instance of ColumnDataSource
        source_list=xy_list, # A list, with entries of type ColumnDataSource
    ),
    code="""
    var plot_i = cb_obj.value // This is an int
    // Now update the y-values for each x, based on the slider value
    source_curr.data['x'] = source_list[plot_i].data['x']
    source_curr.data['y'] = source_list[plot_i].data['y']

    source_curr.change.emit();
""")

首先纠正一个误区:

var old_x = data_curr['x'] // This is a numpy.ndarray

这是 JavaScript 在不存在 NumPy 的浏览器中执行的,因此它要么是 JS 类型数组,要么是普通 JS 数组,但绝对不是 numpy.ndarray


您必须实际更新源内部的值。当你这样做时:

var old_x = data_curr['x']

您创建了一个新的局部变量。如果你这样做:

old_x = new_x

那么您所做的就是为局部变量分配一个新值。这根本不会影响数据源

相反,您需要这样的东西:

source_curr.data['x'] = new_x

这实际上修改了数据源的内容。