7
$\begingroup$

I have to fit a Cos^2 function to data I measured. The function is $a \cos^2(\frac{bx\pi}{180}+c)+d $ and I tried the FindFit and Linear Model Function. I have 4 datasets which I have to fit, the first one worked. The other three only yielded not usable fits. I am pretty new to Mathematica so I hope its just a newbie mistake which is easy to fix.

Here's a minimal example:

data45 = Import["data45.txt", "table"]

{{0, 132}, {20, 279.5}, {40, 289}, {60, 312}, {80, 307}, {100, 
      173}, {120, 92}, {140, 25}, {160, 44.5}, {180, 109.5}, {200, 
      230.5}, {220, 305}, {240, 339}, {260, 246.5}, {280, 181.5}, {300, 
      92.5}, {320, 32}, {340, 43}}

FindFit[data45, a Cos[(b x Pi)/180 + c]^2 + d, {a, b, c, d}, x]
{a -> 45.2733, b -> 0.886263, c -> 39.01, d -> 157.974}

This yields the following fit of the data:

Fit

Which is not usable.

I would really appreciate some help!

Greetings

$\endgroup$
1
  • 1
    $\begingroup$ Since a Cos[(b x 𝜋)/180 + c]^2 + d == a/2 Cos[(b x 𝜋)/90 + 2 c] + (d + a/2) you may be better off (in terms of numerics) to fit a cosine instead of a squared cosine, and then re-interpret the fitting parameters. A further advantage of this would be that you can get an initial guess of the fitting parameters from looking at the peak of the Fourier transform. $\endgroup$
    – Roman
    Commented Feb 10, 2019 at 18:52

5 Answers 5

14
$\begingroup$

First, you better use NonlinearModelFit, Fit and FindFit haven't updated in a while, so newly introduced LinearModelFit and NonlinearModelFit will have greater accuracy and better performance.

Second, for arbitrary curve the initial values for parameters can lead to a local minimum for fitting, thus producing wrong curve fit. The best approach would be to specify some initial values based on some empirics: maximum and minimum values, median etc.

res = NonlinearModelFit[data45, a Cos[b x + c]^2 + d, {{a, 150}, {b, 1/70}, {c, 12}, {d, 170}}, x];
Show[ListPlot[data45, PlotStyle -> Red], Plot[res[x], {x, 0, 400}]]

$\endgroup$
9
$\begingroup$

The two answers so far (@MassDefect and @m0nhawk) are exactly what you want to do: have good initial guesses (especially for anything dealing with sines and cosines).

If you are fitting many sets of data to the same model, then automating the initial values is recommended. For your example, you could use:

sol = FindFit[data45, a Cos[(b x Pi)/180 + c]^2 + d,
  {{a, (Max[data45[[All, 2]]] - Min[data45[[All, 2]]])/2}, b, c, {d, Mean[data45[[All, 2]]]}}, x]
$\endgroup$
8
$\begingroup$

Fitting arbitrary curves is actually a pretty difficult task as it's hard for a computer to make a good guess about what the initial parameters should be. We can help it out by providing some initial values that seem to make sense.

params = FindFit[
  data45, 
  a Cos[b x + c]^2 + d, 
  {{a, 300}, {b, 2 \[Pi]/300}, {c, \[Pi]/2}, {d, 50}}, 
  x]

Cos square plot with fitting

If the fitting algorithm is still having difficulty, you can also add in constraints to force it to only look for the best values in a given range like this:

params = FindFit[
  data45, 
  {a Cos[b x + c]^2 + d, {250 < a < 350, 2\[Pi]/350 < b < 2\[Pi]/250,
     0 < c < \[Pi], 25 < d < 75}}, 
  {{a, 300}, {b, 2 \[Pi]/300}, {c, \[Pi]/2}, {d, 50}}, 
  x]
$\endgroup$
5
$\begingroup$

As always you can minimize cost function using NMinimize.

ClearAll["Global`*"]

data = {{0, 132}, {20, 279.5}, {40, 289}, {60, 312}, {80, 307}, {100, 
    173}, {120, 92}, {140, 25}, {160, 44.5}, {180, 109.5}, {200, 
    230.5}, {220, 305}, {240, 339}, {260, 246.5}, {280, 181.5}, {300, 
    92.5}, {320, 32}, {340, 43}};

model[x_] := a Cos[b x + c]^2 + d

cost = Total[(#2 - model[#1])^2 & @@@ data];
fit = NMinimize[{cost, 0 < a < 500, 0 < b < 0.1, 0 < c < 20, 
   0 < d < 50}, {a, b, c, d}, Method -> "DifferentialEvolution", 
  MaxIterations -> 1000]

{5477.29, {a -> 300.499, b -> 0.0172855, c -> 8.49213, d -> 28.431}}

Thread[{a, b, c, d} = {a, b, c, d} /. Last@fit];
Show[Plot[model[x], {x, 0, 350}], ListPlot[data, PlotStyle -> Red]]

enter image description here

$\endgroup$
3
  • $\begingroup$ For whatever it's worth, the method you suggest (NMinimize with "DifferentialEvolution" does seem to be relatively robust to not-so-hot starting values. But NonlinearModelFit does automatically give essential summary statistics (such as standard errors for predictions and prediction intervals). I consider such summary statistics essential but in this forum folks often seem not to share that view. So your approach to get starting values for NonlinearModelFit might be the way to go for troublesome fits. $\endgroup$
    – JimB
    Commented Feb 10, 2019 at 20:25
  • 3
    $\begingroup$ Actually one can use NonlinearModelFit with Method option NMinimize like so NonlinearModelFit[ data, {a Cos[b x + c]^2 + d, 0 < a < 500, 0 < b < 0.1, 0 < c < 20, 0 < d < 50}, {a, b, c, d}, x, Method -> {"NMinimize", {Method -> "DifferentialEvolution"}}, MaxIterations -> 1000]; Get the same result and do more analysis. $\endgroup$ Commented Feb 10, 2019 at 20:40
  • $\begingroup$ Even better! Thanks! $\endgroup$
    – JimB
    Commented Feb 10, 2019 at 20:55
0
$\begingroup$

You can solve the problem with FindFit-Option Method->"NMinimize

FindFit[data45, a Cos[(b x Pi)/180 + c]^2 + d, {a, b, c, d}, x, 

Method -> "NMinimize"] ({a -> 300.499, b -> 0.990388, c -> 65.0408, d -> 28.431})

$\endgroup$
2
  • $\begingroup$ What made it work wasn't really NMinimize though, but rescaling the argument of cosine, so that automatic initial guesses for b are more likely to be reasonable. $\endgroup$
    – Szabolcs
    Commented Feb 11, 2019 at 9:32
  • $\begingroup$ @Szabolcs Thanks, in MMA 11.0.1 you get two different results! $\endgroup$ Commented Feb 11, 2019 at 11:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.