Comments (11)
I always find these questions very hard... But here's my two cents.
I'm in favor of the first syntax, since IMHO it's more Pythonic (attributes over getter functions). Optionally you could change it to fit_result.a
.
The main downside of the first variant is however, that you need to know the names of your parameters when you're writing your code. Otherwise you're stuck with getattr
to retrieve your values (but this should be <20% of all use cases).
Also, a
should be a parameter object (subclass from float?) that has an stddev attribute so it can be used as transparently as possible. If possible, it should be tied in with one of the sympy primitives such that errors get propagated.
As for the implementation, I don't see why you can not implement this relatively simple with a __getattr__
method that first tries to get it from the object itself ( that gets it from a __dict__
?) and otherwise from a param_dict attribute.param_dict
attribute. See https://docs.python.org/3/reference/datamodel.html#object.__getattr__
EDIT: read up on Python documentation. __getattr__
is called only if normal attribute lookup fails.
from symfit.
Thanks for your reply. Upon reading it I realized I should have thought about my original post longer because I didn't specify all the problems I have with the current fit_result.params.a
syntax. So here goes:
To get standard deviation in each paradigm you do
assert fit_result.params.a_stdev == fit_result.stdev(a)
(Btw, the first works by overloading __getattr__
like you suggest)
However, this way of accesing the value and stdev has no consistent way of getting covariances between fit parameters. Therefore it would be nice to have one syntax which feels self-consistent. This is why I implemented the following functions (#43):
fit_result.value(a)
fit_result.stdev(a)
fit_result.covariance(a, b)
fit_result.variance(a)
They all have a similar feel to them, which is why I like them better.
Now, the ParameterDict
was implemented purely to create the ** shorthand when calling models:
y = model(x=xdata, **fit_result.params)
And then given the additional tasks of returning the value and stdev by attribute. (This is why it's fit_result.params.a
rather than fit_result.a
.)
So this is why currently my way of thinking is to get the values, covariance etc. with the new syntax but to still have fit_result.params as a simple OrderedDict
so the ** still works.
One could even consider making FitResults
objects themselves mappable for this, but I don't think that a good idea since conceptually I would not expect to iterate over parameter values when iterating over FitResults
. Rather, I would expect to get the different solutions in case of multiple solutions, which is indeed something that is currently not accounted for.
The fact that all this originates from the need for having the ** shorthand hopefully also explains why fit_result.params.a
returns a float, not an object. It's a wrapper for fit_result.params['a']
. This could of course be changed if we think that would be better, but personally I think returning an object would ad an unnecessary level of complexity.
from symfit.
Ok, that changes things.
Now, a covariance between a
and b
only makes sense in the context of a FitResult
. You can use the same reasoning to argue that the variance of a
also only makes sense in this context. This would mean that you 'fix' the following:
fit_result.stdev(a)
fit_result.covariance(a, b)
fit_result.variance(a)
Then, for the sake of consistency you should also use fit_result.value(a)
.
Food for thought: fit_result.value(a)
or fit_result.value("a")
?
When making a FitResult
mappable, can you distinguish this from an iterable? I.e. **fit_result
acts like a parameter dict while iter(fit_result)
iterates over the separate solutions?
After reading the python data model (again): you can't distinguish these, and that's probably a good thing.
Or should you return a list of FitResult
s when you have multiple solutions (but that messes with your return types)?
from symfit.
Coming back to this my view hasn't changed much.
a = Parameter(3.14)
... # set up model and Fit object
fit_result = fit.execute()
a.value
should always be 3.14 (my initial guess) since I may reuse this parameter in a later fit with the same initial guess. Conversely, fit_result.value(a)
should be whatever value is found in fitting. fit_results.params
should be a mapping to the resulting values (the outcome of the fit) for sake of convenience.
I personally don't really care about fit_result.params.a
. It might be clearer to deprecate this syntax altogether in order to emphasize the difference between a.value
and fit_result.value(a)
.
Note that InteractiveGuess (PR #33) does for param in model.params: param.value = new_value
. It would be nice if that didn't break ;-)
This would mean that model.params is a(n) (ordered)dict which maps parameter objects to their values. This should (of course) be the same kind of object as fit_result.params; but I guess that has to be a {p.name: p.value} mapping...
Now for the bonus question: should model.params refer to the original parameter objects which have their initial guesses as values, or should it refer to the parameter objects that are the result from fitting? I vote for the first.
from symfit.
To continue the disussion in #57, for me it would make sense to implement more Dict-like behaviour. I really like the ability to ** unpack, which suggests a dict, which makes me want to do this:
for key in result.params:
result.params[key]
Which doesnt work because the key is a Parameter instance and not a string. This does work:
for key in result.params:
result.params[str(key)]
Would it make sense to change the __getitem__
of ParameterDict
to:
def __getitem__(self, param):
"""
....
"""
if isinstance(param, Parameter):
param_name = str(param)
elif isinstance(param, str):
param_name = param
else:
#maybe some error raised
return getattr(self, param_name)
from symfit.
Could work, but actually I think it would make more sense to change result.params
from a ParameterDict
to an OrderedDict
. I think that would offer all the functionality needed if we do away with the result.params.a
syntax (which I think we are all onboard with).
This would solve all these problems right?
from symfit.
Hang on, I'm totally lost now.
We have 3 (?) things related to parameters: Parameter objects, Model objects (which have a params attribute) and FitResults objects (which also have a params attribute).
Requirements stated above:
- Parameter object's values do not change when fitting
- model(x=data, **fitresults.params) works
- Model.params and FitResults.params should be the same kind of object (should they? The first could be a list AFAICT)
- Model.params, Model.independent_vars and Model.dependent_vars should be similar/identical in behaviour
- preferably, it should have the Parameter object as key, but that might be incompatible with requirement 2.
Still unclear:
- should the values of Model.params change upon fitting? I think not.
I think an OrderedDict fits all these requirements, but did I miss anything?
from symfit.
Let me go through those points.
- yes
- yes
Model.params
,Model.independent_vars
andModel.dependent_vars
all are list containing the respectiveParameter
's orVariable
's for that model, ordered by alphabet. To clarify, no information of the fit is transferred back to theModel
in any way.Parameter.value
is only read bysymfit
, never written.- They are not the same, and in retrospect
FitResults.params
maybe should have had a different name, but I couldn't think of a better one at the time. MaybeFitResults.kwargs
orFitResults.as_kwargs
? Because it's simply not even related toModel.params
in terms of what it does. It's simply Parameter.name: best_fit_value pairs. (It's still early enough in the project that this could be changed while adding a deprecation warning to the old name such that it still works) FitResults.params
only exists for the purpose of ** unpacking. It is not currently and will never be used for anything else. It's just a view on the actual info. With that in mind it's key has to bestr
.
I think that also answers the unclear point. So yes, I think an OrderedDict should work right?
And maybe another name to avoid confusion?
from symfit.
Thanks.
Both a normal dict
and and OrderedDict
would suit these purposes. Another name might be desirable to avoid confusion with Model.params
.
Would it be feasible to make FitResults
the actual mapping object? i.e. model(x=data, **fitresults)
. IIRC the argument against this was that you could get multiple results from one fit, right? But I think that opens up a different discussion that should get it's own issue.
from symfit.
Is this still an issue?
from symfit.
No, the switch to OrederedDicts has been made. Thanks
from symfit.
Related Issues (20)
- `tests/test_minimizers.py::test_multiprocessing` fails on i386 HOT 1
- sympy expression with arbitrary variables/parameters HOT 8
- Fitting custom model resulted using sympy HOT 15
- Unknown issue with super(type, obj) in fit.py HOT 3
- "Abnormal Termination in LNSRCH" Error HOT 2
- Create a Model from a constant expression returns an error HOT 2
- Model evaluation problems encountered after several derivations HOT 6
- Initial values used don't match those declared HOT 3
- Symfit errors - not fitting to data HOT 1
- Parameter and Variable shapes
- Models should be recursive
- Symbolic objectives
- Numba integration HOT 3
- No compatible with numpy 1.24.1 HOT 8
- Sensitivity of Fit's slope/max values to model parameters HOT 1
- Perform global sensitivity analysis of ODE system HOT 1
- Initial conditions for different values of independent variable HOT 2
- symfit special functions, exponential integral HOT 13
- Fitting multidimensional datasets - symfit HOT 1
- AttributeError: 'Model' object has no attribute '_cached_numerical_components'
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from symfit.