обновление данных графика с помощью виджета слайдера Bokeh - PullRequest
0 голосов
/ 19 марта 2020

У меня есть общий вопрос о том, как данные должны обновляться с помощью пользовательских JS сценариев в Bokeh, когда виджет запускает обратный вызов.

Следующий код прекрасно работает для меня

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();
""")

1 Ответ

0 голосов
/ 19 марта 2020

Прежде всего, чтобы исправить ошибочное представление:

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

Это JavaScript выполняется в браузере, где NumPy не существует, так что это либо JS Typed Array, либо простой массив JS, но определенно не numpy.ndarray.


Вы должны фактически обновить значения внутри источника . Когда вы делаете:

var old_x = data_curr['x']

Вы создаете новую локальную переменную . Если вы затем выполните:

old_x = new_x

Тогда все, что вы сделали, - это присвоили новое значение локальной переменной . Это никак не влияет на источник данных .

Вместо этого вам нужно что-то вроде:

source_curr.data['x'] = new_x

Это фактически изменяет содержимое источник данных.

...