Running python code by clicking a button in Bokeh - python

Does anyone have an example of how to run python code in jupyter by clicking a button in Bokeh?

UPDATE The original answer was very out of date. The answer has been updated to reflect changes since Bokeh 0.11 which was released in January of 2016.
A complete example pared from the sliders demo, that uses features from Bokeh 0.12.4:
from numpy import linspace, pi, sin
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
# Set up data
x = linspace(0, 4*pi, 200)
y = sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
# Set up plot
plot = figure(x_range=(0, 4*pi), y_range=(-2.5, 2.5))
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# Set up widgets
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1)
# Set up callbacks
def update(attrname, old, new):
# Get the current slider values
a = amplitude.value
k = freq.value
# Update the data for the new curve
source.data = dict(x=x, y=a*sin(k*x))
amplitude.on_change('value', update)
freq.on_change('value', update)
# Set up layout and add to document
inputs = widgetbox(amplitude, freq)
curdoc().add_root(row(inputs, plot, width=1200))
Run with bokeh serve --show <filename> and get the following responsive web app in your browser:

Related

Bokeh plot disappearing when panning or zooming

This simple Bokeh plot disappears when I try to pan or zoom with my mouse/mousewheel. The plotted line vanishes until I let go of my mouse, at which point it returns. How to fix?
from bokeh.plotting import figure, curdoc
from bokeh.driving import linear
from bokeh.models.tools import PanTool, WheelZoomTool
p = figure(sizing_mode="stretch_both", y_axis_location="right", x_axis_type="datetime")
pan_tool = p.select(dict(type=PanTool))
pan_tool.dimensions="width"
zoom_tool = p.select(dict(type=WheelZoomTool))
zoom_tool.dimensions="width"
p.x_range.follow = "end"
p.x_range.range_padding = 0.01
p.xaxis.ticker.desired_num_ticks = 8
r1 = p.step(range(3000), range(3000), color="red", line_width=0.4)
curdoc().add_root(p)
To run it:
bokeh serve --show bokeh_test.py

Use new values from bokeh widgets for interactive python plotting

I need to use the values we receive from Bokeh widgets to run some code and plot newly generated data. I cannot modify the data inside javascript that goes into CustomJS as there not available the libraries that are available in python. I understand that I can use bokeh server and run python callbacks, but this has to happen in a Databricks notebook. Is this possible?
As I cannot expose the code that I am working on, I prepared a sample code that hopefully conveys what I am trying to do.
import numpy as np
from bokeh.io import curdoc, show
from bokeh.models import ColumnDataSource, Grid, LinearAxis, Patches, Plot, Rect
from bokeh.embed import file_html
from bokeh.plotting import figure, output_file, show
from bokeh.resources import CDN
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
button_cds = ColumnDataSource(data=dict(button_val=[1]))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(button_cds = button_cds), code="""
var data = button_cds.data;
var f = cb_obj.value;
data['button_val'] = f;
button_cds.change.emit();
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)
for i in range(len(source.data['x'])):
source.data['y'][i] = x[i]**button_cds.data['button_val'][0]
layout = column(slider, plot)
displayHTML(file_html(layout, CDN))
Please note that I can do modifying the source.data in CustomJS, but I have to use python code.

Rect glyphs on a categorical axis behave unexpectedly when updating axis range via CustomJS - Bokeh

I am plotting a heatmap representation of a correlation matrix with Rect glyphs and a Labelset on a categorical axis. I'm trying to use a MultiSelect widget to dynamically update which elements are currently displayed.
Expected behavior - when I click on the button, the heatmap updates and displays only the elements that were picked in the widget (X and Y axis labels are identical). This works fine most of the time, but
Observed behavior: if I uncheck the first label in the list, the Rect glyphs shift right by 1 unit, while the LabelSet glyphs are displayed correctly.
Here's a minimal example:
from bokeh.io import show, output_notebook
from bokeh.models.callbacks import CustomJS
from bokeh.models.glyphs import Rect
from bokeh.events import ButtonClick
from bokeh.models.widgets import MultiSelect, Button
from bokeh.models import ColumnDataSource, LabelSet
from bokeh.layouts import row, column
from bokeh.plotting import figure
output_notebook()
data = pd.DataFrame({
'A': [0,1,2],
'B': [3,4,5],
'C': [6,7,8]
}, index=['A', 'B', 'C'])
data_transformed = data.stack().rename('value').reset_index()
source = ColumnDataSource(data_transformed)
p = figure(
width=200,
height=200,
x_range=data_transformed.level_0.unique().tolist(),
y_range=data_transformed.level_0.unique().tolist()[::-1],
x_axis_location="above"
)
rect = Rect(
x='level_0',
y='level_1',
width=1,
height=1,
fill_color='red',
line_color='black'
)
labels = LabelSet(
x='level_0',
y='level_1',
text='value',
level='annotation',
source=source
)
p.add_glyph(source, rect)
p.add_layout(labels)
multiselect = MultiSelect(
title='Pick the symbols',
value=data_transformed.level_0.unique().tolist(),
options=data_transformed.level_0.unique().tolist(),
height=200
)
button_callback = CustomJS(args=dict(plot=p, multiselect=multiselect), code="""
var x_range = multiselect.value.concat().sort()
var y_range = x_range.concat().reverse()
plot.x_range.factors = x_range
plot.y_range.factors = y_range
""")
button = Button(label="Calculate", button_type="primary")
button.js_on_event(ButtonClick, button_callback)
widgets = column(children=[multiselect, button])
layout = row(children=[widgets, p])
show(layout)
Initial display:
Image 1
After unchecking the last element and clicking the button (all good here):
Image 2
If the first element is unchecked:
Image 3
After switching to Circle glyphs it works as expected :
Image 4
Is this a bug or am I doing something wrong? Thanks.
I am using Bokeh 2.0.2
I'm pretty sure it's a bug. I've opened https://github.com/bokeh/bokeh/issues/10219.

Python Bokeh: Slider callback in ColumnDataSource not update

I having a problem with the callback, I got everything worked expect the part when the graph doesn't update even thou the array is updated when I change the slider.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
Step = StepHeight.value
x = [0,0,2,2,4,4]
y = [10,0,0,Step,Step,10]
source.data = ColumnDataSource(dict(x=x, y=y))
source.on_change('value', update_data)
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)
You were trying to make source.data a column data source but source should be the column data source. Source.data is just a dictionary. I changed some things in your code and it should work fine now.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
y = [10,0,0,new,new,10]
source.data['y_values'] = y
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
StepHeight.on_change('value', update_data)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)

Manually change x range for Bokeh plot

So I'm trying to create a Bokeh plot for which I would like to be able to manually adapt the range of the X-Axis with a slider.
Being a newbie I've only managed to get this far and despite researching this subject I haven't been able to solve this problem.
Here my code:
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import Figure, output_file, show
output_file("test.html")
x = [x*0.5 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = Figure(plot_width=600, plot_height=400, x_range=(0, 100))
plot.line('x', 'y', source=source, line_width=2, line_alpha=0.75)
callback = CustomJS(args=dict(x_range=plot.x_range), code="""
var start = cb_obj.value
x_range.set({"start": start, "end": start+10})
""")
slider = Slider (start=0, end=90, value=20, step=10)
slider.js_on_change('value', callback)
layout = column(slider, plot)
show(layout)
My biggest problem is to understand how I connect the CustomJS to my plot.
I would gladly appreciate your help.
It has been answered at the Bokeh Google group
To reiterate, the only change needed is to replace x_range.set with x_range.setv.

Categories

Resources