How to make DynamicMap adopt to yaxis range/limits change?

From one plot yielded to DynamicMap to another plot - there might be quite a big change in Y axis range.
Unfortunately that is not propagating through the plots.
Here is illustration - please note that I need to scale Y axis manually each time!


Here is the code
from bokeh.models import HoverTool
import holoviews as hv
import panel as pn
from holoviews import opts, streams
from holoviews.plotting.links import DataLink
import numpy as np
pn.extension()
hv.extension('bokeh','matplotlib','plotly',width=100)
data = ([20, 30], [20, 10], ['red', 'blue',])

grid_style = {'grid_line_color': 'black', 'grid_line_width': 1.5,
              'minor_xgrid_line_color': 'lightgray'}
tooltips = [
    ('X', '$x{g}'),
    ('Y', '$y{g}'),
    ('index', '$index'),
    ('color','@color')
]
hover = HoverTool(tooltips=tooltips)
points = hv.Points(data, vdims='color').opts(gridstyle=grid_style, show_grid=True,).redim.range(x=(0, 49), y=(0, 49))
point_stream = streams.PointDraw(data=points.columns(), num_objects=2, source=points, empty_value='black',drag=True,add=False)
table = hv.Table(points, ['x', 'y'], 'color')
DataLink(points, table)

r = np.arange(100*40).reshape([40,100])
sine = np.exp(-(r-2000)**2/2000)*np.sin(r)
curves = [[(il+ix)**3*sine for ix in range(50)] for il in range(50)]

def reactive_curve_plots(data,ind=0):
    name = data['color'][ind]
    xl=round(data['x'][ind])
    il=round(data['y'][ind])
    width=600
    ylim=np.min(curves[il][xl]),np.max(curves[il][xl])
    mean = np.mean(curves[il][xl],axis=0)
    mylim=np.min(mean),np.max(mean)
    linesMean = hv.Curve(mean,label='mean').opts(color='red',axiswise=True,ylim=mylim,apply_ranges=True)
    quants = [10,90]
    quantile = np.quantile(curves[il][xl],q=[i/100.0 for i in quants],axis=0)
    qylim=[(np.min(q),np.max(q)) for q in quantile]
    linesQuantile = hv.Overlay([
        hv.Curve(qv,label='q'+str(q)).opts(line_dash='dashed',color='red',axiswise=True,ylim=qylim[i],apply_ranges=True) for i,(q,qv) in enumerate(zip(quants,quantile))
    ])
    std = np.std(curves[il][xl],axis=0)
    linesStd = hv.Overlay([
        hv.Curve(mean+(2*i-1)*std,label='m'+s+'s').opts(line_dash='dotted',color='green',axiswise=True) for i,s in enumerate(['-','+'])
    ])
    linesOverlay = hv.NdOverlay(
        {i: hv.Curve(curves[il][xl][i,:]).opts(axiswise=True,ylim=ylim) for i in range(curves[il][xl].shape[0])},
        kdims=['k']
    ).opts(width=width)
    return (linesOverlay
            .opts(opts.Curve(color='blue',alpha=0.05,axiswise=True))
            .relabel(group='overlay',label=name)
            *linesMean.opts(axiswise=True)
            *linesQuantile.opts(axiswise=True)
            *linesStd.opts(axiswise=True)).opts(shared_axes=False)

dmap0 = hv.DynamicMap(lambda data: reactive_curve_plots(data,ind=0), streams=[point_stream])
dmap1 = hv.DynamicMap(lambda data: reactive_curve_plots(data,ind=1), streams=[point_stream])

grid = (points + table).opts(
    opts.Layout(merge_tools=False),
    opts.Points(active_tools=['point_draw'], color='color', height=400,
                size=10, tools=[hover], width=400),
    opts.Table(editable=True))

pn.Row(grid.cols(1),
       pn.Row(
           pn.Column(
               dmap0.opts(legend_position='right').relabel(group='curves',label='ZERO (normally RED)'),
               dmap1.opts(legend_position='right').relabel(group='curves',label='ONE (normally BLUE)')
           )
       )
      )

I’m not in love with this code - it’s obviously too crowded to be minimalistic. So if you wish to suggest any updates to make some radical changes - I’m fine with that.
Whatever it takes, really, to get it work!

Thanks in Advance!

instead of

.opts(shared_axes=False)

you can use this:

.opts(shared_axes=True).redim(x=f"x_{ind}").redim.label(**{f"x_{ind}":"x"})

and of course add opts shared_axes=False to your points

1 Like

Thank you very much for your reply, @xavArtley!
I guess all that code confuses more than helps, so I decided to create another illustration of the problem.
Here is the code

import numpy sa np; import panel as pn; import holoviews as hv; hv.extension('bokeh'); pn.extension();
r = np.arange(100*40).reshape([40,100])
sine = np.exp(-(r-2000)**2/2000)*np.sin(r)
curves = [[(il+ix)**3*sine for ix in range(50)] for il in range(50)]
def plot(i):
    mean = np.mean(curves[i][i],axis=0)
    return hv.Curve(mean)

pn.interact(plot,i=(0,49))

Please note that Y AXIS is updating on slider:

And this code is NOT updating Y AXIS limits on slider:

%%output widget_location='top'
hv.DynamicMap(plot,kdims=['i']).redim.range(i=(0,49))

Here is the video:

So I guess that’s really what I want to be in Synchronization on event for DynamicMap.

I’m not a interact user but using depends and by using framewise=True you can achieve it

import numpy as np; import panel as pn; import holoviews as hv; hv.extension('bokeh'); pn.extension();
r = np.arange(100*40).reshape([40,100])
sine = np.exp(-(r-2000)**2/2000)*np.sin(r)
curves = [[(il+ix)**3*sine for ix in range(50)] for il in range(50)]
s = pn.widgets.IntSlider(value=0, start=0, end=49, name='i')
@pn.depends(i=s)
def plot(i):
    mean = np.mean(curves[i][i],axis=0)
    return hv.Curve(mean)

pn.Column(s, hv.DynamicMap(plot).opts(framewise=True))

dmap

1 Like

Thank you, @xavArtley!
Now it’s working fine in the example above as well!! Splending!

What I did is simply changed the axis names for both dmaps in the original example AND put framewise=True everywhere I could possibly find :man_facepalming:

I learned a lot during this experiment! Thank you very much!