Change ColorMapper via callback JS in standalone Bokeh plot - bokeh

I would like to create a standalone document, similar to the server app example 'Crossfilter': to select different columns for coloring the circles and to update the colorbar as well.
I define a CustomJS with the code below, where I create a new LinearColorMapper with the calculated low and high values. (For debugging purposes, I intentionally add different palette than the one set via the Python code).
var low = Math.min.apply(Math,source.data[cb_obj.value]);
var high = Math.max.apply(Math,source.data[cb_obj.value]);
var color_mapper = new Bokeh.LinearColorMapper({palette:'Viridis5', low:low, high:high});
cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
color_bar.color_mapper = color_mapper;
source.change.emit();
As a result, when selecting the column, the circles become white, the line black, the ticks of the color bar change correctly, but the palette does not change.
Could you help me setting the proper attributes in the callback? Thank you in advance.
Original state, color mapper set from Python code
After selecting the column 'd'
I created a "minimal working example" to show how far I got. The full project with the template it can be found here: https://github.com/pintergreg/bokehjscolormapperexample

It looks like you cannot reference a colour pallet like this in BokehJS. Just pass Viridis5 variable to JS callback and it works (tested on Bokeh v1.0.4):
import pandas as pd
from bokeh.models import ColumnDataSource, ColorBar, Select, CustomJS
from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.palettes import Spectral5, Viridis5
from bokeh.transform import linear_cmap
from bokeh.embed import components
from jinja2 import Environment, FileSystemLoader
df = pd.DataFrame({"a": [2, 6, 5, 3, 7, 8, 1, 9, 2, 4],
"b": [3, 5, 7, 1, 0, 6, 5, 4, 2, 9],
"c": [11, 12, 13, 14, 11, 13, 15, 14, 15, 12],
"d": [21, 23, 24, 25, 21, 22, 23, 24, 25, 22]})
source = ColumnDataSource(df)
mapper = linear_cmap(field_name = "c", palette = Spectral5,
low = min(df["c"]), high = max(df["c"]))
fig = figure(plot_width = 400, plot_height = 400)
cir = fig.circle(x = "a", y = "b", size = 12,
source = source, line_color = mapper, color = mapper)
color_bar = ColorBar(color_mapper = mapper["transform"], width = 8,
location = (0, 0))
fig.add_layout(color_bar, "right")
codec = """
var low = Math.min.apply(Math,source.data[cb_obj.value]);
var high = Math.max.apply(Math,source.data[cb_obj.value]);
var color_mapper = new Bokeh.LinearColorMapper({palette:viridis5, low:low, high:high});
cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
color_bar.color_mapper.low = low;
color_bar.color_mapper.high = high;
color_bar.color_mapper.palette = viridis5;
source.change.emit();
"""
cb_cselect_c = CustomJS(args = dict(cir = cir, source = source, color_bar = color_bar, viridis5 = Viridis5),
code = codec)
c_select = Select(title = "Select variable for color: ", value = "None",
options = ["c", "d"], callback = cb_cselect_c)
layout = gridplot([[fig], [c_select]])
show(layout)
# env = Environment(loader=FileSystemLoader("."))
# template = env.get_template("template.html")
#
# script, div = components(layout)
#
# with open("output.html", "w") as f:
# print(template.render(script=script, div=div), file=f)
You also need to manually add this line to header section of the generated HTML file:
<script type="text/javascript" src="http://cdn.bokeh.org/bokeh/release/bokeh-api-1.0.4.min.js"></script>

Related

bokeh: custom callback for hovertool for figure.line

