I was following the recommendations of @p-himik and @carolyn to deal with multiple lines and callbacks. The included code shows my example. My problem is how to change interactively the number of lines of graph? In line with the approach of @p-himik the problem is how to change the number of elements in the line_renderers variable? In other words how to build new glyphs through one or more callbacks?
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS, MultiChoice, HoverTool
from bokeh.layouts import row, column, layout
from bokeh.plotting import figure, show
from bokeh.palettes import Category20_20
df = pd.DataFrame(data={'ILLUMINANT': {0: 'l33t', 1: 'l33t', 2: 'l33t', 3: 'l33t', 4: 'l33t', 5: 'l33t', 6: 'l33t', 7: 'l33t', 8: 'l33t', 9: 'l33t', 10: 'l33t', 11: 'l33t', 12: 'noob', 13: 'noob', 14: 'noob', 15: 'noob', 16: 'noob', 17: 'noob', 18: 'noob', 19: 'noob', 20: 'noob', 21: 'noob', 22: 'noob', 23: 'noob'}, 'WAVELENGTH': {0: 0, 1: 0, 2: 0, 3: 1, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 3, 10: 3, 11: 3, 12: 0, 13: 0, 14: 0, 15: 1, 16: 1, 17: 1, 18: 2, 19: 2, 20: 2, 21: 3, 22: 3, 23: 3}, 'Label': {0: 'year01', 1: 'year02', 2: 'year03', 3: 'year01', 4: 'year02', 5: 'year03', 6: 'year01', 7: 'year02', 8: 'year03', 9: 'year01', 10: 'year02', 11: 'year03', 12: 'year01', 13: 'year02', 14: 'year03', 15: 'year01', 16: 'year02', 17: 'year03', 18: 'year01', 19: 'year02', 20: 'year03', 21: 'year01', 22: 'year02', 23: 'year03'}, 'Data': {0: 0.0010402764, 1: 0.0011201306, 2: 0.0011656343, 3: 8.00911e-05, 4: 0.0001257282, 5: 0.00016172540000000002, 6: 4.56483e-05, 7: 8.16531e-05, 8: 0.0001140501, 9: 3.60118e-05, 10: 6.841e-05, 11: 0.0001036948, 12: 0.0012474055, 13: 0.0013409409, 14: 0.001397252, 15: 9.38607e-05, 16: 0.0001503632, 17: 0.00020415279999999998, 18: 5.65171e-05, 19: 0.0001103194, 20: 0.00014596190000000002, 21: 5.38097e-05, 22: 8.94597e-05, 23: 0.00012082139999999999}})
df = df.astype('object')
source_inicial = ColumnDataSource(data=df)
ILLUMINANTSS = df.ILLUMINANT.unique().tolist()
ILLUMINANT_select = MultiChoice(value=list(ILLUMINANTSS[:1]), options=ILLUMINANTSS, title="Illuminants")
df_filtered = df[df["ILLUMINANT"].isin(ILLUMINANT_select.value)]
CURVASS = df_filtered.Label.unique().tolist()
line_fig = figure(x_range=(0, 3), y_range=(0, .002), sizing_mode="stretch_both")
hover_lines = HoverTool(
tooltips=[
('Illuminant', '@ILLUMINANT'),
('Curva', '@Label'),
('SRF', '@Data'),
('Wavelength', '@WAVELENGTH')
]
)
line_fig.add_tools(hover_lines)
line_renderers = []
line_renderer_source_subsets = []
for cause, color in zip(CURVASS, Category20_20):
r = line_fig.line(x='WAVELENGTH'
, y='Data'
, color=color
, line_width=3
, source=ColumnDataSource(data=df_filtered[df_filtered.Label == cause].reset_index(drop=True))
, legend_label=str(cause)
)
# print(cause, color)
line_renderers.append(r)
# for each line, maintain a subset that is filtered by CURVAS but not by ILLUMINANT; this is the "master" source
# for that line, which will then be filtered by ILLUMINANT in the ILLUMINANT callback.
line_renderer_source_subsets.append(ColumnDataSource(data=df_filtered[df_filtered.Label == cause].reset_index(drop=True)))
fields_to_update = ['index'] + list(df_filtered.columns.values)
ILLUMINANT_select_callback = CustomJS(args=dict(source_inicial=source_inicial,
line_renderers=line_renderers,
line_renderer_source_subsets=line_renderer_source_subsets,
ILLUMINANT_select=ILLUMINANT_select,
fields_to_update=fields_to_update), code="""
for (var m = 0; m < line_renderers.length; m++) {
line_renderers[m].data_source.clear();
for (var n = 0; n < line_renderer_source_subsets[m].data.index.length; n++) {
for (var o = 0; o < source_inicial.data.index.length; o++) {
if (source_inicial.data.ILLUMINANT[o] == ILLUMINANT_select.value & source_inicial.data.Label[o] == line_renderer_source_subsets[m].data.Label[n] & source_inicial.data.WAVELENGTH[o] == line_renderer_source_subsets[m].data.WAVELENGTH[n]) {
for (var f in fields_to_update) {
var field = fields_to_update[f];
line_renderers[m].data_source.data[field].push(source_inicial.data[field][o]);
}
}
}
}
line_renderers[m].data_source.change.emit();
}
""")
ILLUMINANT_select.js_on_change('value', ILLUMINANT_select_callback )
top_area = row(ILLUMINANT_select)
show(layout(column([top_area, line_fig]), sizing_mode="stretch_both"))
I have tried using CDSview but it does not work with multiple lines. In my low level of understanding, also it is not possible to filter the source before the loop using a callback, is that correct?
Thank you for your suggestion and comments.