One way to address this issue, and one that I think works well, is to fit to an explicitly complex model. While such an approach initially seems unnecessary for this real-valued problem, the crucial point is that, when complex values are encountered, you need a mechanism (that FindFit
can handle without any difficulties) for penalizing the unwanted imaginary part that doesn't involve changing the essential meaning of the model. Just adding Abs
or Re
--a common approach that usually works reasonably well, I admit--will not suffice in more difficult cases. If you're unable to give reasonable initial guesses for the parameters, the fit is then very likely to stray off the real line and into some false minimum somewhere in the complex plane.
It's quite easy to convert a real model to a complex one automatically--and, to a large extent, you don't even have to think about it. Here's a way to do that.
Let's start with your model and data:
data = {
{0.0, 100.0}, {0.02, 81.87}, {0.04, 67.03},
{0.06, 54.88}, {0.08, 44.93}, {0.10, 36.76}
};
model = a b^t;
First we split the data into separate components. Here we simply take the real and imaginary parts, but you can choose whatever transformation you like as long as it represents a complete (not necessarily orthogonal) basis, and maps the complex numbers onto the reals. You should bear in mind, though, that the residuals will now be calculated in this space, and different choices will lead to different error estimates on the parameters. This may be enough (especially for a nonlinear fit) to change the optimum values of the parameters themselves, so choose your transformation wisely.
It's convenient at this point to work with the abscissae and ordinates separately. (And I should note that we assume here a one-dimensional model, which we are converting into an two-dimensional one. A similar approach can be used to change an n-dimensional complex fit into an n+1-dimensional real one, but the code will differ in minor details.)
transformation = {Re, Im};
{abscissae, ordinates} = Transpose[data];
data = Transpose[{abscissae, #}] & /@ Through@transformation[ordinates];
Now we have two data arrays rather than one, but this isn't what FindFit
needs (or can accept). So, we merge these by prepending an index variable to each abscissa:
data = MapIndexed[Prepend[#1, First[#2]] &, data, {2}] ~Flatten~ 1;
Next, the model needs to be extended by a dimension. This can be done using If
, Which
, Piecewise
and so on, but I personally think KroneckerDelta
is neatest:
model = Inner[
#1[model] KroneckerDelta[i, #2] &,
transformation, Range@Length[transformation]
];
Now, FindFit
gets the right answer straight away without any complaints (but don't forget to add the label for the extra dimension!):
FindFit[data, model, {a, b}, {i, t}]
(* -> {a -> 100.004, b -> 0.0000452493} *)
Obviously, if this model wasn't limited to the real line, one might wish to transform the parameters as well. That can be done, of course, but I think it's best left as a subject for another answer.
NonlinearModelFit[]
. If you're fitting an exponential, consider taking the logarithm of values. $\endgroup$\[VeryThinSpace]
issue, I think the issue lies in the message itself: your data isn't real (as in it contains complex numbers). TryLinearModelFit
, instead. $\endgroup$