I am trying to update a worldmap tooltip using a slicer or dropdown select. I got following question which sorted the most of the stuff for a Bokeh Slider custom JS callback
import pandas as pd
import random
from datetime import timedelta
df = pd.DataFrame({'base' : ["2017-01-01" for t in range(10000)],
'Date' : [random.randint(0, 1035) for t in range(10000)],
'Sales' : [random.random() for t in range(10000)]})
df['base'] = pd.to_datetime(df['base'])
df["Date2"] = df.apply(lambda x: x["base"] + timedelta(days=x['Date']), axis=1)
df.drop(['base', 'Date'], axis=1, inplace=True)
df.set_index('Date2', inplace=True)
df['month'] = df.index.month
df['year'] = df.index.year
df['day'] = df.index.day
df.head()
from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file
from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool
source1=df.groupby(['year','month','day'], as_index = False).sum()
source = source1[source1['year']== 2017]
sourcex = source[source['month'] ==1]
Overall=ColumnDataSource(source)
Curr=ColumnDataSource(sourcex)
boolinit = source['month']==1
view = CDSView(source=Overall, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('day', '#day'),('Sales','#{Sales}{0,0}')],
formatters = {'day': 'datetime','Sales': 'numeral'})
p = figure(title='YEARLY SALES', plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],
toolbar_location="above")
r = p.vbar(x='day', top='Sales', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'
callback = CustomJS(args=dict(source=Overall, sc=Curr), code="""
var f = select.value;
sc.data['day'] = [];
sc.data['Sales'] = [];
for (var i = 0; i <= source.get_length(); i++){
if (source.data['month'][i] == f){
sc.data['day'].push(source.data['day'][i])
sc.data['Sales'].push(source.data['Sales'][i])
}
}
sc.change.emit();
""")
select = Select(options=["1","2","3"], title="Month", callback=callback)
callback.args["select"] = select
layout = column(select, p)
#Display plot inline in Jupyter notebook
output_notebook()
output_file("Filterdata.html")
show(layout)
Now, I replicated the same for a worldmap as below:
import pandas as pd
import geopandas as gpd
current_week = 4
shapefile = 'data/countries_110m/ne_110m_admin_0_countries.shp'
gdf = gpd.read_file(shapefile)[['ADMIN', 'ADM0_A3', 'geometry']]
gdf.columns = ['country', 'country_code', 'geometry']
gdf = gdf.drop(gdf.index[159])
df = pd.DataFrame({'Country':['India','India'],
'SalesGain':['10%','20%'],
'Week':[4,5],
'Color':[0.2,0.4]
})
import json
from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file
from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.palettes import brewer
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool,GeoJSONDataSource, LinearColorMapper, ColorBar
from bokeh.plotting import figure, output_file, show
output_file("worldmap.html")
merged = gdf.merge(df, left_on = 'country', right_on = 'Country', how = 'left')
merged_json = json.loads(merged.to_json())
json_data = json.dumps(merged_json)
geosource_all = GeoJSONDataSource(geojson = json_data)
df_curr = df[df['Week']==current_week]
merged_curr = gdf.merge(df_curr, left_on = 'country', right_on = 'Country', how = 'left')
merged_json_curr = json.loads(merged_curr.to_json())
json_data_curr = json.dumps(merged_json_curr)
geosource_curr = GeoJSONDataSource(geojson = json_data_curr)
# boolinit = merged['Week']!=current_week
boolinit = merged['Week']==current_week
view = CDSView(source=geosource_all, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('Country', '#Country'),('Sales','#SalesGain')])
#Define a sequential multi-hue color palette.
palette = brewer['YlGnBu'][8]
#Reverse color order so that dark blue is highest value
palette = palette[::-1]
#Instantiate LinearColorMapper that linearly maps numbers in a range, into a sequence of colors. Input nan_color.
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 12, nan_color = '#d9d9d9')
#Define custom tick labels for color bar.
tick_labels = {'0': '0', '2':'2%', '4':'4%', '6':'6%', '8':'8%','10':'10%','12':'12%'}
#Create color bar.
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=6,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal', major_label_overrides = tick_labels)
#Create figure object.
p = figure(title='Covid-19 Impact', plot_width=900, plot_height=600, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],toolbar_location="above")
p.title.text_font_size = '20pt'
p.title.text_color = "darkblue"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
#Add patch renderer to figure.
p.patches('xs','ys', source = geosource_curr,fill_color = {'field' :'Color', 'transform' : color_mapper},
line_color = 'black', line_width = 0.25, fill_alpha = 1)
p.add_layout(color_bar, 'below')
callback = CustomJS(args=dict(source=geosource_all, sc=geosource_curr), code="""
var f = slider.value;
sc.data['Country'] = [];
sc.data['Week'] = [];
sc.data['SalesGain'] = [];
for (var i = 0; i <= source.get_length(); i++){
if ((source.data['Week'][i] == f ) || (source.data['Country'][i] == null) ){
sc.data['SalesGain'].push(source.data['SalesGain'][i])
sc.data['Week'].push(source.data['Week'][i])
sc.data['Country'].push(source.data['Country'][i])
}
}
sc.change.emit();
""")
# select = Select(options=["201951","201952","201953"], title="Week", callback=callback)
# callback.args["select"] = select
# layout = column(select, p)
slider = Slider(start=1, end=5, value=current_week, step=1, title="Month", callback=callback)
callback.args["slider"] = slider
layout = column(slider, p)
#Display plot inline in Jupyter notebook
output_notebook()
show(layout)
But in this case, as soon as I click on the slider, tooltip data vanish away. World map input file can be found here to smoothly run the code:
https://github.com/CrazyDaffodils/Interactive-Choropleth-Map-Using-Python/tree/master/bokeh-app/data
A tooltip disappears after a small delay after you move your mouse off the glyph that has triggered it.
Right now, Bokeh doesn't have any built-in way of changing that behavior. There's an open issue for that with a workaround that you might be able to adapt to your needs: https://github.com/bokeh/bokeh/issues/5724
Related
I'm new in Bokeh, and I'm trying to construct an interactive scatter plot.
In this plot, I would like to change the p-value cut-off and, then, change the colors according to the rule (if FC < 0, the dots are blue; if FC > 0, dots are red.
I've tried:
from bokeh.plotting import ColumnDataSource, figure, output_file, show, curdoc
from bokeh.models import BoxSelectTool, LassoSelectTool,CustomJS, Slider
from bokeh.transform import linear_cmap
from bokeh.layouts import gridplot, column, row
import pandas as pd
import numpy as np
fc = np.random.uniform(0, 4, 1000)
p_value = np.random.uniform(0,1,1000)
df = pd.DataFrame([fc, p_value], ['FC', 'pvalue']).transpose()
df['log2(fc)'] = np.log2(df['FC'])
df['-log10(p)'] = -np.log10(df['pvalue'])
output_file("df.html")
y = df['-log10(p)']
x = df['log2(fc)']
col = np.where(y> -np.log10(0.5), np.where( x>-0, np.where( x<0, '#606060','#E4001B'), '#6194BC'), '#606060' )
source = ColumnDataSource(data=dict(
x=df['log2(fc)'],
y=df['-log10(p)'],
pvalue=df['pvalue'],
FC = df['log2(fc)'],
color = col))
TOOLTIPS = [
("FC", "#FC"),
("pvalue", "#pvalue")
]
TOOLS="hover,crosshair,pan,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
p = figure(plot_width=500, plot_height=500, tooltips=TOOLTIPS,
title="test", tools=TOOLS)
p.circle('x', 'y', size=5, source=source, alpha = 0.5, fill_color = 'color', line_color='color')
p.xaxis.axis_label = "log2(FC)"
p.yaxis.axis_label = "-log10(p)"
p.background_fill_color = "#fafafa"
p_slider = Slider(start=0.0, end=5, step=.01, title="p_value")
callback = CustomJS(args=dict(source=source,plot=p, color_mapper = col, pvalue=p_slider),
code="""
const data = source.data;
var P = pvalue.value;
const x = df['p_value']
const y = df['log2(fc)']
source.change.emit();
""")
p_slider.js_on_change('value', callback)
layout = row(
p,
column(p_slider),
)
show(layout)
Despite this approach show me the bar to control the p-value, I'm not getting to control the bar, neither change the colors according to p-value...
Thanks for your help!
I am trying to change a NetworkX graph by clicking a value in bokeh data table. I can see and visualize the required value in the layout box but the same value is not getting used to display/ change the NetworkX Graph.
I have tried using python call back, but its not working either.It says "Only JavaScript callbacks may be used with standalone output".
#from bokeh.io import vplot
from bokeh.plotting import figure
from bokeh.models.graphs import
from_networkx,NodesAndLinkedEdges,EdgesAndLinkedNodes
from bokeh.plotting import figure
from bokeh.models import Plot,Range1d
from bokeh.io import output_file, show
from datetime import date
from random import randint
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter,
TableColumn,Spinner
import bokeh.layouts as layouts
import bokeh.models.widgets as widgets
from bokeh.io import curdoc
source= ColumnDataSource(dict(CUSTOMER_NO = pr, priority =
np.arange(1,len(pr)+1,1)))
columns = [TableColumn(field = "CUSTOMER_NO", title = "CUSTOMER_NO"),
TableColumn(field = "priority", title = "PRIORITY")]
data_table = DataTable(source = source, columns = columns, width = 200,
height = 280, editable = True, reorderable = False)
text_row = TextInput(value = None, title = "Row index:", width = 420)
text_CUSTOMER_NO = TextInput(value = None, title = "CUSTOMER_NO:", width
= 420)
text_priority = TextInput(value = None, title = "priority:", width = 420)
plot=figure(title='test',x_range=Range1d(-1.1,1.1),
y_range=Range1d(-1.1,1.1),tools="",toolbar_location=None)
s =
G.subgraph(nx.shortest_path(G.to_undirected(),text_CUSTOMER_NO.value))
pos=nx.planar_layout(s)
graph=from_networkx(s,pos)
plot.renderers.append(graph)
source_code = """
row = cb_obj.indices[0]
text_row.value = String(row);
text_CUSTOMER_NO.value = String(source.data['CUSTOMER_NO'][row])
text_priority.value = String(source.data['priority'][row]);"""
callback = CustomJS(args = dict(source = source, text_row =
text_row,text_CUSTOMER_NO=text_CUSTOMER_NO,text_priority=text_priority),
code = source_code)
source.selected.js_on_change('indices', callback)
layout = column(data_table, text_row,text_CUSTOMER_NO,text_priority,
plot)
#show(layout)
bokeh.io.output_notebook()
bokeh.io.show(layout)
#bokeh.io.show(plot)
There is no error message given, the output shows a correct bokeh data table, the clicks on the customer no shows me correct values in layout boxs but the networkx shows a black triangle.
I was able to somehow achieve this using python callback.Can some one please help me to achieve the same using CustomJS. The code is below.
from datetime import date
from random import randint
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import bokeh.layouts as layouts
import bokeh.models.widgets as widgets
from bokeh.io import curdoc
from bokeh.io import vplot
from bokeh.plotting import figure
from bokeh.models.graphs import
from_networkx,NodesAndLinkedEdges,EdgesAndLinkedNodes
from bokeh.plotting import figure
from bokeh.models import Plot,Range1d
from bokeh.io import output_file, show
from datetime import date
from random import randint
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import bokeh.layouts as layouts
import bokeh.models.widgets as widgets
from bokeh.io import curdoc
G = nx.Graph()
G.add_nodes_from([1,2,3,4,5,6,7,8,9,10])
G.add_edges_from([(1, 2), (1, 3),(3,4),(5,6),(6,7),(8,9),(9,10)])
data = dict(
CUSTOMER_NO=[1,2,3,4,5,6,7,8,9,10],
priority=[1,2,3,4,5,6,7,8,9,10],
)
d_source= ColumnDataSource(data)
columns = [TableColumn(field = "CUSTOMER_NO", title = "CUSTOMER_NO"), TableColumn(field = "priority", title = "PRIORITY")]
data_table = DataTable(source = source, columns = columns, width = 200, height = 280, editable = True, reorderable = False)
def table_select_callback(attr, old, new):
# print 'here'
# print new
# selected_row = new['1d']['indices'][0]
selected_row = new[0]
s = G.subgraph(nx.shortest_path(G.to_undirected(), data['CUSTOMER_NO'][selected_row]))
#download_count = data['downloads'][selected_row]
#chart_data = np.random.uniform(0, 100, size=download_count)
plot = figure(title='test',x_range=Range1d(-1.1,1.1),y_range=Range1d(-1.1,1.1), tools = ['reset', 'pan','wheel_zoom','save', 'lasso_select', ])
#p = figure(title='bla')
pos=nx.random_layout(s)
graph=from_networkx(s,pos)
plot.renderers.append(graph)
#r = p.line(x=range(len(chart_data)), y=chart_data)
root_layout.children[1] = plot
d_source.selected.on_change('indices', table_select_callback)
root_layout = layouts.Column(data_table, widgets.Div(text='Select customer number'))
curdoc().add_root(root_layout)
enter image description here I want to change bokeh chart at run time when click on a radio button. Here is what I've tried till now:
import numpy as np
import pandas as pd
from bokeh.core.properties import value
from bokeh.models.widgets import Paragraph,PreText,RadioButtonGroup
from bokeh.layouts import widgetbox
from bokeh.models.widgets import CheckboxGroup, RadioGroup
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource,LabelSet,CustomJS,Row
from bokeh.plotting import figure, show,save
from bokeh.transform import dodge
from bokeh.palettes import Viridis
colors = ["#c9d9d3", "#718dbf", "#e84d60"]
dataframe = pd.read_csv('Experience.csv')
source = ColumnDataSource(dataframe)
exp = dataframe['Experience']
ys = list(dataframe.keys())
ys.remove('Experience')
TOOLTIPS = [("Experience", "#Experience")]
p = figure(x_range=exp, y_range=(0, 100), plot_height=350, tools=['hover','save','reset','zoom_out','zoom_in','pan','box_zoom'],tooltips=TOOLTIPS)
stacked = p.vbar_stack(stackers=ys, x='Experience',color=colors,source=source,legend=[value(x) for x in ys],name=ys,width=0.5,)
colorList = Viridis[len(ys)]
labels = []
for y, offset, color in zip(ys, [-0.25, 0, 0.25], colorList):
bar = p.vbar(x=dodge('Experience', offset, range=p.x_range), top=y, width=0.2, source=source, legend=y + ' ', color=color)
bar.visible = False
radiogroup = RadioGroup(labels = ["StackedBar", "Bar"], active = 0,)
radiogroup.callback = CustomJS(args = dict(stacked = stacked, bar = bar), code = """
for (i in stacked)
stacked[i].visible = false;
bar.visible = false;
if (cb_obj.active == 0)
for (i in stacked)
stacked[i].visible = true;
else if (cb_obj.active == 1)
bar.visible = true; """)
layout = Row(p, radiogroup)
show(layout)
It is showing two graphs in one figure, but I want bar graph default and when I click on the radio button the graph should change based on click event.here is my full code..pl check and tell what i am doing wrong
The vbar_stack returns a list of glyphs so you need to toggle visibility each of them separately like this:
from bokeh.plotting import show, figure
from bokeh.models import RadioGroup, CustomJS, Row
from bokeh.models.sources import ColumnDataSource
import pandas as pd
data = {'fruits' : ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries'],
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 4, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]}
df = pd.DataFrame(data)
df['total'] = df.sum(axis = 1)
p = figure(x_range = data['fruits'], title = "Fruit Counts by Year", tools = "hover", tooltips = "$name #fruits: #$name")
vbar_stack = p.vbar_stack(["2015", "2016", "2017"], x = 'fruits', width = 0.9, color = ["#c9d9d3", "#718dbf", "#e84d60"], source = data)
vbar = p.vbar(x = 'fruits', width = 0.5, top = 'total', source = ColumnDataSource(df))
vbar.visible = False
radiogroup = RadioGroup(labels = ["StackedBar", "Bar"], active = 0,)
radiogroup.callback = CustomJS(args = dict(vbar_stack = vbar_stack, vbar = vbar), code = """
for (i in vbar_stack)
vbar_stack[i].visible = false;
vbar.visible = false;
if (cb_obj.active == 0)
for (i in vbar_stack)
vbar_stack[i].visible = true;
else if (cb_obj.active == 1)
vbar.visible = true; """)
layout = Row(p, radiogroup)
show(layout)
See below your code with some small corrections:
import os
import numpy as np
import pandas as pd
from bokeh.core.properties import value
from bokeh.models.widgets import Paragraph, PreText, RadioButtonGroup
from bokeh.layouts import widgetbox
from bokeh.models.widgets import CheckboxGroup, RadioGroup
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, LabelSet, CustomJS, Row
from bokeh.plotting import figure, show, save
from bokeh.transform import dodge
from bokeh.palettes import Viridis
colors = ["#c9d9d3", "#718dbf", "#e84d60"]
dataframe = pd.read_csv(os.path.join(os.path.dirname(__file__), 'Experience.csv'))
source = ColumnDataSource(dataframe)
exp = dataframe['Experience']
ys = list(dataframe.keys())
ys.remove('Experience')
TOOLTIPS = [("Experience", "#Experience")]
p = figure(x_range = exp, y_range = (0, 100), plot_height = 350, tools = ['hover', 'save', 'reset', 'zoom_out', 'zoom_in', 'pan', 'box_zoom'], tooltips = TOOLTIPS)
stacked = p.vbar_stack(stackers = ys, x = 'Experience', color = colors, source = source, legend = [value(x) for x in ys], name = ys, width = 0.5,)
colorList = Viridis[len(ys)]
labels = []
bars = []
for y, offset, color in zip(ys, [-0.25, 0, 0.25], colors):
bar = p.vbar(x = dodge('Experience', offset, range = p.x_range), top = y, width = 0.2, source = source, color = color)
bar.visible = False
bars.append(bar)
radiogroup = RadioGroup(labels = ["StackedBar", "Bar"], active = 0,)
radiogroup.callback = CustomJS(args = dict(stacked = stacked, bars = bars), code = """
for (i in stacked)
stacked[i].visible = false;
for (i in bars)
bars[i].visible = false;
if (cb_obj.active == 0)
for (i in stacked)
stacked[i].visible = true;
else if (cb_obj.active == 1)
for (i in bars)
bars[i].visible = true; """)
layout = Row(p, radiogroup)
show(layout)
while using bokeh tap tool with bar chart I need to get the id of selected bar.how can I get this id of each bar without using customJS
the code is running using bokeh server
source = ColumnDataSource(data=df)
p = figure(x_range=source.data['month'], plot_height=600, toolbar_location=None, tools="tap,hover", title="month analysis")
p.vbar(x='month', top='count', width=0.5, source=source, legend="month",
line_color='white', fill_color=factor_cmap('month', palette=Spectral6, factors=df['month']))
hover = HoverTool(tooltips=[("count", "#count")])
p.legend.orientation = "horizontal"
p.legend.location = "top_right"
taptool = p.select(type=TapTool)
curdoc().add_root(row( p, width=800))
This should work. The ID of a selected glyph can be found in source.selected.indices.
#!/usr/bin/python3
import pandas as pd
from bokeh.plotting import figure, show, curdoc
from bokeh.io import output_file
from bokeh.models import ColumnDataSource, HoverTool, TapTool
from bokeh.transform import factor_cmap
from bokeh.palettes import Spectral6
from bokeh.layouts import row
from bokeh.events import Tap
df = pd.read_csv('data.csv')
df['count'] = df['count'].astype(dtype='int32')
source = ColumnDataSource(data=df)
p = figure(x_range=source.data['month'], plot_height=600, toolbar_location=None, tools="tap,hover", title="month analysis")
p.vbar(x='month', top='count', width=0.5, source=source, legend="month",
line_color='white', fill_color=factor_cmap('month', palette=Spectral6, factors=df['month']))
hover = HoverTool(tooltips=[("count", "#count")])
p.legend.orientation = "horizontal"
p.legend.location = "top_right"
taptool = p.select(type=TapTool)
def callback(event):
selected = source.selected.indices
print(selected)
p.on_event(Tap, callback)
curdoc().add_root(row( p, width=800))
I am trying to implement checkboxes in bokeh where each checkbox should show/hide the line associated with it. I'm aware it's possible to achieve this with legends, but I want this effect to happen in two plots at the same time. Also, the legend should update as well. In the example below the checkboxes appear, but do nothing. I am clearly not grasping how to update de dataframe used as source. Thanks for any help.
from bokeh.io import show, curdoc
from bokeh.models import HoverTool, ColumnDataSource, Legend
from bokeh.plotting import figure
from bokeh.palettes import Category10
from bokeh.models.widgets import CheckboxGroup
from bokeh.layouts import row
import pandas as pd
def update(atrr, old, new):
lines_to_plot = [checkbox_group.labels[i] for i in checkbox_group.active]
cols = ['x']
for label in lines_to_plot:
cols += [label + 'y']
cols += [label]
newdf = df0[cols]
source.data.update(ColumnDataSource(newdf))
df0 = pd.DataFrame({'x': [1, 2, 3], 'Ay' : [1, 5, 3], 'A': [0.2, 0.1, 0.2], 'By' : [2, 4, 3], 'B':[0.1, 0.3, 0.2]})
columns = ['A', 'B']
checkbox_group = CheckboxGroup(labels=columns, active=[0, 1])
tools_to_show = 'box_zoom,save,hover,reset'
p = figure(plot_height =300, plot_width = 1200,
toolbar_location='above',
tools=tools_to_show)
legend_it = []
color = Category10[10]
columns = ['A', 'B']
source = ColumnDataSource(df0)
for i, col in enumerate(columns):
c = p.line('x', col, source=source, name=col, color=color[i])
legend_it.append((col, [c]))
legend = Legend(items=legend_it, location=(5,114))#(0, -60))
p.add_layout(legend, 'right')
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Name","$name"), ("Aux", "#$name")]
hover.mode = 'mouse'
layout = row(p,checkbox_group)
checkbox_group.on_change('active', update)
curdoc().add_root(layout)
You will have to manage LegendItem objects manually. Here is a complete example:
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row
from bokeh.palettes import Viridis3
from bokeh.plotting import figure
from bokeh.models import CheckboxGroup, Legend, LegendItem
p = figure()
props = dict(line_width=4, line_alpha=0.7)
x = np.linspace(0, 4 * np.pi, 100)
l0 = p.line(x, np.sin(x), color=Viridis3[0], **props)
l1 = p.line(x, 4 * np.cos(x), color=Viridis3[1], **props)
l2 = p.line(x, np.tan(x), color=Viridis3[2], **props)
legend_items = [LegendItem(label="Line %d" % i, renderers=[r]) for i, r in enumerate([l0, l1, l2])]
p.add_layout(Legend(items=legend_items))
checkbox = CheckboxGroup(labels=["Line 0", "Line 1", "Line 2"], active=[0, 1, 2], width=100)
def update(attr, old, new):
l0.visible = 0 in checkbox.active
l1.visible = 1 in checkbox.active
l2.visible = 2 in checkbox.active
p.legend.items = [legend_items[i] for i in checkbox.active]
checkbox.on_change('active', update)
layout = row(checkbox, p)
curdoc().add_root(layout)