1

I have just initiated myself to Bokeh library and I would like to add interactivity in my dashboard. To do so, I want to use CheckboxGroup widget in order to select which one of a pandas DataFrame column to plot.
I have followed tutorials but I must have misunderstood the use of ColumnDataSource as I cannot make a simple example work...
I am aware of previous questions on the matter, and one that seems relevant on the StackOverflow forum is the latter : Bokeh not updating plot line update from CheckboxGroup

Sadly I did not succeed in reproducing the right behavior.

I have tried to reproduce an example following the same updating structure presented in Bokeh Server plot not updating as wanted, also it keeps shifting and axis information vanishes by #bigreddot without success.

import numpy as np
import pandas as pd

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.palettes import Spectral
from bokeh.layouts import row
from bokeh.models.widgets import CheckboxGroup
from bokeh.io import curdoc


#  UPDATE FUNCTION ------------------------------------------------
# make update function
def update(attr, old, new):
    feature_selected_test = [feature_checkbox.labels[i] for i in feature_checkbox.active]
    # add index to plot
    feature_selected_test.insert(0, 'index')
    # create new DataFrame
    new_df = dummy_df.filter(feature_selected_test)
    plot_src.data = ColumnDataSource.from_df(data=new_df)


# CREATE DATA SOURCE ------------------------------------------------
# create dummy data for debugging purpose
index = list(range(0, 890))
index.extend(list(range(2376, 3618)))
feature_1 = np.random.rand(len(index))
feature_2 = np.random.rand(len(index))
feature_3 = np.random.rand(len(index))
feature_4 = np.random.rand(len(index))

dummy_df = pd.DataFrame(dict(index=index, feature_1=feature_1, feature_2=feature_2, feature_3=feature_3,feature_4=feature_4))


# CREATE CONTROL ------------------------------------------------------
# list available data to plot
available_feature = list(dummy_df.columns[1:])
# initialize control
feature_checkbox = CheckboxGroup(labels=available_feature, active=[0, 1], name='checkbox')
feature_checkbox.on_change('active', update)


# INITIALIZE DASHBOARD ---------------------------------------------------
# initialize ColumnDataSource object
plot_src = ColumnDataSource(dummy_df)
# create figure
line_fig = figure()
feature_selected = [feature_checkbox.labels[i] for i in feature_checkbox.active]
# feature_selected = ['feature_1', 'feature_2', 'feature_3', 'feature_4']

for index_int, col_name_str in enumerate(feature_selected):
    line_fig.line(x='index', y=col_name_str, line_width=2, color=Spectral[11][index_int % 11], source=plot_src)


curdoc().add_root(row(feature_checkbox, line_fig))

The program should work with a copy/paste... well without interactivity... Would someone please help me ? Thanks a lot in advance.

eidal
  • 156
  • 2
  • 8
  • You should do some print debugging to see that the update function is being called and that it is producing the data you want/expect it to. E.g is the `new_df`` you create actually correct? Also are there any error messages in the browser js console? – bigreddot Jan 22 '20 at 15:20
  • @bigreddot thank you for your reply ! I have used print debugging (and breakpoints in PyCharm) in order to monitor the value of each variable. It was apparently running smoothly : once you check a **box** the function **update** is called and the DataFrame **new_df** is normally updated. As for the **plot_src.data**. The data in the ColumnDataSource object is updated accordingly to the checked box... and then nothing... until you check another box... – eidal Jan 22 '20 at 15:30

1 Answers1

1

You are only adding glyphs for the initial subset of selected features:

for index_int, col_name_str in enumerate(feature_selected):
    line_fig.line(x='index', y=col_name_str, line_width=2, color=Spectral[11][index_int % 11], source=plot_src)

So that is all that is ever going to show.

Adding new columns to the CDS does not automatically make anything in particular happen, it's just extra data that is available for glyphs or hover tools to potentially use. To actually show it, there have to be glyphs configured to display those columns. You could do that, add and remove glyphs dynamically, but it would be far simpler to just add everything once up front, and use the checkbox to toggle only the visibility. There is an example of just this in the repo:

https://github.com/bokeh/bokeh/blob/master/examples/app/line_on_off.py

That example passes the data as literals the the glyph function but you could put all the data in CDS up front, too.

bigreddot
  • 33,642
  • 5
  • 69
  • 122
  • 1
    Thanks a lot for your clear answer. I have misslead myself thinking that a ColumnDataSource would be a sort of magical object ! Bokeh is amazing. Thanks for your work. – eidal Jan 22 '20 at 19:55
  • The modified initialization is then : `instantiate_plot = list() for index_int, col_name_str in enumerate(available_feature): instantiate_plot.append(line_fig.line(x='index', y=col_name_str, line_width=2, color=Spectral[11][index_int % 11], source=plot_src)) instantiate_plot[index_int].visible = False` – eidal Jan 22 '20 at 19:56
  • The update function is : `for index_gr, glyph_renderer in enumerate(instantiate_plot): glyph_renderer.visible = index_gr in feature_checkbox.active` – eidal Jan 22 '20 at 19:57