Hi i would like to loop through for loop eg. few times code below to get few times the same graph in one databricks cell:
I imported library:
from bokeh.plotting import figure
from bokeh.embed import components, file_html
from bokeh.resources import CDN
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
for i in range(5):
p = figure(title='test', x_axis_label = 'x values', y_axis_label='y values')
p.line(x,y, line_width =2)
html = file_html(p,CDN,'plot')
displayHTML(html)
I was trying to use for loop but still i am obtaining only one single graph in a cell.
Also tried with different modules, like here:
from bokeh.io import output_file, show
from bokeh.plotting import figure
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
for i in range(5):
p = figure(title='test', x_axis_label = 'x values')
p.line(x,y, line_width =2)
output_file("line"+str(i)+".html")
show(p)
But here i am not getting any result, nothing is plotted. Can you tell me why?
Tried this as well:
d={}
for i in range(5):
p = figure(title='test', x_axis_label = 'x values')
p.line(x,y, line_width =2)
d["html{0}".format(i)]=file_html(p,CDN, 'plot' + str(i))
for j in d:
displayHTML(j)
It looks like with bokeh in databricks it is possible to display only one graph per cell. Anyone knows if it is true?
Could you help me with the syntax for for loop to get it multiple times?
It seems from other answers that there may be issues with Databricks using old notebook versions that Bokeh does not fully support. If the standard output_notebook is not working, but the displayHTML thing does, then I'd say your best bet is to collect the plots you want to show in a column layout, then show them all at once with a single call to show at the end:
from bokeh.layouts import column
plots = []
for i in range(5):
p = figure(...)
p.line(...)
plots.append(p)
layout = column(*plots)
# only use this way for databricks
html = file_html(layout, CDN, 'plot')
displayHTML(html)
Related
I am currently working on a Webapp with Bokeh which contains a table of Swiss Franc values. I cannot figure out how to format the numbers in the following style
1'000'000
so with dahes instead of commas as the 1000 separator. Going down the documentation Bokeh seems to be limited to the options given here
https://docs.bokeh.org/en/latest/docs/reference/models/widgets.tables.html?highlight=numberfor#bokeh.models.widgets.tables.NumberFormatter
but the numbro documentation seems to hint that there is more to it.
http://numbrojs.com/format.html
I still cannot find a "de-CH" option or something like this, and even if I did, I do not see how to hand it over in bokeh. Does anyone have any experience?
Best regards.
Bokeh gives you the option to define your own tick-formatter using FuncTickFormatter. With this you can define you own rule in JavaScript.
Below you can see on example adapting your rule.
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import FuncTickFormatter
output_notebook()
# create a new plot with the toolbar below
p = figure(plot_width=400, plot_height=400,
title=None, toolbar_location="below", )
x = [1e3, 3e3, 5e3, 1e4, 3e4]
y = [2, 5, 8, 2, 7]
p.circle(x, y, size=10)
p.xaxis.formatter = FuncTickFormatter(code=
'''
var num = tick.toFixed(2).toString()
var splitted = num.split(".")
for (var i=splitted[0].length-3; 0<i; i+=-3){
num = num.slice(0,i)+"'"+num.slice(i)
}
return num
'''
)
The Output looks like this for a different set of x and y
Ticks < 30k
Ticks 2e6<1e7
x = [1e3, 3e3, 5e3, 1e4, 3e4]
x = [1e6, 5e6, 9e6]
y = [2, 5, 8, 2, 7]
y = [2, 8, 2]
I use a LinearColorMapper in Bokeh 2.3.0 to map values to certain colors in my plot. Nothing fancy, as shown in the minimal working example:
import pandas as pd
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, LinearColorMapper
from bokeh.layouts import layout
plot1 = figure(plot_width=1000, plot_height=250)
df = pd.DataFrame({"ID":[0, 1, 2, 3, 4, 5, 6, 7],
"Value1":[0, 100, 200, 300, 400, 500, 600, 700],
"Color": [0, 1, 0, 1, 2, 2, 3, 3] # Those somehow correspond to the categories, but will change during runtime
})
source = ColumnDataSource(df)
possible_categories = ["A", "B", "C", "D"]
cmap = LinearColorMapper(palette="Turbo256", low = 0, high = len(possible_categories))
circle = plot1.circle(x='ID', y='Value1', source=source,
fill_color={"field":'Color', "transform":cmap},
line_color={"field":'Color', "transform":cmap})
layout_ = layout([[plot1]])
curdoc().add_root(layout_)
But I wonder if there is a possibility to 'manually' access the colors directly from the cmap that I created? I would like to pass a value to the cmap and get the color or Hex-Color-Code or something similar back. Somehow like this (which of course does not work, but illustrates the idea):
for categ, num in enumerate(possible_categories):
color = cmap.get_colorcode_to_the_following_value(num)
print(f"Color of Category {possible_categories[categ]}: {color}")
with the output:
Color of Category A: ColorCodeXXX
Color of Category B: ColorCodeXXX
Color of Category C: ColorCodeXXX
Color of Category D: ColorCodeXXX
I´m sure this somehow works, but I extensively looked up the reference of Bokeh and did not find anything like this.
The color mapping done by LinearColorMapper is actually performed on the JavaScript side, in the browser. The information is never computed or available in Python. If you require this information, you would need to map and set the colors on the glyph manually, without using Bokeh's LinearColorMapper.
I am trying to set the absolute position of a Bokeh Chart inside a Layout so that one of the plots is shown on top of another plot. Right now when I am plotting something like this:
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.layouts import layout
import numpy as np
x = np.arange(1,10.1,0.1)
y = [i**2 for i in x]
categories = ['A', 'B']
values = [1000, 1500]
fig1 = figure(width=600,plot_height=600, title="First Plot")
fig1.line(x=x, y=y)
fig2 = figure(width=200,plot_height=250,x_range=categories,
title="Second Plot") fig2.vbar(x=categories, top=values, width=0.2)
l = layout([[fig1,fig2]])
curdoc().add_root(l)
The result will be this:
What I am searching for is some way to make it look like that:
How can this result be achieved?
Thank you!
This is what I came up with (works for Bokeh v1.0.4). You need to move your mouse over the plot to get the other one jump inside but you could also copy the JS code from the callback and manually add it to the HTML generated by Bokeh so you achieve the same result.
from bokeh.plotting import figure, show
from bokeh.layouts import Row
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS, BoxSelectTool, HoverTool
import pandas as pd
plot = figure(tools = 'hover', tooltips = [("x", "#x"), ("y", "#y")])
circles = plot.circle('x', 'y', size = 20, source = ColumnDataSource({'x': [1, 2, 3], 'y':[1, 2, 3]}))
inner_plot = figure(name = 'inner_plot', plot_width = 200, plot_height = 200)
lines = inner_plot.line('x', 'y', source = ColumnDataSource({'x': [8, 9, 10], 'y':[8, 6, 8]}))
code = """ div = document.getElementsByClassName('bk-root')[0];
tooltip_plot = div.children[0].children[1]
tooltip_plot.style = "position:absolute; left: 340px; top: 350px;"; """
callback = CustomJS(code = code)
plot.js_on_event('mousemove', callback)
show(Row(plot, inner_plot))
Result:
I would like to set the color of a Bokeh line plot (Bokeh version 0.12.5) using a ColumnDataSource. However, with a line plot nothing is plotted. On the other hand, if I use a circle renderer everything works as expected. Below is an example program with both a line plot and a circle plot and you can comment/uncomment the appropriate lines to see the plotting behavior. I also included a line of code for a line plot where the color is explicitly defined and the plot works perfectly. I have seen a couple similar questions asked but could not find a solid solution to this problem or determine if I am just doing something fundamentally wrong. Thanks for your help.
# bokeh version 0.12.5
# run in terminal with: python -m bokeh serve --show line_plot_color.py
from bokeh.io import curdoc
from bokeh.models import ColumnDataSource
from bokeh.plotting import Figure
from bokeh.layouts import row
source = ColumnDataSource(data = dict(color = ['green','green','green'], xs = [1,2,3], ys = [1,2,3]))
fig = Figure(plot_width=300, plot_height=300)
#r = fig.circle('xs','ys',source = source, size = 12, fill_color = 'color') # works as expected
r = fig.line('xs','ys',source = source, line_color = 'color') # fails to plot; no errors or warnings in terminal
#r = fig.line('xs','ys',source = source, line_color = 'green') # works as expected
layout = row(fig)
curdoc().add_root(layout)
First to help you with debugging the bokeh server, it's very useful to use the devtools that come with web browsers. The devtools' console will contain useful debugging information as is the case for your example.
Second, looking through the docs the line glyph method is not set up to receive a column data source value for its coloring. If you want to plot multiple lines with different colors on a single figure, then you can use the multi_line glyph. To use this glyph, you need to modify your data source xs and ys to be a list of list for each line in your multi_line. Here's a quick example.
source2 = ColumnDataSource(data = dict(color = ['green','red'], xs = [[1, 2],[2, 4]], ys = [[1, 2],[2, 4]]))
r = fig.multi_line('xs','ys',source = source2, line_color = 'color')
I'm slamming my head against the wall with this problem. I simply want to be able to make a selection (lasso, box_select) of points in a streaming bokeh scatterplot that will be remembered when the figure updates (e.g., with new data in the time series).
I think this will require me to be able to access the list of the indices of currently selected points, but I can't figure out how to. Here's an example where I try (slightly modified from the example at
http://docs.bokeh.org/en/latest/docs/user_guide/server.html#streaming-data-with-the-server
Note that selected points are deselected when the plot updates to the new streamed (shuffled in this example) data.
import time
from random import shuffle
from bokeh.plotting import figure, output_server, cursession, show
# prepare output to server
output_server("remember_selected")
p = figure(plot_width=400, plot_height=400,tools="lasso_select,box_select,help")
p.scatter([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], name='ex_line')
show(p)
# create some simple animation..
# first get our figure example data source
renderer = p.select(dict(name="ex_line"))
ds = renderer[0].data_source
while True:
# Update y data of the source object
shuffle(ds.data["y"])
#Can I access currently selected points? (NO!)
print ds.selected['2d']
print ds.selected['1d']
print ds.selected['0d']
# store the updated source on the server
cursession().store_objects(ds)
time.sleep(2.)
To get callbacks, you need to setup a complete Bokeh application. The following demonstrated this:
from bokeh.models import ColumnDataSource, Plot
from bokeh.plotting import figure
from bokeh.properties import Instance
from bokeh.server.app import bokeh_app
from bokeh.models.widgets import HBox
from bokeh.server.utils.plugins import object_page
from random import shuffle
class App(HBox):
extra_generated_classes = [["App", "App", "HBox"]]
jsmodel = "HBox"
# plots
plot = Instance(Plot)
source = Instance(ColumnDataSource)
#classmethod
def create(cls):
# create layout widgets
obj = cls()
# outputs
obj.source = ColumnDataSource(data=dict(x=[1, 2, 3, 4, 5],
y=[6, 7, 2, 4, 5]))
obj.plot = figure(x_range=(0, 6), y_range=(0, 7),
tools="lasso_select,box_select,help", plot_width=400,
plot_height=400)
obj.plot.scatter('x', 'y', source=obj.source, name='ex_line')
# layout
obj.children = [obj.plot]
obj.setup_events()
return obj
def setup_events(self):
super(App, self).setup_events()
if self.source:
self.source.on_change('selected', self, 'click')
def click(self, cds, name, prev, new):
print(new['1d']['indices'])
shuffle(self.source.data['y'])
#bokeh_app.route("/bokeh/select/")
#object_page("select")
def make():
app = App.create()
return app