Bokeh change data_source aren't updated by push_notebook - python

I'm having trouble continuously update a shown figure. Could someone help me, please?
#!/usr/bin/env python
# coding: utf-8
# In[1]:
import numpy as np
import bokeh
from bokeh.io import push_notebook, show, output_notebook
from bokeh import layouts
from bokeh.plotting import figure
output_notebook()
# In[2]:
def to_data_source(y):
y = np.array(y)
x = np.arange(y.size)
return bokeh.models.ColumnDataSource({
'x': x,
'y': y
})
# In[3]:
# this will plot an empty figure
vis = figure()
handle = show(vis, notebook_handle=True)
# In[4]:
# this will plot on the empty figure
line = vis.line()
line.data_source = to_data_source(np.random.randn(30))
push_notebook(handle=handle)
# In[5]:
# this will not update the figure
line.data_source.data['y'] += np.arange(30)
push_notebook(handle=handle)
# In[6]:
# this will not update the figure
line.update(data_source=to_data_source(line.data_source.data['y'] + np.arange(30)))
push_notebook(handle=handle)
# In[7]:
# this will plot the correct figure that should've been updated to the previous `show`
show(vis)
I tried removing the old glyph and adding a new one every time, and it actually works. However, I don't understand why this simple usage that I see everywhere doesn't work here.
Also gist of the notebook here: https://gist.github.com/uduse/f2b17bc67de8fd0ee32f34a87849c8b6

Try to create the handle of the figure after creating the line from vis.
import numpy as np
import bokeh
from bokeh.io import push_notebook, show, output_notebook
from bokeh import layouts
from bokeh.plotting import figure
output_notebook()
line = vis.line()
line.data_source = to_data_source(np.random.randn(30))
## Handle defined here after adding stuff to the figure
handle = show(vis, notebook_handle=True)
push_notebook(handle=handle)
# this will NOW UPDATE the figure
line.data_source.data['y'] += np.arange(30)
push_notebook(handle=handle)
Actually, I'm not sure why exactly it behaves like this, but It should work that way.
I hope this may help you.

This is actually a known bug. See https://github.com/bokeh/bokeh/issues/8244

Related

Problems plotting interactive maps with Python

I'm learning Python applied to data science and I'm trying to create an interactive map of my city, Madrid (Spain), showing the last election results. All I'm doing is in this GitHub link:
https://github.com/BernardoJoseLlamasVerna/Madrid_Elections_2021
You will see draft operations and looking for the best way to represent my data on a map:
https://github.com/BernardoJoseLlamasVerna/Madrid_Elections_2021/blob/main/Madrid_Elections_2021.ipynb
I'm following an example found on Internet about Wisconsin elections and fixes perfectly with what I would like to do with my data. I have downloaded it and stored on my repo to show you what I'm trying:
https://github.com/BernardoJoseLlamasVerna/Madrid_Elections_2021/blob/main/maps.ipynb
I've tried to do the same with my data, but nothing appears (even errors). The code is as follows:
from bokeh.io import output_notebook
from bokeh.plotting import figure, ColumnDataSource
from bokeh.io import output_notebook, show, output_file
from bokeh.plotting import figure
from bokeh.models import GeoJSONDataSource, LinearColorMapper, ColorBar, HoverTool
from bokeh.palettes import brewer
output_notebook()
import json
# res_w_states["clinton_share"] = res_w_states["clinton"] / res_w_states["total"]
#Convert data to geojson for bokeh
wi_geojson=GeoJSONDataSource(geojson=data.to_json())
color_mapper = LinearColorMapper(palette = brewer['RdBu'][10], low = 0, high = 1)
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal')
hover = HoverTool(tooltips = [ ('Municipio','#Municipio'),('P.P.', '#P.P.'),
('P.S.O.E.','#P.S.O.E.'),
('Votos Totales','#Votos Totales')])
p = figure(title="Elecciones Madrid 2021", tools=[hover])
p.patches("xs","ys",source=wi_geojson,
fill_color = {'field' :'P.P.', 'transform' : color_mapper})
p.add_layout(color_bar, 'below')
show(p)
I've been analysing wi_geojson comparing between mine and what I copied and they seem to follow the same structure.
**QUESTION: ** anyone could give me a hint about what is wrong with my code, data, etc?
Thank you for your help.
P.D.: if anyone could also post a link with better interactive mapping, I would be so glad.
Thanks to mosc9575 I could manage a solution...
The problem was the Bokeh version (1.3.4); once updated (2.3.2) I could figure out my map. Now I have to fix the municipalities names XDD.

bokeh errors: import error, color gradient error, JS feedback and browser selection

