I’m doing some experiments in a notebook to work out some data flow for a larger application. I have the following code:
# cell1
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
hv.extension('bokeh')
pn.extension()
# cell2
df = pd.DataFrame({k: np.random.default_rng().random(100) + i for i, k in enumerate('abcd')})
pipe = hv.streams.Pipe()
dm = hv.DynamicMap(hv.Dataset, streams=[pipe])
# cell3
pipe.send(df)
# cell4
def plot(elem, x_col, y_col):
range_x = {x_col: (df[x_col].min(), df[x_col].max())}
range_y = {y_col: (df[y_col].min(), df[y_col].max())}
range = {**range_x, **range_y}
values = {**{x_col: df[x_col], y_col: df[y_col]}}
im = hv.Points(elem, kdims=[x_col, y_col]).redim.values(**values).redim.range(**range)
return im
# cell5
x_sel = pn.widgets.Select(name='x_col', options=list(df.columns), value=list(df.columns)[0])
y_sel = pn.widgets.Select(name='y_col', options=list(df.columns), value=list(df.columns)[0])
pn.Column(x_sel, y_sel) # this puts two selectors in the notebook
# cell6
dm.apply(plot, x_col=x_sel, y_col=y_sel) # executing this cell creates the plot
This looks a bit artificial with the pipe
and the inner wrapper of the DynamicMap.apply
, but there’s a reason for this: this code gets used in an application where the plotting logic is accessing a DynamicMap
constructed in this manner, and then calling .apply(hv.Points)
(or other hv
element function) on the result. That works just fine to generate the plot and now I’m trying to wire up various additional pieces of reactivity into it (for example, if someone changes the columns, I want the plot to be updated automatically).
After executing these cells, the output is a couple of selector widgets and a plot. The different dimensions have different ranges and what I want is that when I change the values in the selector, the plot should be updated with the appropriate axes. Now, the weird thing is that this works correctly in the sense that the plot values are themselves updated, so if I change a
to b
in the x_sel
selector, the actual points get updated. However, the axis range is not updated, despite calling redim.range
. On the other hand, if I simply do the following:
dm.apply(plot, x_col='c', y_col='d') # executing this cell creates the plot
this does exactly what I want, i.e. it generates the correct plot with the correct ranges on the axes. It seems to me that these should be equivalent, and when I put print statements in the plot
function, they show identical values for the dictionaries being passed to redim
, but the effects are different. Why?