How can I get good fit for polar data

data = {{0, 23.734}, {π/18, 33.469}, {π/9, 37.531},
{π/6, 32.75}, {(2 π)/9, 19.719}, {(5 π)/18, 12.812},
{π/3, 12.156}, {(7 π)/18, 19.375}, {(4 π)/9, 28.281},
{π/2, 27.719}, {(5 π)/9, 20.422}, {(11 π)/18, 14.109},
{(2 π)/3, 8.016}, {(13 π)/18, 9.531}, {(7 π)/9, 13.375},
{(5 π)/6, 12.547}, {(8 π)/9, 13.656}, {(17 π)/18, 15.984},
{π, 23.453}, {(19 π)/18, 35.172}, {(10 π)/9, 38.078},
{(7 π)/6, 32.094}, {(11 π)/9, 19.734}, {(23 π)/18, 13},
{(4 π)/3, 12.641}, {(25 π)/18, 17.656}, {(13 π)/9, 21.484},
{(3 π)/2, 23.547}, {(14 π)/9, 17.141}, {(29 π)/18, 9.359},
{(5 π)/3, 10.156}, {(31 π)/18, 9.078}, {(16 π)/9, 10.828},
{(11 π)/6, 11.906}, {(17 π)/9, 11.625},
{(35 π)/18, 15.938}, {2 π, 24.75}};


I am trying to fit this data but could not get a good fit. Here is my try:

nlm1 = NonlinearModelFit[
data,
a Sin[ t + ϕ]^2 + b Cos[c t + ϕ],
{a, ϕ, b, c}, t
];

mpb1 = Table[
Flatten[{t, nlm1["MeanPredictionBands"]}],
{t, 0, 2 π, π/50}
];

Show[
ListPolarPlot[{data}, PlotStyle -> Red,Joined -> True, Mesh -> All, MeshStyle -> Black],
PolarPlot[{nlm1[t]}, {t, 0, 2 π}]
]


• Try providing better starting values for the parameters? Try another form of the model? I find it hard to even evaluate the quality of the fit since I can't discern any obvious "feature" or "shape" in your data. Apr 21, 2022 at 12:21
• @MarcoB, thanks for the comment..it is updated Apr 21, 2022 at 12:27
• @valarmorghulis is there a reason to think the model you're fitting is correct? I notice there is no restriction on the Cos term to enforce periodicity. You've also got features that are skew to one another, suggesting you'll need skew and normal terms in your model to get a good fit. A Fourier series with 7 terms fits this very well. I'll post it in case it helps. Apr 21, 2022 at 13:12

The model you're fitting doesn't enforce periodicity on the Cos term. It also seems lacking, unless you have a specific reason to suspect this functional form, I would go with something more general, like the first N terms of a Fourier series; 6 seems to work well:

Edit: Roman pointed out my definition using strings and ToExpression was pretty hamfisted. I've modified it to use indexed variables, a[i],b[i] directly.

fitFunc = Total@Table[If[i == 0, a[0], a[i]*Sin[i*t] + b[i]*Cos[i*t]], {i, 0, 6}];
params = Flatten@Table[If[i == 0, a[0], {a[i], b[i]}], {i, 0, 6}];

nlm1 = NonlinearModelFit[data, fitFunc, params, t];

Show[ListPolarPlot[{data}, PlotStyle -> Red, Joined -> True,
Mesh -> All, MeshStyle -> Black],
PolarPlot[{nlm1[t]}, {t, 0, 2 \[Pi]}]]


Gives:

With best fit parameters:

nlm1["BestFitParameters"]

{a[0] -> 19.1258, a[1] -> 1.14452, b[1] -> -0.298426, a[2] -> 6.70162,
b[2] -> 3.68088, a[3] -> -0.669741, b[3] -> -0.0567827,
a[4] -> 4.66445, b[4] -> 5.73173, a[5] -> 0.640311, b[5] -> 0.178148,
a[6] -> 4.3474, b[6] -> -4.06389}

• +1 For whatever it's worth, using $AIC_c$ (i.e., nlm1["AICc"]) for $N=2, 3, 4, 5, 6, 7, 8, 9$ also suggests that $N=6$ is best among those models.
– JimB
Apr 21, 2022 at 14:57
• I do not think it is a good idea to use StringTemplate and ToExpression to generate symbols. Better to use indexing, like a[0] etc. Apr 21, 2022 at 15:00
• @Roman , good point! I was waiting on coffee when I wrote that up :D Apr 21, 2022 at 15:10

You can use a linear fit; in fact what's going on here is similar to a Fourier transform. By using components up to frequency 17 we can get a perfect fit:

