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
Related
I'm trying to highlight last value of a time series plot by plot its value on yaxis, as shown in this question. I prefer using LabelSet over Legend because you can precisely control the text positions and also using a data source to update it. But unfortunately, I can not find out how to draw label text outside the plot box.
Here is some code to plot LabelSet and notice how the text is only shown inside the box (66.1x is partially blocked by yaxis):
import pandas as pd
from bokeh.io import output_notebook
output_notebook()
from bokeh.plotting import figure, show
from bokeh.models import LabelSet, ColumnDataSource
#import bokeh.sampledata
#bokeh.sampledata.download()
from bokeh.sampledata.stocks import MSFT
df = pd.DataFrame(MSFT)[:50]
df["date"] = pd.to_datetime(df["date"])
p = figure(
x_axis_type="datetime", width=1000, toolbar_location='left',
title = "MSFT Candlestick", y_axis_location="right")
p.line(df.date, df.close)
ds = ColumnDataSource({'x': [df.date.iloc[-1]], 'y': [df.close.iloc[-1]], 'text': [' ' + str(df.close.iloc[-1])]})
ls = LabelSet(x='x', y='y', text='text', source=ds)
p.add_layout(ls)
show(p)
Please let me know how to show LabelSet outside the box, Thanks
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.
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:
I'm new to bokeh and I just jumped right into using hovertool as that's why I wanted to use bokeh in the first place.
Now I'm plotting genes and what I want to achieve is multiple lines with the same y-coordinate and when you hover over a line you get the name and position of this gene.
I have tried to mimic this example, but for some reason the I can't even get it to show coordinates.
I'm sure that if someone who actually knows their way around bokeh looks at this code, the mistake will be apparent and I'd be very thankful if they showed it to me.
from bokeh.plotting import figure, HBox, output_file, show, VBox, ColumnDataSource
from bokeh.models import Range1d, HoverTool
from collections import OrderedDict
import random
ys = [10 for x in range(len(levelsdf2[(name, 'Start')]))]
xscale = zip(levelsdf2[('Log', 'Start')], levelsdf2[('Log', 'Stop')])
yscale = zip(ys,ys)
TOOLS="pan,wheel_zoom,box_zoom,reset,hover"
output_file("scatter.html")
hover_tips = levelsdf2.index.values
colors = ["#%06x" % random.randint(0,0xFFFFFF) for c in range(len(xscale))]
source = ColumnDataSource(
data=dict(
x=xscale,
y=yscale,
gene=hover_tips,
colors=colors,
)
)
p1 = figure(plot_width=1750, plot_height=950,y_range=[0, 15],tools=TOOLS)
p1.multi_line(xscale[1:10],yscale[1:10], alpha=1, source=source,line_width=10, line_color=colors[1:10])
hover = p1.select(dict(type=HoverTool))
hover.tooltips = [
("index", "$index"),
("(x,y)", "($x, $y)"),
]
show(p1)
the levelsdf2 is a pandas.DataFrame, if it matters.
I figured it out on my own. It turns out that version 0.8.2 of Bokeh doesn't allow hovertool for lines so I did the same thing using quads.
from bokeh.plotting import figure, HBox, output_file, show, VBox, ColumnDataSource
from bokeh.models import Range1d, HoverTool
from collections import OrderedDict
import random
xscale = zip(levelsdf2[('series1', 'Start')], levelsdf2[('series1', 'Stop')])
xscale2 = zip(levelsdf2[('series2', 'Start')], levelsdf2[('series2', 'Stop')])
yscale2 = zip([9.2 for x in range(len(levelsdf2[(name, 'Start')]))],[9.2 for x in range(len(levelsdf2[(name, 'Start')]))])
TOOLS="pan,wheel_zoom,box_zoom,reset,hover"
output_file("linesandquads.html")
hover_tips = levelsdf2.index.values
colors = ["#%06x" % random.randint(0,0xFFFFFF) for c in range(len(xscale))]
proc1 = 'Log'
proc2 = 'MazF2h'
expression1 = levelsdf2[(proc1, 'Level')]
expression2 = levelsdf2[(proc2, 'Level')]
source = ColumnDataSource(
data=dict(
start=[min(xscale[x]) for x in range(len(xscale))],
stop=[max(xscale[x]) for x in range(len(xscale))],
start2=[min(xscale2[x]) for x in range(len(xscale2))],
stop2=[max(xscale2[x]) for x in range(len(xscale2))],
gene=hover_tips,
colors=colors,
expression1=expression1,
expression2=expression2,
)
)
p1 = figure(plot_width=900, plot_height=500,y_range=[8,10.5],tools=TOOLS)
p1.quad(left="start", right="stop", top=[9.211 for x in range(len(xscale))],
bottom = [9.209 for x in range(len(xscale))], source=source, color="colors")
p1.multi_line(xscale2,yscale2, source=source, color="colors", line_width=20)
hover = p1.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
(proc1+" (start,stop, expression)", "(#start| #stop| #expression1)"),
("Gene","#gene"),
])
show(p1)
Works like a charm.
EDIT: Added a picture of the result, as requested and edited code to match the screenshot posted.
It's not the best solution as it turns out it's not all that easy to plot several series of quads on one plot. It's probably possible but as it didn't matter much in my use case I didn't investigate too vigorously.
As all genes are represented on all series at the same place I just added tooltips for all series to the quads and plotted the other series as multi_line plots on the same figure.
This means that if you hovered on the top line at 9.21 you'd get tooltips for the line at 9.2 as well, but If you hovered on the 9.2 line you wouldn't get a tooltip at all.