Search code examples
javascriptpythonvisualizationbokeh

Bokeh Widget Slicing data


I am trying to create a plot using bokeh to visualize my data on IPython Notebook. I want to add some widgets to make it more interactive. Below is an example of the codes.

from bokeh.models import CustomJS, ColumnDataSource
from bokeh.plotting import Figure, output_notebook, show
from bokeh.models.widgets import Select
from bokeh.layouts import column

output_notebook()

x = [x*0.005 for x in range(0, 200)]
y = x
z = ['A' if i>50 else 'B' for i in range(len(x))]

source = ColumnDataSource(data=dict(x=x, y=y, z=z))

plot = Figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

def callback(source=source):
    data=source.get('data')
    f = cb_obj.get('value')
    x, y, z = data['x'], data['y'], data['z']
    x = [x[i] for i in range(len(x)) if z[i] == f]
    y = [y[i] for i in range(len(y)) if z[i] == f]
    z = [z[i] for i in range(len(z)) if z[i] == f]
    source.trigger('change')

slides = Select(title="Option:", value = 'A', options=['A', 'B'], 
                callback=CustomJS.from_py_func(callback))

layout = column(slider,plot)

show(layout)

enter image description here

I want to make it such that if I choose other option, the plot will change accordingly based on the criteria I specify in the callback function. Any suggestion on why the codes do not work?

P.S. I used the codes from here, but I change the widgets because the problem I am facing is similar to the above http://docs.bokeh.org/en/0.11.1/docs/user_guide/interaction.html#customjs-with-a-python-function


Solution

  • The issue is you are not actually changing the contents of data['x'], data['y'] or data['z'].

    Second issue is, if you change the source data, then you cant change it back as it no longer contains the full data you begun with. Work around is passing in the original data to the callback, and assigning the filtered data to the ColumnDataSource, without changing the original data.

    from bokeh.models import CustomJS, ColumnDataSource
    from bokeh.plotting import Figure, output_notebook, show
    from bokeh.models.widgets import Select
    from bokeh.layouts import column
    
    output_notebook()
    
    x = [x*0.005 for x in range(0, 200)]
    y = x
    z = ['A' if i>50 else 'B' for i in range(len(x))]
    
    original_source = ColumnDataSource(data=dict(x=x, y=y, z=z))
    source = ColumnDataSource(data=dict(x=x, y=y, z=z))
    
    plot = Figure(plot_width=400, plot_height=400)
    plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
    
    def callback(source=source, original_source = original_source):
        data=original_source.data
        s_data = source.data
        f = cb_obj.value
        x, y, z = data['x'], data['y'], data['z']
        x = [x[i] for i in range(len(x)) if z[i] == f]
        y = [y[i] for i in range(len(y)) if z[i] == f]
        z = [z[i] for i in range(len(z)) if z[i] == f]
        s_data['x'] = x
        s_data['y'] = y
        s_data['z'] = z
        source.trigger('change')
    
    slides = Select(title="Option:", value = 'A', options=['A', 'B'], 
                    callback=CustomJS.from_py_func(callback))
    
    layout = column(slides,plot)
    
    show(layout)