I've been trying to enable panning/zooming while the hover tool is enabled using this example - http://docs.bokeh.org/en/latest/docs/user_guide/tools.html#hover-tool. But, I can't seem to get the two to work together.
Are they mutually exclusive tools?
I was specifying the tools in the wrong place. Here's a working example if anyone else is trying to do the same -
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import HoverTool
output_file("toolbar.html")
source = ColumnDataSource(
data=dict(
x=[1,2,3,4,5],
y=[2,5,8,2,7],
desc=['A', 'b', 'C', 'd', 'E'],
)
)
hover = HoverTool(
tooltips = [
("index", "$index"),
("(x,y)", "($x, $y)"),
("desc", "#desc"),
]
)
p = figure(plot_width=400, plot_height=400, tools=[hover, 'pan', 'wheel_zoom'],
title="Mouse over the dots")
p.circle('x', 'y', size=20, source=source)
show(p)
Related
I am new to Bokeh and looking for solution to label each data point. Replicating the examples shown in documents, I could not find solutions with X axis being datetime.
import pandas as mypd
from bokeh.models import LabelSet , ColumnarDataSource
from bokeh.plotting import figure, output_file, show
date_1 = ['2020-01-01', '2020-01-02','2020-01-03','2020-01-04','2020-01-05']
sal = mypd.DataFrame(date_1)
sal.columns = ["Date_1"]
sal['Sales'] = [15,25,36,17,4]
sal['Date_1'] = mypd.to_datetime(sal['Date_1'])
p= figure(x_axis_type = "datetime")
p.line(x =sal['Date_1'] ,y = sal['Sales'])
lab = LabelSet(x = sal['Date_1'], y = sal['Sales'], text = sal['Sales'])
p.add_layout(lab)
show(p)
It is throwing the error
ValueError: expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Float)) or Float, got 0 2020-01-01
I understand the error is because x axis take numerical data for labelset.
Is my understanding correct ?
If yes what is the workaround ?
I tried with similar queries but could not find a solution for myself.
Similar Query
And this
The simplest solution is to just use a common data source. It also prevents you from embedding the data twice.
import pandas as pd
from bokeh.models import LabelSet, ColumnDataSource
from bokeh.plotting import figure, show
sal = (pd.DataFrame({'Date_1': pd.to_datetime(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05']),
'Sales': [15, 25, 36, 17, 4]})
.set_index('Date_1'))
ds = ColumnDataSource(sal)
p = figure(x_axis_type="datetime")
p.line(x='Date_1', y='Sales', source=ds)
lab = LabelSet(x='Date_1', y='Sales', text='Sales', source=ds)
p.add_layout(lab)
show(p)
I am trying to inser labels in Bokeh and it is not working.
My code is:
from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.io import output_notebook
from bokeh.models import NumeralTickFormatter
df_carteira_grafico = df_resumo_1
df_carteira_grafico['mes_status'] = (df_carteira_grafico['mes_juncao'].astype(dtype=str))+' - '+df_carteira_grafico['Atraso']
output_notebook()
p=figure()
carteira = df_carteira_grafico['mes_status']
tamanho = df_resumo_1['Valor a Entregar']
p = figure(x_range=carteira, plot_height=300, title="Status_Carteira")
p.vbar(x=carteira, top=tamanho, width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.yaxis[0].formatter = NumeralTickFormatter(format="0.0")
show(p)
I am getting this:
I want to get this:
Tks for the help.
If you put your data in a ColumnDataSource yourself, then that source can be used to drive both the vbar and a LabelSet as demonstrated in the documentation. Something like:
# CDS can also be created directly from data frames, but not clear in your case
source = ColumnDataSource(data=
dict(carteira=carteira, tamanho=tamanho, labels=[str(x) for x in tamanho])
)
p.vbar(x='carteira', top='tamanho', width=0.9, source=source)
labels = LabelSet(x='carteira', y='tamanho', text='labels', y_offset=5, source=source)
p.add_layout(labels)
However please note that I could not actually test this directly, because the example code in your question was not self-contained and complete. Hopefully it points the way, though.
See Providing Data for Plots and Tables for more information about Bokeh data sources.
Got it. For thse who may need in the future, here is the code:
bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.io import output_notebook
from bokeh.models import NumeralTickFormatter
from numpy import pi
from bokeh.models import ColumnDataSource
from bokeh.models import LabelSet
df_carteira_grafico = df_resumo_1
df_carteira_grafico['mes_status'] = (df_carteira_grafico['mes_juncao'].astype(dtype=str))+' - '+df_carteira_grafico['Atraso']
output_notebook()
p=figure()
carteira = df_carteira_grafico['mes_status']
tamanho = df_resumo_1['Valor a Entregar']
source = ColumnDataSource(data=dict(carteira=carteira, tamanho=tamanho, labels=[str(x) for x in tamanho]))
p = figure(x_range=carteira, plot_height=400, title="Status_Carteira")
p.vbar(x='carteira', top='tamanho', width=0.9, source=source)
labels = LabelSet(x='carteira', y='tamanho', text='labels', y_offset=5, source=source)
p.add_layout(labels)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.yaxis[0].formatter = NumeralTickFormatter(format="0.0")
show(p)
I'm trying to plot two things:
Counties for MD, VA, and DC
Lats/Longs for housing listings in that area (most expensive in a certain color
and least expensive in another color)
However, I'm having trouble with part 1, and cannot seem to plot the counties without getting the error message:
"Javascript error adding output!
Error: Error rendering Bokeh model: could not find tag with id: xxxxxxxxxxx
See your browser Javascript console for more details."
where xxxxx are numbers
from bokeh.io import show
from bokeh.models import (
ColumnDataSource,
HoverTool,
LogColorMapper)
from bokeh.palettes import Viridis6 as palette
from bokeh.plotting import figure
from bokeh.sampledata.us_counties import data as counties
from bokeh.sampledata.unemployment import data as unemployment
palette.reverse()
va_counties = {
code: county for code, county in counties.items() if county["state"] == "va"
}
md_counties = {
code: county for code, county in counties.items() if county["state"] == "md"
}
dc_counties = {
code: county for code, county in counties.items() if county["state"] == "dc"
}
va_county_xs = [county["lons"] for county in va_counties.values()]
va_county_ys = [county["lats"] for county in va_counties.values()]
md_county_xs = [county["lons"] for county in md_counties.values()]
md_county_ys = [county["lats"] for county in md_counties.values()]
dc_county_xs = [county["lons"] for county in dc_counties.values()]
dc_county_ys = [county["lats"] for county in dc_counties.values()]
va_county_names = [county['name'] for county in va_counties.values()]
md_county_names = [county['name'] for county in md_counties.values()]
dc_county_names = [county['name'] for county in dc_counties.values()]
#county_rates = [unemployment[county_id] for county_id in counties]
color_mapper = LogColorMapper(palette=palette)
va_source = ColumnDataSource(data=dict(
x=va_county_xs,
y=va_county_ys,
name=va_county_names,
))
md_source = ColumnDataSource(data=dict(
x=md_county_xs,
y=md_county_ys,
name=md_county_names,
))
dc_source = ColumnDataSource(data=dict(
x=dc_county_xs,
y=dc_county_ys,
name=dc_county_names,
))
TOOLS = "pan,wheel_zoom,reset,hover,save"
va = figure(
title="Texas Unemployment, 2009", tools=TOOLS,
x_axis_location=None, y_axis_location=None
)
va.grid.grid_line_color = None
md = figure(
title="Texas Unemployment, 2009", tools=TOOLS,
x_axis_location=None, y_axis_location=None
)
md.grid.grid_line_color = None
dc = figure(
title="Texas Unemployment, 2009", tools=TOOLS,
x_axis_location=None, y_axis_location=None
)
dc.grid.grid_line_color = None
va.patches('x', 'y', source=va_source,
fill_color={'field': 'rate', 'transform': color_mapper},
fill_alpha=0.7, line_color="white", line_width=0.5)
md.patches('x', 'y', source=md_source,
fill_color={'field': 'rate', 'transform': color_mapper},
fill_alpha=0.7, line_color="white", line_width=0.5)
dc.patches('x', 'y', source=dc_source,
fill_color={'field': 'rate', 'transform': color_mapper},
fill_alpha=0.7, line_color="white", line_width=0.5)
hover = p.select_one(HoverTool)
hover.point_policy = "follow_mouse"
hover.tooltips = [
("Name", "#name"),
("(Long, Lat)", "($x, $y)"),
]
show(va)
show(md)
show(dc)
Bokeh maintains an implicit "current document" and by default everything you make gets added to that. This makes it very simple to have a script that generates one HTML file, and especially makes usage in the Jupyter notebook much more simple and transparent. In general this is a net positive, but it does make certain other usage patterns need to be slightly more verbose. In particular in your case with multiple show calls, each call to show gets the same document which is probably not what you intend. The solution is to create and show each plot in order, and call reset_output in between. Additionally, you would really need to specify unique filenames for each separate output, by using output_file. Here is a small complete example:
from bokeh.io import output_file, reset_output, show
from bokeh.plotting import figure
# create and show one figure
p1 = figure(title="plot 1")
p1.circle([1,2,3], [4,6,5])
output_file("p1.html")
show(p1)
# clear out the "current document"
reset_output()
# create and show another figure
p2 = figure(title="plot 2")
p2.circle([1,2,3], [8,6,7])
output_file("p2.html")
show(p2)
There are other ways to do this by managing Bokeh Document objects explicitly yourself, but I would probably say this is the simplest to get started.
I am trying to display line segments on a map using GMapPlot. The lines flashes in red and then disappears, in jupyter notebook. This is my code (some decimals left out):
map_options = GMapOptions(lat=37.88, lng=-122.23, map_type="roadmap", zoom=10)
plot = GMapPlot(
x_range=DataRange1d(), y_range=DataRange1d(), map_options=map_options
)
source = ColumnDataSource( data = dict(
y=[ 37.762260 ],
x=[-121.96226],
ym01=[37.762290 ],
xm01=[-121.96189 ]
)
segment = Segment(x0="x", y0="y", x1="xm01", y1="ym01",line_color="green", line_width=100)
plot.add_glyph(source, segment)
plot.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool())
output_notebook()
show(plot)
UPDATE This issue is resolved in https://github.com/bokeh/bokeh/pull/8240 which will be part of Bokeh 1.0
I've tried to reproduce with updated code:
from bokeh.io import show
from bokeh.models import GMapOptions, ColumnDataSource
from bokeh.plotting import figure, gmap
map_options = GMapOptions(lat=37.88, lng=-122.23, map_type="roadmap", zoom=10)
plot = gmap(google_api_key=API_KEY, map_options=map_options)
source = ColumnDataSource( data = dict(
y=[ 37.762260 ],
x=[-121.96226],
ym01=[37.762290 ],
xm01=[-121.96189 ]
))
plot.segment(x0="x", y0="y", x1="xm01", y1="ym01",line_color="green", line_width=10, source=source)
show(plot)
And can confirm that the segment does not show up. Slightly changing to show circles does work, so I have to conclude that this is a bug of some sort. Please file a detailed GitHub issue to report this bug.
I am using bokeh 0.12.9. I have a table and a figure which I replace in the global layout on callback. I usually build the ColumnDataSource right before I build the new figure/table. Now I wanted to try and see if I can have a global ColumnDataSource so that I can adjust the data via a CDSView (no need to replace table/figure then).
Unfortunately even keeping a separate CDS and view for table and plot fails. When clicking the radio button a couple of times I receive the following javascript error:
Uncaught TypeError: Cannot read property 'data' of undefined
from datetime import date
from random import randint
from bokeh.models import Line
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import bokeh.layouts as layouts
import bokeh.models.widgets as widgets
from bokeh.io import curdoc
from bokeh.models import CustomJS, Slider
from bokeh import palettes
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource, CDSView, IndexFilter
from bokeh.models import widgets
def gen_plot(source=None, view=None):
p = figure(title='test',
x_axis_type="datetime",
plot_width=600, plot_height=400)
colors = palettes.Category10[10]
cols = [str(col) for col in source.column_names]
for ix, col in enumerate(cols):
if col == 'index':
continue
r = p.line(x='index', y=col, source=source, view=view,
legend='_' + col,
color=colors[ix])
p.legend.location = "bottom_left"
return p
def gen_table(source=None, view=None):
columns = [TableColumn(field=ele, title=ele) for ele
in source.column_names]
tab = widgets.DataTable(source=source, view=view, columns=columns,
selectable=False,
reorderable=False,
width=600, height=400)
return tab
def update(attr, old, new):
p = gen_plot(source=cdss[0], view=vs[0])
t = gen_table(source=cdss[1], view=vs[1])
print l.children
l.children[1] = p
l.children[2].children[0] = t
# set up data
cols = ['col1', 'col2', 'col3', 'col4']
df1 = pd.DataFrame(pd.util.testing.getTimeSeriesData())
df1.columns = cols
df2 = pd.DataFrame(pd.util.testing.getTimeSeriesData())
df2.columns = cols
dfs = [df1, df2]
cds1 = ColumnDataSource(df1)
cds2 = ColumnDataSource(df2)
cdss = [cds1, cds2]
filters = [IndexFilter([0, 1, 2, 4])]
filters = []
v1 = CDSView(source=cds1, filters=filters)
v2 = CDSView(source=cds2, filters=filters)
vs = [v1, v2]
# initialize items to replace
p = gen_plot(source=cdss[0], view=vs[0])
t = gen_table(source=cdss[1], view=vs[1])
# initialize controls
radio_wghting = widgets.RadioButtonGroup(labels=["Equal", "Exponential"],
active=0)
radio_wghting.on_change('active', update)
# set up layout
sizing_mode = 'fixed'
l = layout([radio_wghting, p, t], sizing_mode=sizing_mode)
curdoc().add_root(l)
curdoc().title = 'blub'
# call callback initially
update('value', 0, 0)
Any hints are much appreciated!
Now I wanted to try and see if I can have a global ColumnDataSource so
that I can adjust the data via a CDSView (no need to replace
table/figure then).
The code you are showing is the one in which you are trying to replace the figure and table.
When you replace the child of a layout object in that way, you are not actually removing the previous figures from curdoc, and other elements in the document still have the old figures and tables in their references.
You could try something like that to update the sources directly.
for rend in p.renderers:
try:
rend.data_source
except AttributeError:
pass
else:
rend.data_source.data.update(new_data_dictionary)
and
t.source.data.update(new_data_dictionary)
EDIT to answer the comment
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Button
from bokeh.layouts import gridplot, widgetbox
from random import random, choice
import numpy as np
my_data = {1:{'x':[],'y':[],'colo':[],'size':[]}}
kelly_colors = [ '#F3C300','#875692', '#F38400', '#A1CAF1','#BE0032', '#C2B280', '#848482','#008856', '#E68FAC', '#0067A5',
'#F99379', '#604E97', '#F6A600','#B3446C', '#DCD300', '#882D17','#8DB600', '#654522', '#E25822','#2B3D26', ]
x = np.arange(0,50,0.1)
def rand_dict():
rand_x = [choice(x) for i in range(7)]
return {'x':rand_x,'y':np.array([random()*100 for i in rand_x]),'colo':np.array([choice(kelly_colors) for i in rand_x]),'size':np.array([(5+int(random()*50)) for i in rand_x])}
def add_stuff():
global my_data
my_data[max(my_data.keys())+1] = rand_dict()
make_doc()
def change_stuff():
global my_data
myfig = curdoc().select_one({"name":"myfig"})
for i,rend in enumerate(myfig.renderers):
try:
rend.data_source
except AttributeError:
pass
else:
my_data[i+1] = rand_dict()
rend.data_source.data.update(my_data[i+1])
def clear_stuff():
global my_data
my_data = {1:{'x':[],'y':[],'colo':[],'size':[]}}
make_doc()
def make_doc():
curdoc().clear()
myfig = figure(plot_width=1000,plot_height=800,outline_line_alpha=0,name='myfig')
myfig.x_range.start = -5
myfig.x_range.end = 55
myfig.y_range.start = -10
myfig.y_range.end = 110
myfig.renderers = []
add_button = Button(label='add stuff',width=100)
change_button = Button(label='change stuff',width=100)
clear_button = Button(label='clear stuff',width=100)
add_button.on_click(add_stuff)
change_button.on_click(change_stuff)
clear_button.on_click(clear_stuff)
grid = gridplot([[myfig,widgetbox(add_button,change_button,clear_button)]],toolbar_location=None)
curdoc().add_root(grid)
update_doc()
def update_doc():
myfig = curdoc().select_one({"name":"myfig"})
for key in my_data:
myfig.scatter(x='x',y='y',color='colo',size='size',source=ColumnDataSource(data=my_data[key]))
curdoc().title = 'mytitle'
make_doc()
what I like about doing this is that you can just save the my_data dictionary with numpy, load it later and keep changing your plots from there.
def load_data():
global my_data
my_data = np.load(path_to_saved_data).item()
make_doc()
You can probably do something similar using pandas dataframes, I am just more comfortable with plain dictionaries.