With[{n = 17},
func = Flatten@{1, Table[{Cos[i ϕ], Sin[i ϕ]}, {i, n}]}]
(*    {1, Cos[ϕ], Sin[ϕ], Cos[2 ϕ], Sin[2 ϕ], Cos[3 ϕ], Sin[3 ϕ], Cos[4 ϕ], Sin[4 ϕ],
Cos[5 ϕ], Sin[5 ϕ], Cos[6 ϕ], Sin[6 ϕ], Cos[7 ϕ], Sin[7 ϕ], Cos[8 ϕ], Sin[8 ϕ],
Cos[9 ϕ], Sin[9 ϕ], Cos[10 ϕ], Sin[10 ϕ], Cos[11 ϕ], Sin[11 ϕ], Cos[12 ϕ], Sin[12 ϕ],
Cos[13 ϕ], Sin[13 ϕ], Cos[14 ϕ], Sin[14 ϕ], Cos[15 ϕ], Sin[15 ϕ], Cos[16 ϕ], Sin[16 ϕ],
Cos[17 ϕ], Sin[17 ϕ]}    *)

f[ϕ_] = Fit[data, func, ϕ]
(*    19.126 - 0.29809 Cos[ϕ] + 3.68121 Cos[2 ϕ] - 0.0564468 Cos[3 ϕ] +
5.73207 Cos[4 ϕ] + 0.178484 Cos[5 ϕ] - 4.06355 Cos[6 ϕ] +
0.0277196 Cos[7 ϕ] - 0.192153 Cos[8 ϕ] + 0.145366 Cos[9 ϕ] -
0.331939 Cos[10 ϕ] - 0.0507075 Cos[11 ϕ] + 0.138838 Cos[12 ϕ] +
0.612145 Cos[13 ϕ] - 0.0920095 Cos[14 ϕ] - 0.241488 Cos[15 ϕ] -
0.0768152 Cos[16 ϕ] + 0.052813 Cos[17 ϕ] + 1.14452 Sin[ϕ] +
6.70162 Sin[2 ϕ] - 0.669741 Sin[3 ϕ] + 4.66445 Sin[4 ϕ] +
0.640311 Sin[5 ϕ] + 4.3474 Sin[6 ϕ] - 0.484882 Sin[7 ϕ] +
0.12801 Sin[8 ϕ] - 0.277 Sin[9 ϕ] + 0.00229061 Sin[10 ϕ] +
0.321766 Sin[11 ϕ] - 0.202121 Sin[12 ϕ] - 0.398221 Sin[13 ϕ] +
0.0868407 Sin[14 ϕ] - 0.0864252 Sin[15 ϕ] + 0.33726 Sin[16 ϕ] +
0.0571092 Sin[17 ϕ]    *)

P1 = ListPolarPlot[data];
P2 = PolarPlot[f[ϕ], {ϕ, 0, 2π}];
Show[P1, P2]


By lowering n in the definition of the fit functions func, we can make the fit worse: for example, with n=6 we get

• Maybe a warning that needing 35 parameters from just 37 data points isn't a recommended practice?
– JimB
Apr 21, 2022 at 15:05
• Only if you interpret this as a fit; if you look at it as a Fourier transform, then it's perfectly natural to have as many parameters (partial waves) as data points. The Fourier transform point of view also gives an intuition about what happens when we reduce the number of fit parameters. Apr 21, 2022 at 15:06
• We use different religious optometrists. My religion always assumes there is some measurement error but I understand that among physicist-types, that is not always assumed or recognized.
– JimB
Apr 21, 2022 at 15:10
• In other words: using 35 parameters is less of a fit, more of an interpolation scheme. It all depends on what the OP wants to do with the result. Apr 21, 2022 at 19:45
d=#2{Cos[#1],Sin[#1]}&@@@data;

param[x_, m_, t_] :=       Module[{f, n = Length[x], nf},
f = Chop[Fourier[x]][[;; Ceiling[Length[x]/2]]];
nf = Length[f];
Total[Rationalize[
2 Abs[f]/Sqrt[n] Sin[Pi/2 - Arg[f] + 2. Pi Range[0, nf - 1] t], .01][[;; Min[m, nf]]]]]

curve=param[#,20,t]&/@Transpose[d];


Visualising:

 ParametricPlot[curve,{t,0,1}, Epilog->Point[d]]


If interpolation is aim:

{i1,12}= Interpolation/@Transpose[d];
ParametricPlot[ {i1[t],12[t]}, {t,1,37}, Epilog->Point[d]]