[help needed] Updating callback figure on server does not plot

import pandas as pd
import numpy as np

from bokeh.io import push_notebook, show, output_notebook, output_file
from bokeh.plotting import figure, curdoc
from bokeh.models import Title, Legend, NumeralTickFormatter, CustomJS, LinearAxis, Range1d, Panel, ColumnDataSource
from bokeh.models.widgets import CheckboxGroup, Slider, RangeSlider, Tabs, Button, Dropdown
from bokeh.layouts import row, column, widgetbox
from math import pi


PD = pd.read_excel(“ts.xlsx”, sheet_name=‘ts’)

Format X-axis


def make_data(df, seg_num):
df_temp = df[df.Segment == seg_num]
return ColumnDataSource(df_temp)

source = make_data(PD,1)

define a callback function

def callback(attr, old, new):
new_src = make_data(PD, new)

p = figure(plot_width=900, plot_height=300, y_axis_label='Actual Default Rate')

p0 = p.line('Period', 'Actual', source=source, color='blue', line_width=4, legend_label='Actual')

menu = [(‘Tab 1’, ‘1’), (‘Tab 2’, ‘2’)]
dropdown = Dropdown(label=“Model Segment”, menu=menu)

curdoc().add_root(column(dropdown, p))

Hi @scheine

For future reference, please format your code using the </> icon. It helps with readability and also removes invalid characters if someone wants to cut-and-paste your code into an editor to help investigate.

There are a few issues I see, some of which might not be relevant depending on the version of bokeh you are using and what’s actually in your data.

Your statements in your callback have an error. You define a new data source as new_src. You then update the data source used for the plot with new_scr. Notice the misspelling.

If your Segment column of your data is an integer, make sure you take that into account when using it to downselect what is plotted. Note that the Dropdown menu items are string representations of integers.

The callback syntax for a Dropdown is different than what you’ve shown for the latest version of Bokeh. It should be something like the following. What version of bokeh are you using?

#define a callback function
def callback(event):
    new = int(event.item)
    new_src = make_data(PD, new)

Thanks, very helpful. I am adding a second data series to the plot. I want the first y-axis to have a range of (y_min, y_max) and the second y-axis to have a range of (0, vol). However, it does not work. How should I modify the code?

def callback(event):
new = int(event.item)
new_src = make_data(PD, new)
p = figure(y_range=(y_min,y_max), plot_width=900, plot_height=300)
p.extra_y_ranges = {'Volume': Range1d(start=0, end=vol)}


For the follow-up question, it is hard to tell what is not working and why from the code excerpt.

Also, there’s no formatting/indenting in the code excerpt, so its not possible to discern if you are including all of those statements in the callback. If so, I cannot think of a scenario where it is advised to regenerate figures each time the callback is invoked.

For the extra y- ranges, you need to create the ranges as you have done, but you also need to attach them to the figure. And then when you plot, you need to associate data with that axis that you want to show up on that scale.

Here’s a small example; two y-axes on either side of plot area. Second sine wave is associated with righthand axes.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np

from bokeh.plotting import figure, show
from bokeh.models import LinearAxis, Range1d, ColumnDataSource

x = np.linspace(0.0,1.0,1001)
y = np.sin(2.0*np.pi*x)
z = np.pi*np.sin(3.0*np.pi*x)

data = dict(x=x, y=y, z=z)
source = ColumnDataSource(data=data)

p = figure(width=300, height=300)
p.extra_y_ranges = {'Volume': Range1d(start=-np.pi, end=np.pi)}
p.add_layout(LinearAxis(y_range_name="Volume"), place='right')

p.line(x='x', y='y', source=source, line_color='#ff0000')
p.line(x='x', y='z', source=source, line_color='#0000ff', y_range_name='Volume')

Thanks, how about if I want the start and end values to change with each callback dropdown menu?