Models#
Built-in models#
Model |
Parameters |
Use |
|---|---|---|
|
|
offset |
|
|
lines, backgrounds |
|
|
smooth backgrounds |
|
|
peaks; |
|
|
resonances; |
|
|
spectral lines |
|
|
decays, |
|
|
steps, titrations |
|
|
scaling laws |
|
|
oscillations |
Shapes such as exponential and sigmoid have no offset of their own. Add one with
+ fw.models.constant, see below.
Every built-in model can be evaluated directly, which is handy for simulations:
fw.models.gaussian(x, amp=2.0, cen=0.0, sigma=1.0)
Models as text#
For quick fits, write the model as an expression:
result = fw.fit("a*exp(-x/tau) + c", x, y, yerr=dy)
Parameters are all names except x, the functions and pi, in order of first appearance.
Available functions include exp, log/ln, log10, sqrt, sin, cos, tan and their
inverses, the hyperbolic functions, abs, erf, erfc and heaviside; see
fitwise.expression.FUNCTIONS. As in Origin, Excel and MATLAB, ^ means power. For
another variable name, use fw.ExpressionModel("a*exp(-t/tau)", independent="t").
Expressions are checked against an allowlist before anything runs. Only numbers, names,
+ - * / ** ^ and these functions are accepted, so an expression cannot access files or
attributes.
Your own functions#
Any function whose first argument is the independent variable works:
def arrhenius(T, A=1e10, Ea=50e3):
return A * np.exp(-Ea / (8.314 * T))
result = fw.fit(arrhenius, T, k, yerr=dk)
Parameter names come from the signature. Numeric defaults are used as starting values; they
only need the right order of magnitude, because fitwise normalizes all parameters internally.
For more control, wrap the function in fitwise.Model and give it a guess function.
x may also be two-dimensional, shape (n, k), for surface fits; it is passed to your
function unchanged.
Composition#
Models combine with + and *:
model = fw.models.gaussian + fw.models.linear
model = fw.models.exponential + fw.models.constant
If a name occurs twice, prefixes are added automatically:
(gaussian + gaussian).param_names
# ('g1_amp', 'g1_cen', 'g1_sigma', 'g2_amp', 'g2_cen', 'g2_sigma')
Choose your own prefixes with gaussian.with_prefix("peak_").
Linear models need no starting values#
If a model is linear in its parameters, such as a line, a polynomial, or a·sin(x) + b·x²,
fitwise recognizes this automatically and solves the problem directly, without iterations or
starting values. The report then shows Linear least squares (direct) and not needed (linear model). This also works for your own functions, and it reaches the certified values of the
NIST linear reference datasets, including the notoriously ill-conditioned Filip polynomial.
Starting values#
For each parameter, the first applicable rule wins, and the report shows which one:
p0given infit()a default value in the function signature
the heuristic of a built-in model (peak position and width, log-linear fit of a decay, periodogram of an oscillation, …)
a multi-start for everything that is still missing (see below)
Sums of built-in models are estimated step by step: first the background from the flat edges of the data, then each peak or decay on what remains.
Your own functions without starting values#
You do not have to give starting values for your own functions. fitwise then finds them like this:
Parameters the model is linear in, such as amplitudes and offsets, are always computed exactly.
For the others, 400 candidates are drawn from ranges adapted to the data: positions and widths of features, frequencies, and many orders of magnitude of both signs. The best 5 are refined.
The report shows multi-start for these parameters.
On a benchmark of 32 realistic lab models (decays, peaks, sigmoids, Arrhenius, Shockley diode, Planck, Hill, Morse, …) with random parameters, this reaches the global minimum in 99.7 % of 3300 fits. It costs about 60 ms per fit instead of 10 ms.
Where it can fail: several strongly overlapping features in one function, with many nonlinear
parameters, for example two overlapping peaks on a decay. Build such models from built-in
components (fw.models.exponential + fw.models.gaussian + fw.models.gaussian), which are guessed
one by one, or give p0 for the positions.