Efficient plotting of function defined analytically (via CustomJSExpr)

Hello,

I need to plot a number of functions defined analytically (via
CustomJSExpr). As far as I understand, Bokeh requires using explicitly
set x array for plot. But amount of (x,y) points that are actually
needed to draw plot lines depend heavily on the range selected by user:
as range becomes smaller, x points should be placed more densely to
keep plots accurate. And using small steps for x over the whole range is
undesirable because functions are quite costly to re-evaluate after
their parameters are interactively changed.

Iā€™ve looked at topic

but I havenā€™t identified an example which is close enough to my case.
What Iā€™d like to have is not actually a downsampling of dataset, but
dynamic generation of ā€œdatasetā€ (i.e., x values).

Any ideas?

cc @Philipp_Rudiger @James_A_Bednar1

I think using CustumJSExpr will not work well if your goal is non-uniform sampling, since each expression only affects on thing (i.e. x- or y-coordinates) at a time. I think youā€™d find better success using a CustomJS callback on range updates. The callback can compute first compute the appropriate x-coordinate distribution based on the range, and then compute the y-values for those computed c-coordinate [1]. Then you can set both the new data for both x and y fields at the same time to update the plot.


  1. Or however, maybe a range start and end is just an initial input, and you have to perform some more complicated iterative mesh refinement. The point is that you can compute both a whole new array of x-coordinates, and a new corresponding array of y-values up front, then update the CDS with both at the same time at the end of the callback. ā†©ļøŽ

Somewhat related ā†’ CustomJSTransform NOT agnostic of CDS length? - #3 by gmerritt123

With enough CustomJS, you can implement what @mateusz described as ā€œderived datasourcesā€, and have your renderers run off them.

1 Like

ā€œDerived datasourcesā€ indeed sounds like a great concept which describes very well what Iā€™m doing now. I actually have ā€œsmallā€ datasources with pairs (x, amplitude), from which I build compositions of Gaussian functions, centered in those xā€™es, scaled by respective amplitudes, and sigma value is taken from UI setting. Each of those compositions form a glyph on the plot. And on top of that, I have ā€œsummingā€ glyph which displays a sum of a few other existing Bokeh glyphs selected by user in UI and multiplied by with user-specified coefficients.

However, that topic doesnā€™t provide an actual outline how to implement proper derived datasource in JS, and corresponding feature request has not been addressed yet too.

Basic/naive approach Iā€™ve done as follows:

  1. Make a ā€œbaseā€ CDS (or, probably better, a custom DataModel) to hold the process variables from which to create your derived CDS.
  2. Instantiate (you can do this python side or JS-side) an empty CDS to hold your derived data.
  3. Point your renderers etc. to run off the derived CDS.
  4. Build CustomJS (to trigger on user action) that willā€¦
  • Collect all necessary widget states/views, and update the ā€œbaseā€ CDS or DataModel
  • Determine the appropriate range/resolution to evaluate the functionā€¦ e.g. simple 1D case, say user is zoomed to x = 1 to 100 on the figure, therefore weā€™ll evaluate our function at 1000 points evenly spaced from from x=1 to 100, so populate an array accordingly, e.g. var xs = [1,1.1,1.2....99.9,100]
  • Evaluate the function at those values e.g. var ys = xs.map(y=>some_function(y,....other widget state variables as args)
  • Update the derived CDS with those values e.g. `derived_src.data = {ā€˜xsā€™:xs,ā€˜ysā€™:ys}

Obviously this can get more complex, as you can ā€œstackā€ derived datasources on top of each other etc, but the overall paradigm doesnā€™t change. When user changes something, check the necessary states, determine the right range/resolution to evaluate things, evaluate them, and update the derived datasources with that.