I'm trying to write a code which adds hovertool performing customer js-code over figure.line object. In order to do it I used userguide code as example (https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-hover) but simplified and modified (see below). I've found that it works over figure.segment however doesn't work over figure.line.
Here is complete example:
from bokeh.models import CustomJS, HoverTool
from bokeh.plotting import figure, output_file, show
output_file("hover_callback.html")
# define some points and a little graph between them
x = [2, 3, 5, 6, 8, 7]
y = [6, 4, 3, 8, 7, 5]
p = figure(plot_width=400, plot_height=400, tools="", toolbar_location=None)
lines = p.line(x, y, line_color='blue', line_width=5)
seg = p.segment(x[0:3], y[0:3], x1=x[3:], y1=y[3:], color='olive', alpha=0.6, line_width=3)
code = """
const indices = cb_data.index.indices
if (indices.length > 0) {
console.log('Hello!')
}
"""
callback = CustomJS(code=code)
p.add_tools(HoverTool(tooltips=None, callback=callback, renderers=[lines, seg]))
show(p)
cb_data.index.indices is not empty when I put pointer over segment (olive-color) but not over line (blue).
Is it sort of expected behaviour? If so I would appreciate reference to some doc where it's explained. THanks!
You can activate the HoverTool for lines, too. But you have to adapt your JavaScript quite a bit. Lines return the information of the index a bit different, you have to ask for const line_indices = cb_data.index.line_indices.
Here is your complete example:
from bokeh.models import CustomJS, HoverTool
from bokeh.plotting import figure, output_file, show
output_file("hover_callback.html")
# define some points and a little graph between them
x = [2, 3, 5, 6, 8, 7]
y = [6, 4, 3, 8, 7, 5]
p = figure(plot_width=400, plot_height=400, tools="", toolbar_location=None)
lines = p.line(x, y, line_color='blue', line_width=5)
seg = p.segment(x[0:3], y[0:3], x1=x[3:], y1=y[3:], color='olive', alpha=0.6, line_width=3)
code = """
const indices = cb_data.index.indices
const line_indices = cb_data.index.line_indices
if (indices.length + line_indices.length > 0) {
console.log('Hello!')
}
"""
callback = CustomJS(code=code)
p.add_tools(HoverTool(tooltips=None, callback=callback, renderers=[lines, seg]))
show(p)

Add several scatter glyphs to one plot (figure) - Python, Bokeh