from bokeh.core.state import State
from bokeh.io import _CommsHandle, push_notebook
from bokeh.embed import notebook_div
ModuleNotFoundError Traceback (most recent call last)
<ipython-input-4-b18f604973b2> in <module>()
----> 1 from bokeh.core.state import State
2 from bokeh.io import _CommsHandle, push_notebook
3 from bokeh.embed import notebook_div
ModuleNotFoundError: No module named 'bokeh.core.state'
from add_on import output_notebook, show_figure
from bokeh.io import output_notebook
from bokeh.plotting import Figure
from bokeh.models import ColumnDataSource
output_notebook()
The bokeh.core.state import State is the only import that doesnt work. The python versions are the same and there is no file with bokeh in the name. Anyone have any idea whats going on?
FYI: this I'm trying to create a bokeh graph where I can select data points with the lasso and get them back into jupyter notebook to for analysis. Here is the code I have to create the graphs, would love some insight on getting the datapoints back (assuming with Javascript).
import matplotlib as mpl
import bokeh.plotting as bpl
import bokeh.models as bmo
from bokeh.models import CategoricalColorMapper, ContinuousColorMapper
TOOLS="hover,crosshair,pan,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"
def graph_bokeh(algorithm,coordinate_df,metric):
source = bpl.ColumnDataSource(coordinate_df)
# color_map = bmo.CategoricalColorMapper(factors=coordinate_df[metric].unique())
# colors = {1:'red',0:'grey'}
if metric != 'LengthOfStayDaysNBR':
color_mapper = CategoricalColorMapper(palette=['red','grey'])#,factors = metric)
else:
color_mapper = ContinuousColorMapper(palette='Magma256', low=min(coordinate_df[metric]), high=max(coordinate_df[metric]))
bokeh_graph = figure(width=250, height = 250, title = '{}'.format(str(algorithm)[:3]),tools=TOOLS)
bokeh_graph.scatter('x-{}'.format(str(algorithm)[:3]),'y-{}'.format(str(algorithm)[:3])\
,color={'field': metric,'transform': color_mapper}\
, legend = metric, source = source)
return bokeh_graph
for metric in metrics:
bokeh_graphs = {}
for i,algorithm in enumerate(algorithms):
bokeh_graphs[i+1] = graph_bokeh(algorithm,df_algs,metric)
graph_list = []
for k,v in bokeh_graphs.items():
if k%2 == 1 and k!= len(bokeh_graphs):
graph_list.append([bokeh_graphs[k],bokeh_graphs[k+1]])
elif k%2 == 1 and k == len(bokeh_graphs):
graph_list.append([bokeh_graphs[k],None])
p = gridplot(graph_list)
show(p)
(sorry for three/four part question):
I'm having trouble with the color gradient for the lengthofstay metric (a third column i would like to color the x/y coordinates by)
color_mapper = ContinuousColorMapper(palette='Magma256', low=min(coordinate_df[metric]), high=max(coordinate_df[metric]))
always outputs the blue color gradient (no matter what palette i specify)
Lastly,
the graphs open in internet explorer, and i would like chrome.
import webbrowser
webbrowser.register("chrome", None)
doesnt seem to work,
neither does
show(browser='chrome')
That module moved to bokeh.io some time ago:
In [3]: from bokeh.io.state import State
In [4]:
Can I ask why you think you need to use State directly? It's not something I would really consider necessary for normal users to ever use. It mostly exists to facilitate testing.

Plotting in Bokeh using Custom Function?

does anyone know if/how one can use a "custom" function to plot in Bokeh using the Bokeh server? For example, I know you can use something like
plot = figure(toolbar_location=None)
plot.vbar(x='x', width=0.5, bottom=0, top='y', source=source)
But how can you plot using something like
def mplot(source):
p = pd.DataFrame()
p['aspects'] = source.data['x']
p['importance'] = source.data['y']
plot = Bar(p, values='importance', label='aspects', legend=False)
return plot
My current attempt is, here:
http://pastebin.com/7Zk9ampq
but it doesn't run. I'm not worried about getting the function "update_samples_or_dataset" working yet, just the initial plot to show. Any help would be much appreciated. Thanks!
Is this what you want? Note that I did not use the Bar function imported from bokeh.charts as this does not update upon updating the data source.
If you want to stick with using Bar from bokeh.charts you need to recreate the plot each time.
Note: to run this and have updating work - you need to execute bokeh serve --show plotfilename.py from the command line.
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models.widgets import Button
from bokeh.plotting import ColumnDataSource, figure
import random
def bar_plot(fig, source):
fig.vbar(x='x', width=0.5, bottom=0,top='y',source=source, color="firebrick")
return fig
def update_data():
data = source.data
data['y'] = random.sample(range(0,10),len(data['y']))
source.data =data
button = Button(label="Press here to update data", button_type="success")
button.on_click(update_data)
data = {'x':[0,1,2,3],'y':[10,20,30,40]}
source = ColumnDataSource(data)
fig = figure(plot_width=650,
plot_height=500,
x_axis_label='x',
y_axis_label='y')
fig = bar_plot(fig, source)
layout = layout([[button,fig]])
curdoc().add_root(layout)
EDIT: See below a method that plots a bokeh plot but uses data from a dataframe as you wanted. It also will update the plot on each button press. Still you need to use the command bokeh serve --show plotfilename.py
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models.widgets import Button
from bokeh.plotting import ColumnDataSource
from bokeh.charts import Bar
import random
import pandas as pd
def bar_plot(source):
df = pd.DataFrame(source.data)
fig = Bar(df, values='y', color="firebrick")
return fig
def update_data():
data = {'x':[0,1,2,3],'y':random.sample(range(0,10),4)}
source2 = ColumnDataSource(data)
newfig = bar_plot(source2)
layout.children[0].children[1] = newfig
button = Button(label="Press here to update data", button_type="success")
button.on_click(update_data)
data = {'x':[0,1,2,3],'y':[10,20,30,40]}
source = ColumnDataSource(data)
fig = bar_plot(source)
layout = layout([[button,fig]])
curdoc().add_root(layout)
I think you still have to attach your Bar instance to a Figure instance; a Figure is a set of plots, essentially, with niceties like the toolbar.

How to create a multi-line plot title in bokeh?

How do you create a multiline plot title in bokeh?... same question as https://github.com/bokeh/bokeh/issues/994
Is this resolved yet?
import bokeh.plotting as plt
plt.output_file("test.html")
plt.text(x=[1,2,3], y = [0,0,0], text=['hello\nworld!', 'hello\nworld!', 'hello\nworld!'], angle = 0)
plt.show()
Additionally, can the title text string accept rich text?
In recent versions of Bokeh, labels and text glyphs can accept newlines in the text, and these will be rendered as expected. For multi-line titles, you will have to add explicit Title annotations for each line you want. Here is a complete example:
from bokeh.io import output_file, show
from bokeh.models import Title
from bokeh.plotting import figure
output_file("test.html")
p = figure(x_range=(0, 5))
p.text(x=[1,2,3], y = [0,0,0], text=['hello\nworld!', 'hello\nworld!', 'hello\nworld!'], angle = 0)
p.add_layout(Title(text="Sub-Title", text_font_style="italic"), 'above')
p.add_layout(Title(text="Title", text_font_size="16pt"), 'above')
show(p)
Which produces:
Note that you are limited to the standard "text properties" that Bokeh exposes, since the underlying HTML Canvas does not accept rich text. If you need something like that it might be possible with a custom extension
You can add a simple title to your plot with this:
from bokeh.plotting import figure, show, output_file
output_file("test.html")
p = figure(title="Your title")
p.text(x=[1,2,3], y = [0,0,0], text=['hello\nworld!', 'hello\nworld!', 'hello\nworld!'], angle = 0)
show(p)
Addendum
Here is a working example for plotting a pandas dataframe for you to copy/paste into a jupyter notebook. It's neither elegant nor pythonic. I got it a long time ago from various SO posts. Sorry, that I don't remember which ones anymore, so I can't cite them.
Code
# coding: utf-8
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
import pandas as pd
import numpy as np
# Create some data
np_arr = np.array([[1,1,1], [2,2,2], [3,3,3], [4,4,4]])
pd_df = pd.DataFrame(data=np_arr)
pd_df
# Convert for multi-line plotting
data = [row[1].as_matrix() for row in pd_df.iterrows()]
num_lines = len(pd_df)
cols = [pd_df.columns.values] * num_lines
data
# Init bokeh output for jupyter notebook - Adjust this to your needs
output_notebook()
# Plot
p = figure(plot_width=600, plot_height=300)
p.multi_line(xs=cols, ys=data)
show(p)
Plot

Migrating code from Bokeh 0.10.0 to 0.11.0

I have the below example of real time streaming in Bokeh 0.10.0 from reddit thread.
import time
from random import shuffle
from bokeh.plotting import figure, output_server, cursession, show
# prepare output to server
output_server("animated_line")
p = figure(plot_width=400, plot_height=400)
p.line([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"])
# store the updated source on the server
cursession().store_objects(ds)
time.sleep(0.5)
I know that there is no cursession from 0.11.0 version. What would the code be like in Bokeh 0.11.0? Here is my attempt at it. Am I missing something? Basically, what I want the below code to do is to run as an app so that when I provide live streaming data, I can update the source and plot it realtime.
from bokeh.models import ColumnDataSource, HoverTool, HBox, VBoxForm
from bokeh.plotting import Figure, output_file, save
from bokeh.embed import file_html
from bokeh.models import DatetimeTickFormatter, HoverTool, PreText
from bokeh.io import curdoc
from bokeh.palettes import OrRd9, Greens9
plot = Figure(logo=None, plot_height=400, plot_width=700, title="",
tools=["resize,crosshair"])
source = ColumnDataSource(data=dict(x=[], y=[]))
plot.line([1,2,3], [10,20,30], source=source, legend='Price', line_width=1, line_color=OrRd9[0])
curdoc().add_root(HBox(plot, width=1100))
you probably want to add a periodic callback, something like:
def update():
ds.data["y"] = shuffle(y)
curdoc().add_periodic_callback(update, 500)
But also you actually need to put the data into the column data source, , and tell line the columns you want to use, instead of passing list literals to figure:
source = ColumnDataSource(data=dict(x=[1,2,3], y=[10,20,30]))
plot.line('x', 'y', source=source, legend='Price',
line_width=1, line_color=OrRd9[0])

Categories