Liveplotting a function of a ColumnDataSource

Thanks to really useful previous help on this forum, I got a live scatterplot up and running, using a ColumnDataSource with a column for ‘x’ and a value for ‘y’. However, I would like to plot not the column itself, but a function of the column, say the mean. I’m struggling to find out how to do this with a View.

For example, I have something like:

xplot = xfig.circle(x='x1', y='x2', source=self.source, size=20, color="navy", alpha=0.5, 
                    fill_alpha=0.2, line_color=None,  radius=self.radius,
                     hover_fill_color="black", hover_fill_alpha=0.7, hover_line_color=None)

What if instead of plotting the ‘x1’ and ‘x2’ columns, I want to plot their mean?

@reubenharry it would really help to have more details about interactions and goals, since the answer will vary. e.g. is this a standalone HTML output you will regenerate on demand? Then just compute those values once up front. Or these need to respond to some interactions or data source update? If so, what exactly.

Thanks! This is for a jupyter notebook liveplot. Suppose the dataframe in the ColumnDataSource looks like:

'x'           'y'
1.2           4.555
3.44          7.6
...           ...

I would like the graph, side by side, a 2D scatter plot of all the points (this is straightforward), and secondly, a 2D scatterplot with a single point, which is the mean of the ‘x’ column on the x-axis and the mean of the ‘y’ column on the y-axis. Every time I update the dataframe, I’d like the plot of the means to also update.

Thanks so much,

Reuben

It’s still unclear, are you expecting that merely updating a dataframe, e.g. by editing and re-executing a notebook cell for the dataframe and doing nothing else, will automatically update the plots? If so, Bokeh does not support a mode of operation like that. [1] You would need to update the dataframe, then also re-execute something to generate new plots in new output cells (or re-generate plots in old output cells). With that workflow the mean could be computed in the python code that generates the plots, or you could potentially use a CustomJS approach for the mean.

it is possible to embed Bokeh server apps inside notebooks, but with those any plot updates would still only be in response to some event callback. [2]

It’s possible I am misunderstanding something about what you are trying to accomplish, though. As always, the best way to avoid miscommunication is to provide a complete Minimal Reproducible Example of your work.


  1. notebooks are a morass of time-dependent global state smeared across arbitrary, potentially out-of-order executed cells. We just aren’t able to keep that, and the BokehJS runtime, in sync “automatically” ↩︎

  2. I suppose it’s possible an embedded app could set up a periodic callback to “poll” the global dataframe value but I wouldn’t ever personally recommend an approach like that. ↩︎

Hi,

Sorry for not including a minimal example earlier. Here is one below. It runs in a notebook cell:

import time
import numpy as np
from bokeh.io import push_notebook, show, output_notebook
from bokeh.models import HoverTool
from bokeh.plotting import figure
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, Range1d, Slider, Circle, CheckboxButtonGroup, Select
import numpy as np
import pandas as pd
from scipy.stats import gaussian_kde
from bokeh.palettes import Blues9
from bokeh.plotting import figure, show
from bokeh.sampledata.autompg import autompg as dff
import bokeh
from bokeh.palettes import Sunset8
from bokeh.plotting import figure, show
import scipy

output_notebook()
import numpy as np

class bkapp:  
    df = pd.DataFrame({'x1': np.random.normal(size=10), 'x2': np.random.normal(size=10)})


    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 periodically(self):

        pass
        # perform some update here
        # self.source.data = ...
    
        
    def __call__(self, doc):
        self.source = ColumnDataSource(data=self.df)
        
        xfig = figure(width=400, height=400)
        xfig.x_range = Range1d(-5, 5)
        xfig.y_range = Range1d(-5, 5)
        
        
    
        xplot = xfig.circle(x='x1', y='x2', source=self.source, size=20, color="navy", alpha=0.5, 
                    fill_alpha=0.2, line_color=None,  radius=1.0,
                     hover_fill_color="black", hover_fill_alpha=0.7, hover_line_color=None)

        
        doc.add_periodic_callback(self.periodically, 10)
    
        doc.add_root(xfig)
        
show(bkapp(), notebook_url="http://localhost:8891") # put the address of the jupyter notebook you are on here!

So the idea is that periodically will update the plot in real time. This part works. For example, if I update self.source to change the data in the dataframe, that will automatically be reflected in the plot (xplot).

However, the use case I am interested in is to have xplot display some function of self.source.data, not the data itself. So for example, suppose I want to plot the mean of x1 and x2. All I want to do is plot that function of the source data, and have it change accordingly as I update self.source.data in the periodic callback. Is that possible, or is there a better way?

I think you are asking if Bokeh has features to automatically compute and display summary statistics or other derived quantities? If so, the answer is no, you need to compute those things, yourself. This recent answer in another thread seems relevant

Since you are already using Python the very simplest thing to do is just compute those things in your callback, and update (other) ColumnDataSource objects that drive glyphs that display those things. It’s almost certainly possible to figure out something that does the computations with a CustumJS but I don’t see what that effort would gain you here.

Thanks, that answers my question!

1 Like

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