Live plot with interactivity

I’m trying to create a jupyter Bokeh demo in which a graph is updated in real time (e.g. via a loop) but at the same time, the updating depends on interactivity from a slider. So a simple example would be:

a random walk in 2D shown on a scatter plot, which updates every millisecond, in which the size of the random jumps is controlled by a slider.

I’ve found Bokeh tutorials showing separately how to do interactivity and live plotting (https://github.com/bokeh/bokeh/tree/branch-3.3/examples/output/jupyter/push_notebook), but I’m having quite a lot of trouble combining them in a clean way. I was wondering if anyone knew of any simple example of this that I could work from, or simply had a suggestion of how to implement.

Thanks so much for your help!

Reuben

I would recommend not using push_notebook and instead embedding a Bokeh server app following this example:

https://github.com/bokeh/bokeh/blob/branch-3.3/examples/server/api/notebook_embed.ipynb

With a Bokeh server app you can mix and use standard Bokeh server periodic callbacks and event callbacks. push_notebook is an older API that pre-dates the Bokeh server by several years, and has various significant limitations in comparison. It is useful for exactly the sort of examples already in the repo, and not much beyond that.

1 Like

A follow up question.

In the linked example, which is very helpful, I see that the variable source can be mutated in a callback. However a new variable I define (eg. j = 0 on the line below the definition of source) cannot be without raising an error. Is there a good way to keep a state variable which isn’t a ColumnDataSource?

Since the entire app including callback functions is itself defined inside a function, you’d need to use the somewhat uncommon nonlocal keyword in this instance.

There are potentially other ways to embed Bokeh apps in notebooks using different applications handler types that don’t involve nested functions. But I’m not sure they are especially better in any particular ways.

Actually, looking back at the relevant codepaths, I think you could define a class with a __call__ method that does the work of modify_doc and then pass an instance of that callable object to show and it would work. In that case, you could use instance vars e.g. self.j freely, which seems a little more commonplace than relying on nonlocal

Yes, here is an updated version from that example:

class bkapp:  
    df = sea_surface_temperature.copy()

    def __init__(self):
        self._theme_json = """
           attrs:
                figure:
                    background_fill_color: "#DDDDDD"
                    outline_line_color: white
                    toolbar_location: above
                    height: 500
                    width: 800
                Grid:
                    grid_line_dash: [6, 4]
                    grid_line_color: white
        """

    def callback(self, attr, old, new):
        if new == 0:
            data = self.df
        else:
            data = self.df.rolling('{0}D'.format(new)).mean()
        self.source.data = ColumnDataSource.from_df(data)

    def __call__(self, doc):
        self.source = ColumnDataSource(data=self.df)
    
        plot = figure(x_axis_type='datetime', y_range=(0, 25),
                      y_axis_label='Temperature (Celsius)',
                      title="Sea Surface Temperature at 43.18, -70.43")
        plot.line('time', 'temperature', source=self.source)
    
        slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
        slider.on_change('value', self.callback)
    
        doc.add_root(column(slider, plot))

Then call with

show(bkapp()) # pass an instance to show

I’ll make an issue to add this version to the docs and “officially” support it.

Edit:

Thank you, that’s awesome! I really appreciate the help!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.