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.
Related
I am running an adapted version of https://github.com/bokeh/bokeh/issues/9431
Bokeh 2.2.3
Python 3.8.6
JupyterLab 2.2.9
In JupyterLab, it displays just fine, but it is not updating the color bar and image when the Range Slider is adjusted. Does anyone know how to fix this?
import numpy as np
from bokeh.plotting import figure, show
from bokeh.models import Row
from bokeh.models import LinearColorMapper, BasicTicker, ColorBar, CustomJS
from bokeh.models.widgets import RangeSlider
import bokeh.io
from bokeh.resources import INLINE
bokeh.io.output_notebook(INLINE)
data = np.random.rand(10,10)
color_mapper = LinearColorMapper(palette="Viridis256", low=0, high=1)
figure = figure(x_range=(0,1), y_range=(0,1))
img = figure.image(image=[data], color_mapper=color_mapper,
dh=[1.0], dw=[1.0], x=[0], y=[0])
color_bar = ColorBar(color_mapper=color_mapper, ticker= BasicTicker(),
location=(0,0))
figure.add_layout(color_bar, 'right')
range_slider = RangeSlider(start=data.min(), end=data.max(), value=(data.min(), data.max()), step=.1, title="Stuff", width=400)
range_slider.js_on_change("value", CustomJS(code="""
range = cb_obj.range
img.glyph.color_mapper.low = cb_obj.value[0];
img.glyph.color_mapper.high = cb_obj.value[1];
"""))
show(Row(figure, range_slider))
If you look in the browser JS console you can see the relevant error messages, e.g. "range is not defined". You need to pass any objects you want to refer to in the JS code in the args paramter of CustomJS. This is described in the docs. Here is a working version (tested w/ Bokeh 2.3, Jupyterlab >=3):
cb = CustomJS(args=dict(slider=range_slider, glyph=img.glyph), code="""
glyph.color_mapper.low = slider.value[0];
glyph.color_mapper.high = slider.value[1];
""")
range_slider.js_on_change("value", cb)
Also, FYI it's generally a bad idea to shadow existing function names:
figure = figure(...) # BAD, now you can never call figure again
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)
I'm using the Bokeh package to plot a line chart.
I want a given line to bolden (alpha to increase) when I hover over it.
I added a hover tool and then added "hover_line_alpha = 0.6" in my line chart.
However when I hover over points on a given line, the line disappears altogether!
Can you help me fix this?
Code below so you can see my logic.
Thanks,
Ross
# Code in Question
from bokeh.io import output_notebook, show, output_file
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
output_notebook()
# set out axes
x = 'time_rnd'
y = 'count'
# set colour palette
col_brew = ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd','#ccebc5','#ffed6f']
# map out figure
plot = figure(tools='box_select, lasso_select, save' ,x_axis_type='datetime')
# add HoverTool
hover_info = [('time', '#hover_time'),
('word', '#word'),
('count', '#count')]
hover = HoverTool(names=['use'],tooltips=hover_info,
mode='mouse',
show_arrow=True
)
plot.add_tools(hover)
### FOR LOOP OF PLOT [THIS IS WHERE THE ISSUE MANIFESTS]
for i in top_wds_test:
df_eng_word = df_eng_timeline[df_eng_timeline['word']==i]
source = ColumnDataSource(df_eng_word)
plot.line(x, y, line_width = 3,
line_alpha = 0.1, line_color=col_brew[top_wds.index(i)],
hover_line_alpha = 0.6,
#hover_line_color = 'black',
#hover_line_color = col_brew[top_wds.index(i)],
source = source, legend=i, name = 'use'
)
plot.circle(x, y, fill_color='white', size=5,
selection_color='green',
nonselection_fill_color='grey',nonselection_fill_alpha=0.4,
hover_color='red',
source = source, name = 'use')
# add legend
plot.legend.location = "top_left"
plot.legend.label_text_font_style = 'bold'
# materialize the plot
show(plot)
There seems to be an issue when the renderers share a data source. However, this works (with Bokeh >= 0.13.0) if you let Bokeh create a new separate source for each glyph:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
p = figure(tools="hover", tooltips="$name: #$name")
data=dict(x=[1,2,3], y1=[2,6,5], y2=[6,2,3])
p.line('x', 'y1', color="navy", line_width=3, source=data,
alpha=0.1, hover_color="navy", hover_alpha=0.6, name="y1")
p.line('x', 'y2',color="firebrick", line_width=3, source=data,
alpha=0.1, hover_color="firebrick", hover_alpha=0.6, name="y2")
show(p)
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.
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: