I have a parameterized class that creates a dynamic number of parameters. The aim of this class is to create interactive matplotlib plots inside Jupyter Notebook.
The final user should be able to use it without any knowledge of panel. So, to create an interactive plot, the user instantiate the class providing a numerical function and one or more parameters. Then, the show
method should be called.
This is what I have so far, which I took inspiration from this thread.
It works, but it is terribly slow: I don’t understand what is causing this slowdown, after all I’m evaluating the function over 30 points, so it must be panel-related. I’m not willing to set throttled=True
to evaluate just 30 points.
What can be done to improve it?
import param
import panel as pn
pn.extension()
import numpy as np
from matplotlib.figure import Figure
class MPL(param.Parameterized):
check_val = param.Integer(default=0)
def __init__(self, func, *parameters):
super().__init__()
# remove the previous class attributes added by the previous instances
cls_name = type(self).__name__
setattr(type(self), "_" + cls_name + "__params", dict())
prev_params = [k for k in type(self).__dict__.keys() if "dyn_param_" in k]
for p in prev_params:
delattr(type(self), p)
self.mapping = {}
# create and attach the params to the class
for i, p in enumerate(parameters):
pname = "dyn_param_{}".format(i)
# TODO: using a private method: not the smartest thing to do
self.param._add_parameter(pname, p)
self.param.watch(self._increment_val, pname)
self.mapping[i] = pname
self.fig = Figure()
self.ax = self.fig.add_subplot(1, 1, 1)
self.ax.plot([], [])
self.ax.set_xlim(-10, 10)
self.ax.set_ylim(-5, 5)
self.x = np.linspace(-10, 10, 30)
self.func = func
self.pane = pn.pane.Matplotlib(self.fig, dpi=96)
def _increment_val(self, *depends):
self.check_val += 1
def _read_parameters(self):
readout = []
for k, v in self.mapping.items():
readout.append(getattr(self, v))
return readout
@param.depends("check_val")
def _view(self):
y = self.func(self.x, *self._read_parameters())
self.ax.lines[0].set_data(self.x, y)
self.pane.param.trigger("object")
return pn.Column(self.param, self.pane)
def show(self):
return pn.Row(self._view)
f = lambda x, t: np.cos(x * t)
c = MPL(f, param.Number(default=1, softbounds=(0, 2)))
c.show()