scipp.scipy.optimize.curve_fit#

scipp.scipy.optimize.curve_fit(f, da, *, p0=None, bounds=None, **kwargs)#

Use non-linear least squares to fit a function, f, to data.

This is a wrapper around `scipy.optimize.curve_fit()`. See there for a complete description of parameters. The differences are:

• Instead of separate xdata, ydata, and sigma arguments, the input data array defines provides these, with sigma defined as the square root of the variances, if present, i.e., the standard deviations.

• The fit function f must work with scipp objects. This provides additional safety over the underlying scipy function by ensuring units are consistent.

• The fit function f must only take a single positional argument, x. All other arguments mapping to fit parameters must be keyword-only arguments.

• The initial guess in p0 must be provided as a dict, mapping from fit-function parameter names to initial guesses.

• The parameter bounds must also be provided as a dict, like p0.

• The fit parameters may be scalar scipp variables. In that case an initial guess p0 with the correct units must be provided.

• The returned optimal parameter values popt and the coverance matrix pcov will have units provided that the initial parameters have units. popt and pcov are a dict and a dict of dict, respectively. They are indexed using the fit parameter names. The variance of the returned optimal parameter values is set to the corresponding diagonal value of the covariance matrix.

Parameters
Returns

• `popt` – Optimal values for the parameters.

• `pcov` – The estimated covariance of popt.

Examples

```>>> def func(x, *, a, b):
...     return a * sc.exp(-b * x)
```
```>>> x = sc.linspace(dim='x', start=0.0, stop=0.4, num=50, unit='m')
>>> y = func(x, a=5, b=17/sc.Unit('m'))
>>> y.values += 0.01 * np.random.default_rng().normal(size=50)
>>> da = sc.DataArray(y, coords={'x': x})
```
```>>> from scipp.scipy.optimize import curve_fit
>>> popt, _ = curve_fit(func, da, p0 = {'b': 1.0 / sc.Unit('m')})
>>> sc.round(sc.values(popt['a']))
<scipp.Variable> ()    float64            [dimensionless]  5
>>> sc.round(sc.values(popt['b']))
<scipp.Variable> ()    float64            [1/m]  17
```

Fit-function parameters that have a default value do not participate in the fit unless an initial guess is provided via the p0 parameters:

```>>> from functools import partial
>>> func2 = partial(func, a=5)
>>> popt, _ = curve_fit(func2, da, p0 = {'b': 1.0 / sc.Unit('m')})
>>> 'a' in popt
False
>>> popt, _ = curve_fit(func2, da, p0 = {'a':2, 'b': 1.0 / sc.Unit('m')})
>>> 'a' in popt
True
```