Hi,
I am working on a Python package for data sciences. In particular, I have “models” that are callable objects and depend on some parameters. Currently I have my own implementation for those parameters, but I am trying to revisit this as it is quite clunky. I stumbled on Param and it looks like it could be a good fit for my use cases. Do you know of other similar packages that make use of Param?
I also have more practical questions:
- For my use case, the ability to set a parameter to refer to another parameter is quite useful. However, I want to be quite strict about this: once the user does this, they shouldn’t be able to modify the dependent parameter. Right now I do this by additionally setting the said parameter’s readonly to True, in a method linkparam of the model (Parameterized) class. Is there a “better” way?
- I want to run optimizations over parameter values. For some parameters, I might want to do this in the log-scale. But at the same time, when I print the parameter I don’t want it on the log-scale, same if the user sets the parameter manually. I was thinking of introducing an extra parameter for the log-scale and create a reference to that log-scale parameter. But if the user modifies the parameter directly this will break the reference, and I would want the log-scale parameter to update as well in that case (so the link should be two-way somehow). Any direction on how I might be able to achieve this?
- If I want to create a widget, my understanding is that the go-to is Panel for ease of integration?
Thank you.
That sounds like an appropriate use for Param, to me! Param was developed in a modelling context, for a long-obsolete project called Topographica, and so this use case should be well covered. To address your questions:
- Setting the readonly slot to True does sound appropriate here.
- I’m not sure what you mean about a log scale for a parameter. Are you wanting to e.g. to be able to set it to “10” and but then have it display as “1E10”? If so, you should be able to subclass Number as a special LogNumber to override how the value displays (if you want it stored internally as “10” or how it is set (if you want 10 to be converted to “1E10” before it is stored).
- Panel is the only currently supported set of widget bindings for Param. Over time, there have been bindings for Tk, Qt, and ipywidgets, but none of those are currently maintained. Param authors would be happy to encourage others to develop and maintain other bindings, but we all currently use Panel for our own widget needs, and so do not plan to offer any other widget support at present.
Thanks for the reply! Great, for now I can say that although I do not understand all the intricacies yet, it has made my code much cleaner.
Regarding point 2, sorry for not being clear. I think a minimal example that would answer my use case is the following. Say I have a parameter alpha with bounds (0, 1000). But I also want to define beta=log(alpha), where the equal is an equality not an assignment. I want to be able to either do:
model.alpha = 100 and model.beta changing to 2 automatically
or model.beta = 2 and model.alpha changing to 100 automatically.
And ideally when printing the parameters / in _html_repr_() I only want to print alpha.
I started reading about watchers, I think my answer might be there. Similarly I am trying to enforce that the euclidean norm of several parameters is always normalized to 1, which I guess would also require watchers?
Ok, so you do want the user to specify either the log10-ed version or the bare version, in different scenarios? In that case you can probably use @param.depends
(a higher-level interface than using watchers directly) to write code that updates the other version of it whenever alpha or beta changes. You might need to be careful to avoid circular loops where they bounce back and forth, which is why one-way conversion is much simpler. Not sure what trouble that will run into; it’s been a long time since I’ve tried messing with such an approach! In any case you can mark beta hidden by setting its precedence<0, and I think that will make it not get printed in the HTML repr (though again I don’t remember). Same issues apply to enforcing the Euclidean norm; you can use @depends
rather than watchers, but then you’ll have to make sure that you don’t get trapped in circular updates. In that case I’d personally want to separate the unnormalized and normalized versions of the parameter, e.g. users would set a_unnorm, b_unnorm, and/or c_unnorm, then you’d compute read-only versions a,b,c with @depends
that are automatically normalized whenever the unnormed ones change, and your code would consume the normalized versions rather than the unnormalized ones.
Yes that is what I want to do. I will give it a try with depends, like you said I probably have to be careful with circular calls but I guess I can add a check within the callback that if the values already match then I don’t do anything.
That seems reasonable for the other problem, thank you for the suggestion!