Updating Data in Bokeh - python

I'm new to Bokeh and not sure how to get my plot data and how to update it.
My code is following these instructions:
https://github.com/WillKoehrsen/Bokeh-Python-Visualization/blob/master/interactive/exploration/interactive_development.ipynb
But unfurtunately the update method doesn't seem to work for me. i was trying to find documentation regarding this method but couldn't find any. can anyone assist?
basically i've generated a pandas dataframe from my data and converted it to ColumnDataSource. now i want to add or subtract data from it but the new ColumnDataSource does not update the old one.
Any help would be most appreciated! This is my code as of now, it still won't update properly:
def update(attr, old, new):
stations_to_plot = [int(station_selection.labels[i]) for i in station_selection.active]
by_station = pd.DataFrame(data=no_bikes.loc[stations_to_plot,:'23 PM'].values,index=list(map(str,initial_stations))
,columns=no_bikes.loc[:,:'23 PM'].columns.tolist())
new_src = ColumnDataSource(by_station.T)
r.data_source.data.update(new_src.data)
stations=list(map(str,no_bikes.loc[:,'station_avg'].nlargest(10).index.tolist()))
station_selection = CheckboxGroup(labels=stations, active = [0,1,3])
station_selection.on_change('active', update)
initial_stations = [int(station_selection.labels[i]) for i in station_selection.active]
range_select = RangeSlider(start = 0, end = 23, value = (0, 23),step = 1, title = 'Hours to plot')
range_select.on_change('value', update)
by_station = pd.DataFrame(data=no_bikes.loc[initial_stations,:'23 PM'].values,index=list(map(str,initial_stations))
,columns=no_bikes.loc[:,:'23 PM'].columns.tolist())
src = ColumnDataSource(by_station.T)
p = figure(plot_width = 500, plot_height = 500, title = 'Chances not to find bikes',
x_axis_label = 'Hour of the day', y_axis_label = 'Proportion',x_range=src.data['index'])
for i,station in enumerate(src.data.keys()):
if station in list(map(str, initial_stations)):
r=p.line(x='index',y=station,source =src, legend='Station N.'+station, color=Category20_16[i], line_width=5)
controls = WidgetBox(station_selection,range_select)
layout = row(controls, p)
curdoc().add_root(layout)

Related

Interactively change a plot in Bokeh using sliders to select column

My question is very similar to this one, but I still cannot find how to adapt the answers to my problem.
I have a dataframe with 100 columns. I want to use two sliders in Bokeh to select one column to show in the plot. I want to do this with CDSView.
Say the columns are named as such: ["11", "12", .."99"]. Plus I have one column, "x", which is the x axis and does not change. The first slider, range [0-9], should select the first digit of the column name. The second slider should select the last two digits in the same way.
This would mean that if the user selects 2, 5 on the first and second sliders, Bokeh would show a plot using the column "25" from my dataframe.
How can I do this?
So I've found a solution, using some snippets from other questions.
Here is a working example (Bokeh 2+), I hope somebody will find it useful in the future.
import pandas as pd
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.layouts import column
from bokeh.models import CustomJS, Slider
df = pd.DataFrame([[1,2,3,4,5],[2,20,3,10,20]], columns = ['1','21','22','31','32'])
source_available = ColumnDataSource(df)
source_visible = ColumnDataSource(data = dict(x = df['1'], y = df['21']))
p = figure(title = 'SLIMe')
p.circle('x', 'y', source = source_visible)
slider1 = Slider(title = "SlideME", value = 2, start = 2, end = 3, step = 1)
slider2 = Slider(title = "SlideME2", value = 1, start = 1, end = 2, step = 1)
slider1.js_on_change('value', CustomJS(
args=dict(source_visible=source_visible,
source_available=source_available,
slider1 = slider1,
slider2 = slider2), code="""
var sli1 = slider1.value;
var sli2 = slider2.value;
var data_visible = source_visible.data;
var data_available = source_available.data;
data_visible.y = data_available[sli1.toString() + sli2.toString()];
source_visible.change.emit();
""") )
slider2.js_on_change('value', CustomJS(
args=dict(source_visible=source_visible,
source_available=source_available,
slider1 = slider1,
slider2 = slider2), code="""
var sli1 = slider1.value;
var sli2 = slider2.value;
var data_visible = source_visible.data;
var data_available = source_available.data;
data_visible.y = data_available[sli1.toString() + sli2.toString()];
source_visible.change.emit();
""") )
show(column(p, slider1, slider2))

How to update the data plot in a Bokeh app

