Using Manipulate to find best starting parameters to fit data
As I understand the question a curve fitting procedure that has the following properties is sought:
- Manually adjust the parameters to get an approximate fit.
- Use these parameters as the starting values for
FindFit
. - Propagate the solution from
FindFit
back to theManipulate
parameters. - Subsequently enable further editing of the
Manipulate
parameters and repeat the cycle.
The following code satisfies this criteria by wrapping Manipulate
inside a DynamicModule
and the use of a Button
to indicate when FindFit
should be run.
data = Table[{x, 8 x^3 - 7 x^2 - 10 x + 1 + RandomReal[{-5, 5}]}, {x, -2, 2, 0.1}];
DynamicModule[
{
sol
},
Manipulate[
If[computeFlag == True,
sol = FindFit[data,
aa x^3 + bb x^2 + cc x +
dd, {{aa, a}, {bb, b}, {cc, c}, {dd, d}}, x];
{a, b, c, d} = {aa, bb, cc, dd} /. sol;
computeFlag = False;
];
Column[{
Dynamic[
Button["Compute",
computeFlag = True
]
],
Show[
Plot[a x^3 + b x^2 + c x + d, {x, -2, 2},
PlotStyle -> Black],
ListPlot[data, PlotStyle -> Red],
ImageSize -> 300,
PlotRange -> {{-2.05, 2.05}, All}
]
}],
(*Manipulate variables*)
{{computeFlag, False}, ControlType -> None},
{{a, 1}, -10, 10, Appearance -> "Open"},
{{b, 1}, -10, 10, Appearance -> "Open"},
{{c, 1}, -10, 10, Appearance -> "Open"},
{{d, 1}, -10, 10, Appearance -> "Open"}
] (* end of Manipulate *)
] (* end of DynamicModule *)
Below is a figure where the parameters have been manually adjusted.
After clicking the Compute button FindFit
propagates the solution back to the Manipulate
parameters.
The user is free to re-edit the Manipulate
parameters and repeat the cycle.
dynamicFit[data_, expr_, pars_] :=
With[{
n = Length[pars],
x1 = Min[First /@ data],
x2 = Max[First /@ data]},
DynamicModule[{pars2, fit, fitted = Null},
pars2 = Table[Unique[], n];
Do[pars2[[i]] = RandomReal[{-2, 2}], {i, n}];
Column[{
Sequence @@ Table[
With[{i = i},
Labeled[
Slider[Dynamic[pars2[[i]]], {-10, 10},
Appearance -> "Labeled",
ImageSize -> 275], pars[[i]], Left]], {i, n}],
Dynamic@Framed@Show[
Plot[Evaluate[{expr[x] /. Thread[pars -> pars2], fitted}],
{x, x1, x2}],
ListPlot[data],
ImageSize -> 300],
Row[{
Button["Fit",
fit = FindFit[data, expr[x], Transpose[{pars, pars2}], x];
fitted = expr[x] /. fit,
ImageSize -> Automatic],
Button["Export",
exported = fitted,
ImageSize -> Automatic]
}]
}]
]
]
Parameters are: data
, model expr
(as a Function) and model parameters pars
(used in the model). Your arguments can be quite general as long as the data is 2D and model a function of one variable. Clear exported
before invoking the routine, adjust parameters dynamically, try a fit, adjust some more ... eventually export the fit to exported
.
test = Table[{x,
x + Sin[3 x] + RandomReal[{-.5, .5}]}, {x, -3, 3, 0.1}];
exported = Null;
dynamicFit[test, Function[x, a x + Sin[b x]], {a, b}]
The fit resides in exported
. This is a bit clumsy, I wish I knew how to do it better.
exported
0.995803 x + Sin[3.04859 x]