I'm trying to allow the users to select line colors in a bokeh server. My try, adapting this answer, is the following. However, the line color doesn't update. Anyone knows how to fix it?
Also, is ti possible to pass some dictionary for the select, so the options won't be hex code/
from bokeh.io import show, curdoc
from bokeh.models import ColumnDataSource, Legend, CustomJS, Select
from bokeh.plotting import figure
from bokeh.palettes import Category10
from bokeh.layouts import row
import pandas as pd
df0 = pd.DataFrame({'x': [1, 2, 3], 'Ay' : [1, 5, 3], 'A': [0.2, 0.1, 0.2], 'By' : [2, 4, 3], 'B':[0.1, 0.3, 0.2]})
columns = ['A', 'B']
tools_to_show = 'box_zoom,save,hover,reset'
p = figure(plot_height =300, plot_width = 1200,
toolbar_location='above',
tools=tools_to_show)
legend_it = []
color = Category10[10]
columns = ['A', 'B']
source = ColumnDataSource(df0)
c = []
for i, col in enumerate(columns):
c.append(p.line('x', col, source=source, name=col, color=color[i]))
legend_it.append((col, [c[i]]))
legend = Legend(items=legend_it, location=(5,114))#(0, -60))
p.add_layout(legend, 'right')
select = Select(title="color", value=color[0],
options = color)
callbacks = CustomJS(args=dict(renderer=c[0], select=select), code ="""
renderer.glyph.color.value = select.value;
renderer.trigger('change')
""")
select.callback = callbacks
layout = row(select, p)
curdoc().add_root(layout)
the JavaScript code is wrong, please change it to :
renderer.glyph.line_color = select.value;
Related
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]))
I have two plots and a data table. I want to select a value in plot 1 and then the corresponding value of plot two should be highlighted in plot 2. In addition I would like to show a data table under both plots with the selected values. Here is what I have so far:
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.io import show
import numpy as np
import pandas as pd
from bokeh.layouts import row
from bokeh.models.widgets import DataTable, TableColumn
df2 = pd.DataFrame(np.array([[1, 3.280, 3.3925], [2, 3.3012, 3.4303], [3, 3.5972, 3.8696]]),
columns=['abspos', 'val1', 'val1_q'])
source = ColumnDataSource(data=df2)
p1 = figure(title="Plot1",
plot_width=1500,
plot_height=900,
x_range=[0, 5],
y_range=[0, 5])
p1.circle('abspos', 'val1', source=source, line_color=None, color='red', size=6)
pq1 = figure(title="Plot2",plot_width=900, plot_height=900)
pq1.circle('val1_q', 'val1', source=source, line_color=None, size=6)
columns = [
TableColumn(field="abspos", title="abspos"),
TableColumn(field="val1", title="val1"),
TableColumn(field="val1_q", title="val1_q")
]
data_table = DataTable(source=source, columns=columns, width=300, height=280)
def plot_both(plot1, plot2):
show(row(plot1, plot2))
plot_both(p1, pq1)
If I select the data point [2, 3.3012] in plot 1, the data point [3.3012, 3.4303] should be highlighted in plot 2. The data table underneath should show [2, 3.3012, 3.4303].
Question 1: How to achieve a highlight in plot 2 according to selected point in plot 1 (and vice versa).
Question 2: How to display a Table under both plots which shows the data of the selected data points.
The simplest way would be to use the tap tool (code below works for Bokeh v2.1.1)
from bokeh.plotting import show, figure
from bokeh.models import ColumnDataSource, Row, Column, CustomJS, DataTable, TableColumn
import numpy as np
import pandas as pd
df2 = pd.DataFrame(np.array([[1, 3.280, 3.3925], [2, 3.3012, 3.4303], [3, 3.5972, 3.8696]]),
columns=['abspos', 'val1', 'val1_q'])
source = ColumnDataSource(data=df2)
p1 = figure(title="Plot1",plot_width=900, plot_height=500, tools="tap,pan,box_zoom,wheel_zoom,save,reset")
p1.circle('abspos', 'val1', source=source, line_color=None, color='blue', size=10)
p2 = figure(title="Plot2",plot_width=900, plot_height=500, tools="tap,pan,box_zoom,wheel_zoom,save,reset")
p2.circle('val1_q', 'val1', source=source, line_color=None, color='blue', size=10)
columns = [
TableColumn(field="abspos", title="abspos"),
TableColumn(field="val1", title="val1"),
TableColumn(field="val1_q", title="val1_q")
]
dt1 = DataTable(source=source, columns=columns, width=900, height=300)
dt2 = DataTable(source=source, columns=columns, width=900, height=300)
show(Column(Row(p1, p2), Row(dt1, dt2)))
If you need more functionality you would need to add a JS callback like p1.js_on_event('tap', CustomJS(args={...}, code="..."))
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 plot lines from some columns of a dataframe. I would like the hover tool to display the name of the column that originated that data and also some information from other columns not plotted.
For example, in the code below I would like the hover tool to display "Name = A; Aux = 0.1" when the mouse is over the central point on line A. This value is stored in column A1. Conversely, when over the central point on line B the tool should display "Name = B; Aux = 0.3"
from bokeh.io import show
from bokeh.models import HoverTool, ColumnDataSource
from bokeh.plotting import figure
import pandas as pd
df = pd.DataFrame({'x': [1, 2, 3], 'A' : [1, 5, 3], 'A1': [0.2, 0.1, 0.2],
'B' : [2, 4, 3], 'B1':[0.1, 0.3, 0.2]})
tools_to_show = 'box_zoom,save,hover,reset'
p = figure(plot_height =300, plot_width = 1200,
toolbar_location='above',
tools=tools_to_show)
columns = ['A', 'B']
source = ColumnDataSource(df)
for col in columns:
p.line('x', col, source=source)
hover = p.select(dict(type=HoverTool))
hover.tooltips = [("Name","#col"), ("Aux", "#col1")]
hover.mode = 'mouse'
show(p)
Thanks!
There is a recent feature to support this. With Bokeh 0.13.0 or newer, you can set the name on a glyph and refer to that name in a tooltip with $name. Additionally, you can refer to a column with that name with #$name. However, the "indirection" column has to be the one specified in name, so you will have to re-arrange your column names to this expectation:
from bokeh.io import show
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
import pandas as pd
df = pd.DataFrame({'x': [1, 2, 3], 'A_y' : [1, 5, 3], 'A': [0.2, 0.1, 0.2],
'B_y' : [2, 4, 3], 'B':[0.1, 0.3, 0.2]})
tools_to_show = 'box_zoom,save,hover,reset'
p = figure(plot_height =300, plot_width = 1200,
toolbar_location='above', tools=tools_to_show,
# "easy" tooltips in Bokeh 0.13.0 or newer
tooltips=[("Name","$name"), ("Aux", "#$name")])
columns = ['A', 'B']
source = ColumnDataSource(df)
for col in columns:
# have to use different colnames for y-coords so tooltip can refer to #$name
p.line('x', col + "_y", source=source, name=col)
show(p)
I'd like to use multiple Hovertools in one plot together with Hovertool's names attribute to selectivly apply each tool. Take for instance
hover1 = HoverTool(tooltips=[("group", "1")], names = ['line1'])
hover2 = HoverTool(tooltips=[("group", "2")], names = ['lines2'])
and the two data sources:
source1 = ColumnDataSource(data=dict(
xs=[[1, 3, 2], [3, 4, 6, 6]],
ys=[[2, 1, 4], [4, 7, 8, 5]],
))
source2 = ColumnDataSource(data=dict(
xs=[[1, 3, 2], [6, 7, 9, 8]],
ys=[[-1, 0, 1], [1, 1, 2, 1]]
))
I'd though the following (using the bokeh.models API) should do what I want
p = figure(plot_width=400, plot_height=400)
l1 = MultiLine(xs='xs', ys='ys', name='lines1')
l2 = MultiLine(xs='xs', ys='ys', name='lines2')
p.add_tools(hover)
p.add_tools(hover2)
p.add_glyph(source1, l1)
p.add_glyph(source2, l2)
show(p)
Alas the Hovertools in the resulting plot do not work (i.e. no tooltips are shown). Using the bokeh.plotting API as follows and everything works as expected:
p = figure(plot_width=400, plot_height=400, tools=[hover, hover2])
p.multi_line(xs='xs', ys='ys', source=source1, name='lines1')
p.multi_line(xs='xs', ys='ys', source=source2, name='lines2')
show(p)
Question: How does one replicate the result of the bokeh.plotting API with the bokeh.models API?
The names attribute of the HoverTool model in the Bokeh Documentation:
names: property type: List ( String )
A list of names to query for. If set, only renderers that have a matching value for their name attribute will be used.
With this
l1 = MultiLine(xs='xs', ys='ys', name='lines1')
You are assigning the name to the Multiline object and that is a glyph, not a renderer. So try this instead
from bokeh.io import output_notebook, show
output_notebook()
import numpy as np
from bokeh.plotting import figure
from bokeh.models.sources import ColumnDataSource
from bokeh.models.glyphs import MultiLine
from bokeh.layouts import row
from bokeh.models.tools import HoverTool
source = ColumnDataSource(data=dict(
xs1=[[1, 2, 3], [5, 6, 7]],
ys1=[[1, 2, 3], [6, 5, 7]],
xs2=[[7, 8, 9], [1, 2, 3]],
ys2=[[4, 5, 7], [6, 7, 2]],
)
)
hover1 = HoverTool(tooltips=[("group", "1")], names = ['lines1'])
hover2 = HoverTool(tooltips=[("group", "2")], names = ['lines2'])
p = figure(plot_width=400, plot_height=400)
l1 = MultiLine(xs='xs1', ys='ys1')
l2 = MultiLine(xs='xs2', ys='ys2')
r1 = p.add_glyph(source, l1, name='lines1') # the name is assigned to the renderer
r2 = p.add_glyph(source, l2, name='lines2')
# r1.name = 'lines1' # or you could assign the name like this as well
# r2.name = 'lines2'
p.add_tools(hover1)
p.add_tools(hover2)
# p = figure(plot_width=400, plot_height=400, tools=[hover1, hover2])
# p.multi_line(xs='xs1', ys='ys1', source=source, name='lines1')
# p.multi_line(xs='xs2', ys='ys2', source=source, name='lines2')
show(p)