I am having a Pandas DataFrame with a TimeSeries Index. The idea is to use the bokeh server in order to have a DateSlider. This means the values that I pass over the function are dates. However, I think my problem is the data is not updated when I am running the bokeh server. The Slider appears but the data is not updating while moving the cursor from the Slider. I would like to have your view. This is my code:
#The file was already imported and cleaned as DF: trt_file
day_one = str(min(trt_file.index)) #Checking point file
day_last = str(max(trt_file.index)) #Checking point file
# Creating the data source which will be selected from the whole file
source = ColumnDataSource(data={
"x" : trt_file.loc[day_one]["Depth [m]"],
"y" : trt_file.loc[day_one]["Temperature"],
"Temperature" : trt_file.loc[day_one]["Temperature"],
"Depth" : trt_file.loc[day_one]["Depth [m]"]
})
#Minimum values for axis generation
xmin, xmax = min(trt_file["Depth [m]"]), max(trt_file["Depth [m]"])
ymin, ymax = min(trt_file["Temperature"]), max(trt_file["Temperature"])
#Set the figure fopr the initial draw
p = figure(title="2018-07-09", x_axis_label="Depth [m]", y_axis_label="Temperature", plot_height=300, plot_width=1500, x_range=(xmin, xmax), y_range=(ymin, ymax),tools=[HoverTool(tooltips=[("Temperature", "#Temperature"), ("Depth [m]", "#Depth")])] )
#Draw the values from the source we defined first
p.circle(x="x", y="y", fill_alpha=0.5, source=source)
#Set the axis names
p.xaxis.axis_label = "Kable lenght [m]"
p.yaxis.axis_label = "Temperature [C]"
#Creating the DateTime for the Slider
day_one_dt = (min(trt_file.index)) #Checking point file
day_last_dt = (max(trt_file.index))
#Slider
slider = DateSlider(start=day_one, end=day_last, value=day_one, step=1, title="TRT Day")
#Creating the bokeh interactive figure
def update_plot(attr, old, new): #attribute to change, old_val, new_val
#set day for the slider value
day_start = (str(slider.value)) #Value from the slider
new_data = {
"x" : trt_file.iloc[day_start]["Depth [m]"],
"y" : trt_file.iloc[day_start]["Temperature"],
"Temperature": trt_file.iloc[day_start]["Temperature"],
"Depth" : trt_file.iloc[day_start]["Depth [m]"]
}
source.trt_file = new_data
p.title.text = "Day TRT"
slider.on_change("value", update_plot)
layout = row(widgetbox(slider), p)
curdoc().title = "Temp"
curdoc().add_root(layout)
Bokeh knows nothing at all about a "trt file" so setting source.trt_file is not something Bokeh will notice or do anything with. The property Bokeh knows about and responds to is data:
source.data = new_data

Unable to update datasource in Bokeh python

I am using Bokeh 1.0.1. I am unable to update the data source in the Update method i.e src.data.update(new_src.data) doesn't seem to work. Below is the full code.
def modify_doc(doc):
def create_dataset(df, resample='D'):
# Resample the data
src = df.resample(resample).mean()
# Reset index for hovering
src.reset_index(inplace=True)
return ColumnDataSource(src)
def create_plot(src):
# Blank plot with correct labels
p = figure(plot_width=700, plot_height=300, x_axis_type="datetime",
title = 'Variation of Pollution',
x_axis_label = 'Time', y_axis_label = 'Pollution (µg/m³)')
p.line(source=src, x='Date & Time', y='pm2.5', line_width=2,
color='firebrick', line_alpha=0.5, legend='Actual')
hover = HoverTool(tooltips=[('Pollution', '#{pm2.5} µg/m³'),
('Air Temp', '#{Air Temp.} °C'),
('Temp', '(#{Min. Temp.}{0.2f}, #{Max. Temp.}{0.2f}) °C'),
('Dew Pt.', '#{Dew Pt.} °C'),
('Rainfall', '#Rainfall mm'),
('Wind Dir.', '#{Wind Dir.} °'),
('Wind Speed', '#{Wind Speed} km/hr'),
('Relative humidity', '(#{Min. RH}{0.2f}, #{Max. RH}{0.2f}) %')],
mode='vline')
p.add_tools(hover)
p.legend.click_policy = 'hide'
return p
# Update function takes three default parameters
def update(attr, old, new):
# Resampling list
re_list = ['D', 'W', 'M', 'A']
# Make a new dataset based on the selected carriers and the
# make_dataset function defined earlier
new_src = create_dataset(df,
resample = re_list[resample_button_group.active])
# Update the source used the quad glpyhs
src.data.update(new_src.data)
resample_button_group = RadioButtonGroup(labels=["Day", "Week", "Month", "Year"], active=1)
resample_button_group.on_change('active', update)
controls = WidgetBox(resample_button_group)
# Initial Plot
src = create_dataset(df)
p = create_plot(src.data)
layout = row(controls, p)
doc.add_root(layout)
# Set up an application
handler = FunctionHandler(modify_doc)
app = Application(handler)
You should be able to update the line glyph directly.
First, modify your plotting code to assign a name to the line glyph:
pm_line = p.line(
source=src,
x='Date & Time',
y='pm2.5',
line_width=2,
color='firebrick',
line_alpha=0.5,
legend='Actual',
name='pm_line' # Add this!
)
Then in your update function, replace your existing update line with the following:
pm_line = p.select_one({'name':'pm_line'})
pm_line.data_source.data = new_src.data

