LMFIT | Contents | Download | Develop | ||
Non-Linear Least-Squares Minimization and Curve-Fitting for Python | Introduction | Parameters | Models |
This chapter describes Parameter objects which is the key concept of lmfit. A Parameter is the quantity to be optimized in all minimization problems, replacing the plain floating point number used in the optimization routines from scipy.optimize. A Parameter has a value that can be varied in the fit, fixed, have upper and/or lower bounds. It can even have a value that is constrained by an algebraic expression of other Parameter values. Since Parameters live outside the core optimization routines, they can be used in all optimization routines from scipy.optimize. By using Parameter objects instead of plain variables, the objective function does not have to be modified to reflect every change of what is varied in the fit. This simplifies the writing of models, allowing general models that describe the phenomenon to be written, and gives the user more flexibility in using and testing variations of that model.
Whereas a Parameter expands on an individual floating point variable, the optimization methods need an ordered group of floating point variables. In the scipy.optimize routines this is required to be a 1-dimensional numpy ndarray. For lmfit, where each Parameter has a name, this is replaced by a Parameters class, which works as an ordered dictionary of Parameter objects, with a few additional features and methods. That is, while the concept of a Parameter is central to lmfit, one normally creates and interacts with a Parameters instance that contains many Parameter objects. The objective functions you write will take an instance of Parameters as its first argument.
create a Parameter object.
Parameters: |
|
---|
Each of these inputs is turned into an attribute of the same name.
After a fit, a Parameter for a fitted variable (that is with vary = True) will have the value attribute holding the best-fit value. Depending on the success of the fit and fitting algorithm used, it may also have attributes stderr and correl.
the estimated standard error for the best-fit value.
a dictionary of the correlation with the other fitted variables in the fit, of the form:
{'decay': 0.404, 'phase': -0.020, 'frequency': 0.102}
See Bounds Implementation for details on the math used to implement the bounds with min and max.
The expr attribute can contain a mathematical expression that will be used to compute the value for the Parameter at each step in the fit. See Using Mathematical Constraints for more details and examples of this feature.
set or update a Parameters value or other attributes.
param name: parameter name param value: the numerical value for the parameter param vary: whether to vary the parameter or not. param min: lower bound for value param max: upper bound for value param expr: mathematical expression to use to evaluate value during fit. Each argument of set() has a default value of None, and will
update some Parameter attribute without affecting others, for example:
p1 = Parameter('a', value=2.0)
p2 = Parameter('b', value=0.0)
p1.set(min=0)
p2.set(vary=False)
to set a lower bound, or to set a Parameter as have a fixed value.
Note that to use this approach to lift a lower or upper bound, doing:
p1.set(min=0)
.....
# now lift the lower bound
p1.set(min=None) # won't work! lower bound NOT changed
won’t work – this will not change the current lower bound. Instead you’ll have to use np.inf to remove a lower or upper bound:
# now lift the lower bound
p1.set(min=-np.inf) # will work!
Similarly, to clear an expression of a parameter, you need to pass an empty string, not None. You also need to give a value and explicitly tell it to vary:
p3 = Parameter('c', expr='(a+b)/2')
p3.set(expr=None) # won't work! expression NOT changed
# remove constraint expression
p3.set(value=1.0, vary=True, expr='') # will work! parameter now unconstrained
create a Parameters object. This is little more than a fancy dictionary, with the restrictions that
Two methods are for provided for convenient initialization of a Parameters, and one for extracting Parameter values into a plain dictionary.
add a named parameter. This creates a Parameter object associated with the key name, with optional arguments passed to Parameter:
p = Parameters()
p.add('myvar', value=1, vary=True)
add a list of named parameters. Each entry must be a tuple with the following entries:
name, value, vary, min, max, expr
This method is somewhat rigid and verbose (no default values), but can be useful when initially defining a parameter list so that it looks table-like:
p = Parameters()
# (Name, Value, Vary, Min, Max, Expr)
p.add_many(('amp1', 10, True, None, None, None),
('cen1', 1.2, True, 0.5, 2.0, None),
('wid1', 0.8, True, 0.1, None, None),
('amp2', 7.5, True, None, None, None),
('cen2', 1.9, True, 1.0, 3.0, None),
('wid2', None, False, None, None, '2*wid1/3'))
return an ordered dictionary of name:value pairs containing the name and value of a Parameter.
This is distinct from the Parameters itself, as the dictionary values are not Parameter objects, just the value. This can be a very convenient way to get updated values in a objective function.
return a JSON string representation of the Parameter object. This can be saved or used to re-create or re-set parameters, using the loads() method.
Optional keywords are sent json.dumps().
write a JSON representation of the Parameter object to a file or file-like object in file – really any object with a write() method. Optional keywords are sent json.dumps().
use a JSON string representation of the Parameter object in sval to set all parameter settins. Optional keywords are sent json.loads().
read and use a JSON string representation of the Parameter object from a file or file-like object in file – really any object with a read() method. Optional keywords are sent json.loads().
Using Parameters` and minimize() function (discussed in the next chapter) might look like this:
#!/usr/bin/env python
#<examples/doc_basic.py>
from lmfit import minimize, Parameters, Parameter, report_fit
import numpy as np
# create data to be fitted
x = np.linspace(0, 15, 301)
data = (5. * np.sin(2 * x - 0.1) * np.exp(-x*x*0.025) +
np.random.normal(size=len(x), scale=0.2) )
# define objective function: returns the array to be minimized
def fcn2min(params, x, data):
""" model decaying sine wave, subtract data"""
amp = params['amp'].value
shift = params['shift'].value
omega = params['omega'].value
decay = params['decay'].value
model = amp * np.sin(x * omega + shift) * np.exp(-x*x*decay)
return model - data
# create a set of Parameters
params = Parameters()
params.add('amp', value= 10, min=0)
params.add('decay', value= 0.1)
params.add('shift', value= 0.0, min=-np.pi/2., max=np.pi/2)
params.add('omega', value= 3.0)
# do fit, here with leastsq model
result = minimize(fcn2min, params, args=(x, data))
# calculate final result
final = data + result.residual
# write error report
report_fit(params)
# try to plot results
try:
import pylab
pylab.plot(x, data, 'k+')
pylab.plot(x, final, 'r')
pylab.show()
except:
pass
#<end of examples/doc_basic.py>
Here, the objective function explicitly unpacks each Parameter value. This can be simplified using the Parameters valuesdict() method, which would make the objective function fcn2min above look like:
def fcn2min(params, x, data):
""" model decaying sine wave, subtract data"""
v = params.valuesdict()
model = v['amp'] * np.sin(x * v['omega'] + v['shift']) * np.exp(-x*x*v['decay'])
return model - data
The results are identical, and the difference is a stylistic choice.