Reputation: 932
So yet again I am struggling with Bokeh, and this time with callbacks
from a Select
-menu...
Below is a working example having categorical data. The plotting works fine, but when trying to change the category (from the Select
object), nothing happens! I can't really understand what I am doing wrong here...
import pandas as pd
import math
import numpy as np
from bokeh.models import ColumnDataSource, FactorRange, Select
from bokeh.transform import factor_cmap
from bokeh.plotting import figure, show
from bokeh.palettes import Spectral5
from bokeh.layouts import layout, widgetbox
import random
""" Coloring based on universe """
index_cmap = factor_cmap('x', palette=Spectral5, factors=['Universe_1', 'Universe_2'], start=1)
universe_list = []
category_list_1 = []
category_list_2 = []
category_dependence = {'a' : ['A', 'B', 'C', 'D'], 'b' : ['E', 'F', 'G', 'H'], 'c' : ['I', 'J', 'K', 'L']}
N = 20
for x in range(N):
universe_list.append(random.choice(['Universe_1', 'Universe_2']))
category_list_1.append(random.choice(['a', 'b', 'c']))
# Category 2 is dependent on Category 1
category_list_2.append(random.choice(category_dependence[category_list_1[-1]]))
data = pd.DataFrame({'X' : category_list_1, 'Y' : category_list_2, 'Z' : np.random.randint(0,N,size=(N, 1)).flatten().tolist(), 'W' : universe_list})
data = data.drop_duplicates(subset=['X','Y','W'])
# Dropdown choices
dropdown_choices = sorted(data['X'].drop_duplicates().tolist())
menu_dropdown = Select(title="Category", options=dropdown_choices, value='a')
def select_category():
val = menu_dropdown.value
selected = data.copy()
return selected[selected.X.str.contains(val)==True]
def create_plot():
df_ = select_category()
x = zip(df_['Y'], df_['W'])
y = df_['Z'].values
legend = df_['W'].tolist()
""" Create new figure... """
p = figure(x_range=FactorRange(*x), y_range=(0.0, round(y.max()*1.1,0)), plot_height=500, plot_width=800, title='Some fancy title...', toolbar_location=None, tools="hover")
p.x_range.range_padding = 0.1
p.xaxis.major_label_orientation = math.pi/3
p.xaxis.major_label_text_font_size = "0pt"
p.xgrid.grid_line_color = None
plot_source = ColumnDataSource(data=dict(x=x, top=y, legend=legend))
p.vbar(x='x', top='top', width=0.9, source=plot_source, line_color="white", legend='legend', fill_color=index_cmap)
return p
def update():
l.children[1] = create_plot()
controls = [menu_dropdown]
for control in controls:
control.on_change('value', lambda attr, old, new: update)
inputs = widgetbox(*controls, sizing_mode='fixed')
l = layout([inputs, create_plot()], sizing_mode='fixed')
show(l)
As of the above code, I thought of creating a new plot each time the user makes a change to the Select
object, since the x-/y-axis and all categories will be different - instead of just changing the plot_source
(right or wrong...).
Any help would be very much appreciated...
Upvotes: 0
Views: 467
Reputation: 34568
You are not actually calling your update
function in the lambda. This line:
control.on_change('value', lambda attr, old, new: update)
should instead be this:
control.on_change('value', lambda attr, old, new: update())
Tho I will mention as of version 0.12.14
(released today) it should also work to update the data and factors on the existing plot, instead of replacing the plot wholesale.
Upvotes: 1