Remove bokeh logo from a list of figures - python

You can easily remove Bokeh logo from a single figure doing the following:
from bokeh.plotting import figure, show
from bokeh.models.tools import PanTool, SaveTool
p = figure()
p.line([1, 2, 3, 4],[1, 4, 3, 0])
p.toolbar.logo = None
p.tools = [SaveTool(), PanTool()]
show(p)
or just using p.toolbar_location = None
I, however, didn't manage to hide it when having multiple figures:
from bokeh.plotting import figure, show
from bokeh.models.tools import PanTool, SaveTool
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource, BoxZoomTool, WheelZoomTool, LassoSelectTool, BoxSelectTool, ResetTool, \
PanTool, TapTool, SaveTool
tools = [PanTool(), BoxZoomTool(match_aspect=True), WheelZoomTool(), BoxSelectTool(),
ResetTool(), TapTool(), SaveTool()]
figures = [figure(plot_width=800, plot_height=800,
tools=tools, output_backend="webgl", match_aspect=True) for i in range(2)]
figures[0].line([1, 2, 3, 4], [1, 4, 3, 0])
figures[0].toolbar.logo = None
figures[1].line([1, 2, 3, 4], [1, 4, 3, 0])
figures[1].toolbar.logo = None
show(gridplot([figures], merge_tools=True, sizing_mode='scale_height'))
I've also tried figures.toolbar.logo = None but of course it doesn't work as it's a list and it has no such attribute. How can i do that?

You can configured toolbar options to gridplot by passing a toolbar_options argument to gridplot:
grid = gridplot([figures], merge_tools=True, sizing_mode='scale_height',
toolbar_options=dict(logo=None))
show(grid)
This is documented in the Reference Guide entry for gridplot

logo=None didn't work for me. This css declaration did however:
<style>
.bk-logo {
display:none !important;
}
</style>

Related

How to use custom JS to change source of line plot?

My dataframe has several columns. I want to use a dropdown to trigger a change in the y-values.
So far I have
import pandas as pd
from bokeh.io import output_file, save
from bokeh.models import ColumnDataSource, Dropdown, CustomJS
from bokeh.layouts import column
from bokeh.plotting import figure
if __name__ == '__main__':
df = pd.DataFrame(data={"a": [1, 2, 3], "b": [2, 2, 2], "c": [3, 2, 1]})
source = ColumnDataSource(df)
fig = figure()
line = fig.line(x="a", y="b", source=source)
dropdown = Dropdown(label="Choose y", menu=[("b", "b"), ("c", "c")])
code = """
// how to set the line source equal to "this.item"?
"""
update_line_callback = CustomJS(args={"dropdown": dropdown, "line": line, "source": source}, code=code)
dropdown.js_on_event("menu_item_click", update_line_callback)
output_file("out.html")
save(column(children=[fig, dropdown]))
How do I link the dropdown item with the line source?
One working option is to create a column in your data source you want to show and change this values if the Dropdown is called by the selected name. The new name is this.item if you call dropdown.js_on_event("menu_item_click", *callback)
Here is the working code:
import pandas as pd
from bokeh.io import output_file, save
from bokeh.models import ColumnDataSource, Dropdown, CustomJS
from bokeh.layouts import column
from bokeh.plotting import figure, show, output_notebook
output_notebook()
# create some source
df = pd.DataFrame(data={"a": [1, 2, 3], "b": [2, 2, 2], "c": [3, 2, 1]})
df["show"] = df["b"]
source = ColumnDataSource(df)
# figure
fig = figure()
line = fig.line(x="a", y="show", source=source)
dropdown = Dropdown(label="Choose y", menu=[("b", "b"), ("c", "c")])
# overwrite show values with new selection
code = """
let s = source.data
s["show"] = s[this.item]
source.change.emit();
"""
dropdown.js_on_event("menu_item_click", CustomJS(args={"source": source}, code=code))
# output figure
show(column(children=[fig, dropdown]))

Bokeh: Unable to generate different line colours when using MultiLine glyph