I am trying to plot several scatter glyphs to one figure, using a loop. The goal is to use an own glyph for each brand and to update the values if another value is chosen by the Select widgets.
But the figure shows an empty graph. I assume that the problem is the ColumnDataSource and the update in the function "update()".
This is an executable example. Do you have any idea?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool
from bokeh.plotting import figure, show
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
df = pd.DataFrame(zip(brands, product, price, size, value, id), columns=col)
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#id"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#x'),
(select_y_axis.value, '#y')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
source = {}
plots = {}
for brand in brands:
# Create Column Data Source that will be used by the plot
source[brand] = ColumnDataSource(data=dict(x=[], y=[], id=[], product=[], brand=[]))
plots[brand] = fig.scatter(x='x', y='y', size=5, source=source[brand])
def update():
x_name = select_x_axis.value
y_name = select_y_axis.value
fig.xaxis.axis_label = x_name
fig.yaxis.axis_label = y_name
for brand in brands:
df1 = df.loc[df['brand'] == brand]
source[brand].data = dict(
x=df1[x_name],
y=df1[y_name],
id=df1['ID'],
product=df1['product'],
brand=df1['brand']
)
# Set up layouts and add to document
controls = [select_x_axis, select_y_axis]
for control in controls:
control.on_change('value', lambda attr, old, new: update())
inputs = column(select_x_axis, select_y_axis)
update() # initial load of the data
show(row(inputs, fig, width=1000))
#curdoc().add_root(row(inputs, fig, width=1000))
#curdoc().title = "xyz"
resultig plot
Something like that ?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
import pandas as pd
def make_data() :
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
ident = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
return pd.DataFrame(zip(brands, product, price, size, value, ident), columns=col)
if __name__ == "__main__" :
df = make_data()
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Scatter plot
hover = HoverTool(tooltips=[(name, "#" + name) for name in df.columns])
figure = figure(tools=[hover, 'reset'])
# Source
source = ColumnDataSource(df)
figure.scatter("xvalues", "yvalues", source=source)
# Selects
options = ["product", "price", "size", "value"]
select_x_axis = Select(title="x-Axis:", value=default_xcol, options=options)
select_y_axis = Select(title="y-Axis:", value=default_ycol, options=options)
# callback
callback = CustomJS(args={"source":source, "axis":figure.xaxis[0]}, code="""
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change("value", callback)
callback = CustomJS(args={"source":source, "axis":figure.yaxis[0]}, code="""
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_y_axis.js_on_change("value", callback)
show(row(
column(select_x_axis, select_y_axis),
figure, width=1000))
#jsgounot Thanks for your help. I figured something out that works quiet well at the moment:
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
from bokeh.palettes import d3
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
colors = d3["Category20c"][len(brands)]
markers = ['circle', 'square', 'triangle', 'asterisk', 'circle_x', 'square_x', 'inverted_triangle', 'x', 'circle_cross', 'square_cross', 'diamond', 'cross']
df = pd.DataFrame(zip(id, brands, product, price, size, value), columns=col)
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#ID"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#xvalues'),
(select_y_axis.value, '#yvalues')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
df_brand = []
sources = []
plots = {}
for index, brand in enumerate(df.brand.unique()):
df_brand.append(df.loc[df['brand'] == brand])
sources.append(ColumnDataSource(df_brand[index]))
plots[brand] = fig.scatter(x='xvalues', y='yvalues', source=sources[index],
legend_label=brand,
marker=markers[index],
color=colors[index])
fig.legend.click_policy="hide"
callback_x = CustomJS(args={'sources': sources, 'axis': fig.xaxis[0], 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
callback_y = CustomJS(args={'sources': sources, 'axis': fig.yaxis[0], 'hov': fig.hover, 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change('value', callback_x)
select_y_axis.js_on_change('value', callback_y)
show(row(
column(select_x_axis, select_y_axis),
fig, width=1000))

Once a dropdown option is selected, how do I "change.emit" or "trigger change" on the plot?

Any ideas what's supposed to go where the triple '?'s are?
import pandas as pd
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider, Select
import bokeh.plotting as bp
from bokeh.plotting import Figure, output_file, show
from bokeh.models import HoverTool, DatetimeTickFormatter
# Create an output file
bp.output_file('columnDataSource.html')
# Create your plot as a bokeh.figure object
myPlot = bp.figure(height = 600,
width = 800,
y_range=(0,3))
x_values = [1, 2, 3, 4, 5]
y_values = [1, 2, 3, 4, 5]
myPlot.line(x = x_values, y= y_values, line_width=2)
callback = CustomJS(args={
'source1': {'x': [1,2,3,4], 'y':[1,1,1,1]},
'source2': {'x': [0,0,0,0], 'y':[2,2,2,2]},
'source3': {'x': [1,2,3,4], 'y':[1,1,1,1]}},
code="""
var data1 = source1;
var data2 = source2;
var data3 = source3;
var f = cb_obj.value;
if(f == 'A'){
console.log("A selected from dropdown.");
data1.x = data1.x;
data1.y = data1.y;
}
else if(f == 'B'){
// Substitute all old data1 values in with data2 values
console.log("B selected from dropdown.");
data1.x = data2.x;
data1.y = data2.y;
}
else{
console.log("C selected.");
// Substitute all old data1 values in with data3 values
data1.x = data3.x;
data1.y = data3.y;
}
// Problematic line!
???.change.emit();
""")
select = Select(title='Choose', value='A', options=['A','B','C'])
select.js_on_change('value', callback)
layout = column(select, myPlot)
show(layout) # et voilĂ .
I expect my x and y values to change and plot accordingly to my Bokeh graph.
Nothing is changing at the moment as I don't know what object's "trigger" function I'm supposed to be calling. Please help, I'm new to Bokeh.
You do ColumnDataSource.change.emit() if you updated the data source fields by reference e.g. when you update only x or only y:
ColumnDataSource.data['x'] = [4, 3, 2, 1]
ColumnDataSource.change.emit()
When you update them both you do:
ColumnDataSource.data = new_data
Where new_data is a new json object like {'x': [1], 'y':[2]}.
The reason for this is that JS can automatically detect a change when existing object is replaced with a new one but it cannot detect changes by reference so in those cases you need explicitly to call: ColumnDataSource.change.emit() to update the BokehJS model.
Here is your modified code:
from bokeh.models import CustomJS, ColumnDataSource, Select, Column
from bokeh.plotting import figure, show
myPlot = figure(y_range = (0, 4))
data = {'A': {'x': [1, 2, 3, 4], 'y':[1, 1, 1, 1]},
'B': {'x': [1, 2, 3, 4], 'y':[2, 2, 2, 2]},
'C': {'x': [1, 2, 3, 4], 'y':[3, 3, 3, 3]} }
source = ColumnDataSource(data['A'])
myPlot.line('x', 'y', line_width = 2, source = source)
callback = CustomJS(args = {'source': source, 'data': data},
code = """source.data = data[cb_obj.value]; """)
select = Select(title = 'Choose', value = 'A', options = ['A', 'B', 'C'])
select.js_on_change('value', callback)
layout = Column(select, myPlot)
show(layout)

Bokeh: Duplicate factor or sub factor error with CustomJS change of x_range

I plot data in a bar chart. The data is grouped in two levels where one level is year. I add a range slider in order to alter the x_range of the plot with respect to which years to show. This I have tried to implement through a CustomJS callback (first time I try CustomJS).
Using the slider the factors on the x-axis gets updated as expected. However if I then use the zoom tool and afterwards use the reset tool I get an error message in the web console:
Error: duplicate factor or subfactor: 2016
Not sure what I'm doing wrong with the setup of the data. Is the update of the factor range wrong?
I'm using version 1.1.0 of Bokeh on MacOS. Same error observed in Safari and Firefox.
The code below will reproduce the error.
from bokeh.models import ColumnDataSource, FactorRange, RangeSlider, CustomJS
from bokeh.plotting import figure
from bokeh.layouts import column
import pandas as pd
output_file("grouped_customJS.html")
fruits = ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries']
data = {'fruits' : fruits,
'2015' : [2, 1, 4, 3, 2, 4],
'2016' : [5, 3, 3, 2, 4, 6],
'2017' : [3, 2, 4, 4, 5, 3]}
df = pd.DataFrame.from_dict(data)
df=df.set_index('fruits').stack().reset_index()
df=df.rename(columns={'level_1':'year', 0:'value'})
# add year as int column for slider
df['year_int'] = df['year'].astype(int)
df=df.set_index(['fruits','year'])
cats = df.index.values
source = ColumnDataSource(
data = {
'categories': cats,
'values': df['value'],
'year': df['year_int']
}
)
p = figure(
x_range=FactorRange(*cats),
plot_height=250,
title="Fruit Counts by Year",
)
p.vbar(x='categories', top='values', width=0.9, source=source)
p.y_range.start = 0
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None
slider = RangeSlider(
start=df['year_int'].min(),
end=df['year_int'].max(),
step = 1,
value = (df['year_int'].min(), df['year_int'].max()),
)
callback = CustomJS(args=dict(slider=slider, source=source, plt = p), code="""
plt.x_range.factors = [];
for (var i = 0; i < source.get_length(); i++){
if (source.data['year'][i] >= slider.value[0] && source.data['year'][i] <= slider.value[1]){
plt.x_range.factors.push(source.data['categories'][i]);
}
}
""")
slider.js_on_change('value', callback)
p.x_range.js_on_change('factors', callback)
show(column(p, slider))
Try this (works fine with Bokeh v1.1.0):
callback = CustomJS(args = dict(slider = slider, source = source, plt = p), code = """
var factors = []
for (var i = 0; i < source.get_length(); i++){
if (source.data['year'][i] >= slider.value[0] && source.data['year'][i] <= slider.value[1]){
factors.push(source.data['categories'][i]);
}
}
plt.x_range.factors = factors; """)

Slider based on Networkx node attribute value with Bokeh

I am attempting to develop a slider which will limit the number of nodes visible in a network graph based on the value of a node's attribute. The below Pandas DataFrame (df) represents the nodes, and the node's associated attributes (count information).
source target source_count target_count
A C 15 10
A D 15 20
A E 15 30
B F 25 10
B G 25 20
B H 25 30
I have used the following code to generate a network graph for the nodes and their associated attributes.
import pandas as pd
from bokeh.layouts import column, widgetbox,layout,
from bokeh.plotting import figure, show, output_file,
from bokeh.models import HoverTool, value,PanTool, LabelSet, Legend, ColumnDataSource,Circle,Plot, Range1d, MultiLineBoxSelectTool,ResetTool,LassoSelectTool,Slider
from bokeh.models.callbacks import CustomJS
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes
df = pd.DataFrame({
"source":["A", "A", "A", "B", "B","B"],
"target":["C", "D", "E", "F", "G","H"],
"source_count":["15", "15", "15", "25","25","25"]
"target_count":["10", "20", "30", "10","20","30"]
})
net_graph = from_pandas_edgelist(df, 'source', 'target')
#assign attributes
for index, row in df.iterrows():
net_graph.nodes[row['source']]['yearly_count'] = row['source_count']
net_graph.nodes[row['target']]['yearly_count'] = row['target_count']
graph_plot= Plot(plot_width=800, plot_height=600,
x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1))
node_hover_tool = HoverTool(tooltips=[("Name", "#index"),("Yearly Count", "#yearly_count")])
graph_plot.add_tools(node_hover_tool)
graph_setup = from_networkx(net_graph, nx.spring_layout, scale=1, center=(0, 0))
graph_setup.node_renderer.glyph = Circle(size=20,fill_color = 'blue')
graph_setup.edge_renderer.glyph = MultiLine(line_color="red", line_alpha=0.8, line_width=1)
graph_plot.renderers.append(graph_setup)
output_file("test_1.html")
show(graph_plot)
The slider I am trying to would use the yearly_count attribute to limit the number of nodes on display. I know that Bokeh allows the embedding of a JavaScript Callback, however, I have not seen a use-case integrated with NetworkX.
Any assistance that anyone could provide would be greatly appreciated.
If you can run your app with bokeh serve then I would try:
from bokeh.models import Slider
graph_plot= Plot()
graph_setup.node_renderer.glyph = Circle()
graph_setup.edge_renderer.glyph = MultiLine()
def callback(attr, old, new):
//filter your data here to show less nodes and edges based
graph_setup.node_renderer.data_source.data = data
graph_setup.edge_renderer.data_source.data = data
slider = Slider()
slider.on_change('value', callback)
If you want to run a Bokeh standalone app then replace slider callback with:
code = """
//filter your data here to show less nodes and edges
graph_setup.node_renderer.data_source.data = data;
graph_setup.edge_renderer.data_source.data = data; """
callback = CustomJS(args = dict(graph_setup = graph_setup, data = data), code = code)
slider = Slider()
slider.js_on_change('value', callback)
See complete JS callback example below:
import networkx as nx
from bokeh.io import show, output_file
from bokeh.models import Plot, Range1d, MultiLine, Circle, TapTool, OpenURL, HoverTool, CustomJS, Slider, Column
from bokeh.models.graphs import from_networkx, EdgesAndLinkedNodes
from bokeh.palettes import Spectral4
from dask.dataframe.core import DataFrame
import pandas as pd
data = {'source': ['A', 'A', 'A', 'A', 'A', 'A'], 'target': ['C', 'D', 'E', 'F', 'G', 'H'], 'source_count': [15, 15, 15, 25, 25, 25], 'target_count': [10, 20, 30, 10, 20, 30]}
df = pd.DataFrame(data)
net_graph = nx.from_pandas_edgelist(df, 'source', 'target')
for index, row in df.iterrows():
net_graph.nodes[row['source']]['yearly_count'] = row['source_count']
net_graph.nodes[row['target']]['yearly_count'] = row['target_count']
graph_plot = Plot(plot_width = 800, plot_height = 600, x_range = Range1d(-1.1, 1.1), y_range = Range1d(-1.1, 1.1))
node_hover_tool = HoverTool(tooltips = [("Name", "#index"), ("Yearly Count", "#yearly_count")])
graph_plot.add_tools(node_hover_tool)
graph_setup = from_networkx(net_graph, nx.spring_layout, scale = 1, center = (0, 0))
graph_setup.node_renderer.glyph = Circle(size = 20, fill_color = 'blue')
graph_setup.edge_renderer.glyph = MultiLine(line_color = "red", line_alpha = 0.8, line_width = 1)
graph_plot.renderers.append(graph_setup)
code = """
var new_start = start.slice();
var new_end = end.slice();
new_index = end.slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_index = ['A'].concat(new_end)
new_data_edge = {'start': new_start, 'end': new_end};
new_data_nodes = {'index': new_index};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
"""
callback = CustomJS(args = dict(graph_setup = graph_setup,
start = df['source'].values,
end = df['target'].values), code = code)
slider = Slider(title = 'Slider', start = 1, end = 6, value = 6)
slider.js_on_change('value', callback)
layout = Column(graph_plot, slider)
show(layout)
Result:
Newer versions of Bokeh uses strict mode for JavaScript (see release log), which implies that code from Tony's accepted answer does not work for Bokeh version 2.0.0 and upwards. Only a few small explicit declarations of variables are needed for the code to work for newer Bokeh versions:
code = '''
var new_start = start.slice();
var new_end = end.slice();
var new_index = end.slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_index = ['A'].concat(new_end)
var new_data_edge = {'start': new_start, 'end': new_end};
var new_data_nodes = {'index': new_index};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
'''

Resources