Using a slider to pick one vector to plot

what i am trying to do:

have a slider that selects data from a list, and plots that. moving the slider should cause the data with index corresponding to the slider value to be plotted

code that i have so far:

# make some fake data
time = np.linspace(0,1,100)
all_traces = [np.random.random(100) + x for x in np.arange(10)]
    
output_notebook()
source = ColumnDataSource(data=dict(x=time, y=all_traces[0],all_traces=all_traces))


trace_fig = figure(width=500, height=500)
trace_fig.line('x','y',source=source)


slider = Slider(start=0, end=10, step=1, value=2, max_height=30, max_width=500)

callback = CustomJS(args=dict(source=source, 
                              slider=slider),
                    code="""
    source.data.y = source.data['all_traces'][slider.value]
    source.change.emit();
""")

slider.js_on_change('value', callback)

show(layout([
        [slider],
        [trace_fig],
       ]))

the problem:

bokeh yells at me that:

BokehUserWarning: ColumnDataSource's columns must be of the same length. Current lengths: ('all_traces', 10), ('x', 100), ('y', 100)

which is correct. and it only plots the first 10 points. any help here would be much appreciated! thank you!

Your code does work, but yeah you probably don’t want to set up your CDS to intrinsically have columns of different lengths - it’ll lead to a lot more problems down the road.

You can avoid this easily by just not passing all_traces to your CDS, and instead passing it directly to the args in the CustomJS:

import numpy as np
from bokeh.models import ColumnDataSource, Slider, CustomJS
from bokeh.layouts import layout
from bokeh.plotting import figure,show
# make some fake data
time = np.linspace(0,1,100)
all_traces = [np.random.random(100) + x for x in np.arange(10)]
source = ColumnDataSource(data=dict(x=time, y=all_traces[0])) #No more all_traces in here


trace_fig = figure(width=500, height=500)
trace_fig.line('x','y',source=source)


slider = Slider(start=0, end=10, step=1, value=2, max_height=30, max_width=500)

callback = CustomJS(args=dict(source=source, all_traces=all_traces, #add all_traces here instead, then access it in the JS code
                              slider=slider),
                    code="""
                        source.data.y = all_traces[slider.value]
                        source.change.emit();
                    """)

slider.js_on_change('value', callback)

show(layout([
        [slider],
        [trace_fig],
       ]))
1 Like

thank you @gmerritt123 ! works perfectly.

@gmerritt123 follow up question if i may:

if i change all_traces to a 2D np.array instead of a list of arrays, it no longer works:


time = np.linspace(0,1,100)
all_traces = np.random.random((100,10))
for i in np.arange(10):
    all_traces[:,i] += i
    

and

callback = CustomJS(args=dict(source=source, 
                              all_traces=all_traces, 
                              slider=slider),
                    code="""
                        source.data.y = all_traces[:,slider.value]
                        source.change.emit();
                    """)

now the slider doesn’t seem to have any effect…please tell me if i’m missing something obviously dumb

When you pass stuff into the args in a CustomJS instance, essentially you are telling bokeh to “translate” this python object into its javascript equivalent, so you can have access to it in standalone callbacks. Insanely powerful and useful. However, some “equivalents” simply do not exist (e.g. you can’t pass a pandas dataframe into a CustomJS callback arg), or exactly what that equivalent is may not be clear cut, and perhaps most importantly in your case some features you take for granted on the python side (i.e. numpy multidim array slicing) will not available to you with their “equivalent” in JS land. This is what you’re trying to do here.

1 Like

i see, thank you!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.