I try to build a dynamic chart with Bokeh and I’m sutcked with JavaScript part, wording the Custom JS callback. I precise that I am absolutely not familiar with JavaScript.
Here is my dataframe :
num_tra num_ts Item annee valeur TRA_label TS_label TRA TS Sensi Cumul 1 1 PVFP 10 62 0 bps 0 bps 0 0 Sensi 62 1 1 PVFP 20 28 0 bps 0 bps 0 0 Sensi 90 1 1 PVFP 30 87 0 bps 0 bps 0 0 Sensi 177 1 2 PVFP 10 25 0 bps - 15 bps 0 -15 Sensi 25 1 2 PVFP 20 95 0 bps - 15 bps 0 -15 Sensi 120 1 2 PVFP 30 95 0 bps - 15 bps 0 -15 Sensi 215 2 1 PVFP 10 49 - 10 bps 0 bps -10 0 Sensi 49 2 1 PVFP 20 17 - 10 bps 0 bps -10 0 Sensi 66 2 1 PVFP 30 98 - 10 bps 0 bps -10 0 Sensi 164 2 2 PVFP 10 83 - 10 bps - 15 bps -10 -15 Sensi 83 2 2 PVFP 20 58 - 10 bps - 15 bps -10 -15 Sensi 141 2 2 PVFP 30 52 - 10 bps - 15 bps -10 -15 Sensi 193 1 1 PVFP 10 44 0 bps 0 bps 0 0 Central 44 1 1 PVFP 20 60 0 bps 0 bps 0 0 Central 104 1 1 PVFP 30 97 0 bps 0 bps 0 0 Central 201 1 2 PVFP 10 82 0 bps - 15 bps 0 -15 Central 82 1 2 PVFP 20 88 0 bps - 15 bps 0 -15 Central 170 1 2 PVFP 30 38 0 bps - 15 bps 0 -15 Central 208 2 1 PVFP 10 58 - 10 bps 0 bps -10 0 Central 58 2 1 PVFP 20 30 - 10 bps 0 bps -10 0 Central 88 2 1 PVFP 30 69 - 10 bps 0 bps -10 0 Central 157 2 2 PVFP 10 2 - 10 bps - 15 bps -10 -15 Central 2 2 2 PVFP 20 62 - 10 bps - 15 bps -10 -15 Central 64 2 2 PVFP 30 69 - 10 bps - 15 bps -10 -15 Central 133
What I am looking for is a chart with two sliders (slider_TRA & slider_TS) based on the values of variables num_tra and num_ts. Finally, I would like to update the sources of the plot depending on the values of the two sliders.
Based on the Bokeh documentation examples, I tried to build the following code, but have no idea how to handle the JS part :
import numpy as np import pandas as pd from bokeh.models import ColumnDataSource, CustomJS, Slider from bokeh.layouts import column, row from bokeh.plotting import ColumnDataSource, figure, output_file, show central=pvfp.loc[pvfp.Sensi=="Central"] sensi=pvfp.loc[pvfp.Sensi=="Sensi"] source1 = ColumnDataSource(central) source2 = ColumnDataSource(sensi) plot = figure(plot_width=400, plot_height=400) plot.line('annee', 'valeur', source=source1) plot.line('annee', 'valeur', source=source2) slider_TRA = Slider(start=1, end=2, value=1, step=1, title="Sensi TRA") slider_TS = Slider(start=1, end=2, value=1, step=1, title="Sensi TS") callback = CustomJS( args=dict(source1=source1,source2=source2, slider_TRA=slider_TRA,slider_TS=slider_TS), code=""" const data1 = source1.data; const data2 = source2.data; const stra = slider_TRA.value; const sts = slider_TS.value; const num_tra1 = data1['num_tra'] const num_ts1 = data1['num_ts'] const num_tra2 = data2['num_tra'] const num_ts2 = data2['num_ts'] for ...some JS to say : num_tra1=num_tra2=stra num_ts1=num_ts2=sts and source1=source1.loc[(source1.num_tra==num_tra1)&(source1.num_ts==num_ts1)] source2=source2.loc[(source2.num_tra==num_tra2)&(source2.num_ts==num_ts2)] source1.change.emit(); source2.change.emit(); """ ) slider_TRA.js_on_change('value', callback) slider_TS.js_on_change('value',callback) layout = row( plot, column(slider_TRA, slider_TS), ) show(layout)
As mentioned above, I’m not famliliar with JS and I’m looking for someone who can help me out. If you have any ideas or suggestions, it would be very appreciated.
Advertisement
Answer
This solution works for Bokeh v2.3.0.
You need to pass the complete data to the callback function and make there filtering based on the slider values. But you cannot assign the resulting filtered data to the original data as you will loose information this way. So you should assign the filtered data to the data_source
object of the corresponding glyphs.
Also the starting data for both lines should get filtered according to initial slider positions.
import os import pandas as pd from bokeh.models import ColumnDataSource, CustomJS, Slider, BooleanFilter, CDSView from bokeh.layouts import column, row from bokeh.plotting import figure, show pvfp = pd.read_csv(os.path.join(os.path.dirname(__file__), 'data', 'data.csv'), sep = ",") central = pvfp.loc[pvfp.Sensi=="Central"] sensi = pvfp.loc[pvfp.Sensi=="Sensi"] min_tra_central = central['num_tra'].min() max_tra_central = central['num_tra'].max() min_ts_central = central['num_ts'].min() max_ts_central = central['num_ts'].max() min_tra_sensi = sensi['num_tra'].min() max_tra_sensi = sensi['num_tra'].max() min_ts_sensi = sensi['num_ts'].min() max_ts_sensi = sensi['num_ts'].max() start_tra = min(min_tra_central, min_tra_sensi) start_ts = min(min_ts_central, min_ts_sensi) end_tra = max(max_tra_central, max_tra_sensi) end_ts = max(max_ts_central, max_ts_sensi) slider_TRA = Slider(start = start_tra, end = end_tra, value=start_tra, step=1, title="Sensi TRA", show_value = False) slider_TS = Slider(start = start_ts, end = end_ts, value=start_ts, step=1, title="Sensi TS", show_value = False) ########################################################################## plot = figure(plot_width=400, plot_height=400) source_central = ColumnDataSource(central) source_sensi = ColumnDataSource(sensi) source_start_central = central.loc[(central['num_tra'] == start_tra) & (central['num_ts'] == start_ts)] source_start_sensi = sensi.loc[(sensi['num_tra'] == start_tra) & (sensi['num_ts'] == start_ts)] line_central = plot.line('annee', 'valeur', color = 'red', source=source_start_central) line_sensi = plot.line('annee', 'valeur', color = 'blue', source=source_start_sensi) ########################################################################## callback = CustomJS( args=dict(source_central=source_central, source_sensi=source_sensi, line_central=line_central, line_sensi=line_sensi, slider_TRA=slider_TRA, slider_TS=slider_TS), code=""" const data_central = source_central.data; const data_sensi = source_sensi.data; const tra_value = slider_TRA.value; const ts_value = slider_TS.value; var new_central_y = [] var new_sensi_y = [] for (var i=0; i<data_central['num_tra'].length; i++) { if(data_central['num_tra'][i] == tra_value && data_central['num_ts'][i] == ts_value) { new_central_y.push(data_central['valeur'][i]) } if(data_sensi['num_tra'][i] == tra_value && data_sensi['num_ts'][i] == ts_value) { new_sensi_y.push(data_sensi['valeur'][i]) } } line_central.data_source.data['valeur'] = new_central_y line_sensi.data_source.data['valeur'] = new_sensi_y line_central.data_source.change.emit(); line_sensi.data_source.change.emit(); """ ) slider_TRA.js_on_change('value', callback) slider_TS.js_on_change('value', callback) layout = row( plot, column(slider_TRA, slider_TS), ) show(layout)