Hi,
I am eager to utilize the parametrized classes for dashboarding. I am on a learning curve, but it often works very nicely. The challenge I got is when I want to provide the class with some custom instance vaiables and have these update the objects of a param.ObjectSelector. One purpose of this example is to have the class to enableing showing a holoviews plot where the user can choose between pairs of dataframe columns to crossplot (this can be achieved othervise more simply, but I need a simple example here).
First example - hard coded options
First I will show a working example with hard_coded_options and then show a so far failing attempt in enabling custom instance variables.
# Imports
from io import StringIO
import pandas as pd
import param
import holoviews as hv
import panel as pn
from collections import defaultdict
hv.extension()
pn.extension()
# Setting up test data
data_text = (
"""
sensor| a| b| c| e
s1| 7.| 2.| 3.| 11.
s1| 3.| 4.| 4.| 2.
s1| .5| 2.| 3.| 5.
s2| 2.| 3.| 2.| 1.
s2| 1.| 7.| 2.| -2.
s2| 1.| 2.| 1.| 0.
""")
df = pd.read_csv(StringIO(data_text), sep='|', skipinitialspace=True)
Preview of df below:
# setting up predefined options as a list of lists where each inner list is a pair of column names from dataframe.
pairs_custom = [
['a', 'b'],
['b', 'c'],
['a', 'c']
]
# Hard coding the option list into the class is straight forward.
class Viewer(param.Parameterized):
pair = param.ObjectSelector(objects=pairs_custom, default=pairs_custom[0])
@param.depends('pair')
def view(self):
return hv.Points(df, kdims=self.pair)
viewer_firm_options = Viewer()
pn.Row(viewer_firm_options.param, viewer_firm_options.view)
# And it works as expected:
![image|690x336](upload://79lQYSUAvWQ4G2RzyxqAvjXllvR.png)
Second example - user provide instance variable on options
Now the tricky bit comes. I want the class to be usable in a situation where the user will define which pairs that are usable. I would want the user to provide options in the instance variables to a init method.
Very well: While sitting and writing this post I notice this is my the time I actually had this second example working. I so far rely on #440 to update the options of the param.ObjectSelectori inside the init. I post this anyways as it might be useful for others and to discuss one issue.
1 # My best attempt so far (trying to read up on different challenges.
2 class Viewer(param.Parameterized):
3 pair = param.ObjectSelector() # (objects={'A': ['a', 'c']}, default=['a', 'c'])
4 def __init__(self, pairs, **params):
5 super().__init__(**params, name='Test viewer')
6 new_opts = {', '.join(v): v for v in pairs}
7 self.param.pair.names = new_opts
8 self.param.pair.objects = list(new_opts.values())
9 # self.param.pair.default = list(new_opts.values())[1]
10 # pair = param.ObjectSelector(objects=self.pairs_custom) # This one can not reach to self and cannot be used it seems
11 @param.depends('pair')
12 def view(self):
13 return hv.Points(df, kdims=self.pair)
14
15 viewer_flexible = Viewer(pairs=pairs_custom)
16 pn.Row(viewer_flexible.param, viewer_flexible.view)
Working, after a lot of attempts!
But actually, paying attention to the axis labels (xlabel is initiated with ‘sensor’ which is unexpected) and location of points in x-dim these are not correct until the widget has been updated at least one time. Any suggestion here on how to solve it?
Third example - A simpler desired future solution?:
I do not know if it is possible at all, but it would have been a great simplification if line 10 above (5 below) could have been enabled to fetch the init instance variable ‘pairs’.
1 # My 'wish-it-was this-easy' way of writing this
2 class Viewer(param.Parameterized):
3 def __init__(self, pairs, **params):
4 super().__init__(**params, name='Test viewer')
5 pair = param.ObjectSelector(objects=self.pairs) # This one can not reach to self and cannot be used it seems
6 @param.depends('pair')
7 def view(self):
8 return hv.Points(df, kdims=self.pair)
9
10 viewer_flexible = Viewer(pairs=pairs_custom)
11 pn.Row(viewer_flexible.param, viewer_flexible.view)
Currently this last example returns an error indicating that self cannot be reached in line 5
Do anyone know if the last example at all could be possible in terms of how python classes and ‘param’ works?
And do anyone know how to avoid issues with initiating the pair widget in the second example (see animation where xlabel = ‘sensor’ and values are located in the ‘sensor’ space initially which is wrong).