Fitting a sine to data

11 views (last 30 days)
Felix94
Felix94 on 17 Aug 2022
Edited: John D'Errico on 17 Aug 2022
Dear community,
I am trying to use MATLAB to fit a sine to random data (as a first test).
I have thrown together some code based on this previous question
However my fit works only sometimes. Most of the time it does not, although I choose almost the exact "true" parameters. The data that I later want to apply this code to will not be as nice as this.
What is the reason the fit derails so horribly although the data is pretty "obvious" ?
Am I doing something fundamentally wrong?
Can I improve it by specifying certain fitoptions?
Best regards,
clear all;
close all;
FigPosFigSize = [4 1.5 30 22.5];
f = 1;
time_stamps = rand(500, 1) * 5;
time_stamps = sort(time_stamps);
phase = 0;
amplitude = 2;
offset = 0;
y = sin((2*pi*f*time_stamps + phase))*amplitude + offset;
%%
ft = fittype('sin((2*pi*f*x + phase))*amplitude + offset','coefficients',{'phase','amplitude','offset', 'f'}, 'independent', {'x'})
options = fitoptions('Method','NonlinearLeastSquares',...
'Algorithm', 'Levenberg-Marquardt',...
'StartPoint',[0, 1.9, 0, 0.9]);
[mdl gof output] = fit(time_stamps, y, ft, options);
x = 0:0.01:5;
fct = sin((2*pi*mdl.f* x + mdl.phase))*mdl.amplitude + mdl.offset;
f1 = figure('Units', 'centimeters', 'Position', FigPosFigSize);
hold all
s1 = scatter(time_stamps, y, 15, [0 0 0] ,'filled' );
l_fit = plot(x, fct, 'Color', [0 0 1] ,'LineWidth', 2 );
set(gca,'LineWidth', 1.25 ,'FontSize', 25);
ax = gca;
box on;
set(gcf, 'color','w')

Answers (1)

John D'Errico
John D'Errico on 17 Aug 2022
Edited: John D'Errico on 17 Aug 2022
Are you doing something fundamentally wrong? Sort of, yes.
Fitting periodic functions has a probem, in that you will need to provide intelligent starting values for the period. I'll pick an example:
x = rand(200,1)*5;
y = 4*sin(10*x + 3) + randn(size(x))/10;
plot(x,y,'.')
So a little bit of noise. A very nice sine wave. Now let me try to fit it, using fit.
mdl = fittype('a*sin(b*x+c)','indep','x');
fittedmdl = fit(x,y,mdl)
Warning: Start point not provided, choosing random start point.
fittedmdl =
General model: fittedmdl(x) = a*sin(b*x+c) Coefficients (with 95% confidence bounds): a = 13.69 (-5.424e+04, 5.427e+04) b = 0.01773 (-70.28, 70.31) c = -0.03438 (-136.4, 136.3)
Well, that fit will look like pure crap.
plot(fittedmdl)
hold on
plot(x,y,'.')
hold off
So why did it fail? If you do not pass in intelligent starting values for the period of a periodic function like this, fit will use a random start point. And that start point is not chosen very intelligently. It cannot do that.
But now, let me try a fit, where the period is chosen intelligently. Let me see, over a span of x going from 0-5, I can count 8 peaks. That would suggest the period would be roughly 5/8/(2*pi). And the parameter b will be the inverse of the period. The other two parameters? The fit will be pretty insensitive to the start points I choose for them.
fittedmdl2 = fit(x,y,mdl,'start',[rand(),1/(5/8/(2*pi)),rand()])
fittedmdl2 =
General model: fittedmdl2(x) = a*sin(b*x+c) Coefficients (with 95% confidence bounds): a = -3.995 (-4.015, -3.976) b = 10 (9.996, 10) c = -0.1413 (-0.1522, -0.1305)
plot(fittedmdl2)
hold on
plot(x,y,'b.')
Note that the sign(a) was different from how I created it, but that is irrelevant, because the phase angle is also not unique.
Again, your failure results from a poor choice of starting values. Even though you provided a start point, you need to use a better start point for the period. This is a classical problem when fitting trig functions.

Products


Release

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by