I've used Bokeh to generate a multiline chart that updates using a slider. I cannot find a way to have each line drawn with a different colour. I've tried using itertools to iterate through a palette, and passing a range of palette colours.
Here's the itertools approach (full_source is there to support the slider interaction which uses CustomJS):
import itertools
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.palettes import Category20 as palette
from bokeh.models.glyphs import MultiLine
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.io import show
data={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
colors=itertools.cycle(palette[2])
source = ColumnDataSource(data)
full_source = ColumnDataSource(data)
glyph = MultiLine(xs='xdata', ys='ydata', line_color = next(colors))
p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
p.add_glyph(source, glyph)
print(glyph.line_color)
show(p)
This gives two lines, but both of the same colour. print(glyph.line_color) shows just one color passed - #1f77b4 (which is the first colour in the Category20 palette)
I've also tried using the example found here:
import itertools
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.palettes import Spectral11
from bokeh.models.glyphs import MultiLine
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.io import show
data={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
my_pallet = Spectral11[0:2]
source = ColumnDataSource(data)
full_source = ColumnDataSource(data)
glyph = MultiLine(xs='xdata', ys='ydata', line_color = my_pallet)
p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
p.add_glyph(source, glyph)
print(glyph.line_color)
show(p)
This gives:
ValueError expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Color)) or Color, got ['#5e4fa2', '#3288bd', '#66c2a5']
How can I get multiple colours from a palette into a MultiLine graph?
Ok it looks like I'd not been using ColumnDataSource correctly. By passing the colours into the ColumnDataSource as an additional key:value pair in the data Dict, it works. I also could get rid of the MultiLine glyph object.
Working code is:
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.palettes import Category20 as palette
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.layouts import column, row
from bokeh.io import show
data = {'xs':[[...,...,..,][...,...,...]],'ys':[[...,...,..,][...,...,...]]}
length = len(data)
colors = palette[length]
#because Category20 has a minimum of 3 values, and length may be smaller
while len(colors)>length:
colors.pop()
data['color'] = colors
source = ColumnDataSource(data)
full_source = ColumnDataSource(data)
p = figure(title = None, plot_width = 400, plot_height = 400, toolbar_location = None)
p.multi_line(xs='xdata', ys='ydata', source=source, line_color='color')

Setting absolute screen position of Bokeh Charts in Web App

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:

Bokeh: tools='reset' makes the graph fixed

I wanted to put only reset toolbar on by the graph, so I was trying to like
logo=None, tools='reset'
The reset button is actually placed but instead the graph is fixed and cannot move from original position.
How can I improve it?
To move the plot you just need to add the PanTool. Check this minimal example:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import curdoc
plot = figure(
width=300,
height=300,
tools='pan,reset',
logo=None,
)
x = [1, 2, 3, 4]
y = [4, 3, 2, 1]
source = ColumnDataSource(data=dict(x=x, y=y))
plot.circle(
x='x',
y='y',
source=source,
size=5,
fill_alpha=1.0,
fill_color='green',
line_color=None,
)
curdoc().add_root(plot)
Run this with bokeh serve --show example.py

Add hover to Bokeh without losing rest of tools

So with Bokeh I can do something like this to create a hover option:
From bokeh.models import HoverTool #add hover functionality
Hover = HoverTool(tooltips=[(name1:#column1), (name2:#columns2)])
Plot = figure(tools=[hover])
Plot.circle(x,y,hover_color=’red’)
However, by doing so, I lose the standard tools you get when you call figure() like pan, box_zoom, wheel_zoom, etc. I know I can add them back 1 by 1 inside the figure(tools=[]), but is there a way to only add hover to the rest of the default tools of figure(), after it is defined??
Thanks!
Use the add_tools() method, as outlined in the docs:
https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#specifying-tools
Slightly modified example from the docs:
from bokeh.plotting import figure, output_file, show
from bokeh.models import HoverTool
output_file("toolbar.html")
# create a new plot with the toolbar below
p = figure(plot_width=400, plot_height=400,
title=None, toolbar_location="below")
p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)
p.add_tools(HoverTool())
show(p)

Categories

Resources