Updating hbar_stack in Bokeh Server

I am trying to update the bars in a Bokeh server application. I am already doing this in a line chart like this:
p.line(x="x", y="y1", source=self.source01, line_width=2, line_alpha=1.0
via updating the source's dictionary within a function like this:
self.source01.data = dict(
x=self.df_update['timestamp_h'],
y1=self.df_update[dropdown_aux_surp.value])
but this does not seem to work for an hbar_stack which I create like this:
accu_result_Qaux_therm = 0
acc_result_Qaux_el = 0
acc_result_Q45 = 0
acc_result_Q65 = 0
acc_result_Q85 = 0
self.categories = ['Qaux_therm', 'Qaux_el', ' Q45', 'Q65', 'Q85']
self.exports_source = ColumnDataSource(data=dict(categories=[], Berechnete=[]))
self.exports_dict = {'categories': self.categories ,
'Berechnete': [ accu_result_Qaux_therm, acc_result_Qaux_el, acc_result_Q45, acc_result_Q65, acc_result_Q85]}
self.exports_source.data = self.exports_dict
p_1 = figure(y_range=self.categories, plot_height=250, x_range=(-16, 16),
title="Kumulierte Jahreswerte",
toolbar_location=None)
p_1.hbar_stack(status, y='categories', height=0.9, color='red',
source=self.exports_source.data,
legend=['Legende'])
if try to update the hbar_stack's dict to load new values into the graph, nothing happens:
accu_result_Qaux_therm = 10
acc_result_Qaux_el = 20
acc_result_Q45 = 44
acc_result_Q65 = 5
acc_result_Q85 = 6
self.exports_dict = {'categories': self.categories,
'Berechnete': [accu_result_Qaux_therm, acc_result_Qaux_el, acc_result_Q45,
acc_result_Q65, acc_result_Q85]}
self.exports_source.data = self.exports_dict
I apologize if this can be achieved more efficiently but I am new to Python and Bokeh and just need some enlightenment or information if and how I need to update the hbar_stack differently.
I am very sorry, I solved this by myself. I overlooked the following:
There is a typo: Instead of
p_1.hbar_stack(status, y='categories', height=0.9, color='red',
source=self.exports_source.data,
legend=['Legende'])
it needs to read:
p_1.hbar_stack(status, y='categories', height=0.9, color='red',
source=self.exports_source,
legend=['Legende'])
I.e. instead of referencing the ColumnDataSource Object self.exports_source I had been referencing the dictionary self.exports_source.data. Changing this showed the desired result and the graph was updating.

How to change the drop down values dynamically

I am new to Bokeh and need some help please. I am trying change the drop-down-1 box values dynamically based on other drop-down-2 box selection. I looked at Bokeh examples but cant find one. Here is the code that I am messing around.
source = ColumnDataSource(data=dict(server_list=["old_value_1", "old_value_2"]))
def update():
tech_val = tech.value
if tech_val == 'art':
source.data = dict(
server_list=["new_value_1", "new_value_2"]
)
# servers.update()
env = Select(title="Environment", value="PROD", options=["DEV", "QA", "PROD"])
tech = Select(title="Subject Area", value="science", options=["science", "art"])
servers = Select(title="Server", options=source.data['server_list'])
controls = [env, tech, servers]
for control in controls:
control.on_change('value', lambda attr, old, new: update())
sizing_mode = 'fixed'
inputs = widgetbox(*controls, sizing_mode=sizing_mode)
l = layout([[inputs]], sizing_mode=sizing_mode)
curdoc().add_root(l)
curdoc().title = "Sliders"
Here is an example which will change the options displayed in the Environment drop down depending on which value is selected in the Subject area drop down.
If you also want the values to change you can just use the same approach.
This should allow you to change values and options of drop downs dynamically.
from bokeh.layouts import column,row, widgetbox,layout
from bokeh.io import curdoc
from bokeh.models.widgets import (Select)
from bokeh.plotting import ColumnDataSource
source = ColumnDataSource(data=dict(server_list=["old_value_1", "old_value_2"]))
def update(attrname, old, new):
tval = tech.value
env.options = env_dict[tval]
tech_options = ["science", "art"]
env_options1 = ["DEV", "QA", "PROD"]
env_options2 = ["DEV2", "QA2", "PROD2"]
env_dict = dict(zip(tech_options,[env_options1, env_options2]))
env = Select(title="Environment", value="PROD", options=["DEV", "QA", "PROD"])
tech = Select(title="Subject Area", value="science", options=tech_options)
servers = Select(title="Server", options=source.data['server_list'])
""" update drop down 1 based off drop down 2 values """
tech.on_change("value", update)
sizing_mode = 'fixed'
inputs = widgetbox([env,tech,servers], sizing_mode=sizing_mode)
l = layout([[inputs]], sizing_mode=sizing_mode)
curdoc().add_root(l)
curdoc().title = "Sliders"

Categories