I have been trying to fit a parabola to parts of data where y is positive. I am told, that P1(x1,y1) is the first data point, Pg(xg,yg) is the last, and that the top point is at x=(x1+xg)/2. I have written the following:
x=data(:,1);
y=data(:,2);
filter=y>0;
xp=x(filter);
yp=y(filter);
P1=[xp(1) yp(1)];
Pg=[xp(end) yp(end)];
xT=(xp(1)+xp(end))/2;
A=[1 xp(1) power(xp(1),2) %as the equation is y = a0 + x*a1 + x^2 * a2
1 xp(end) power(xp(end),2)
0 1 2*xT]; % as the top point satisfies dy/dx = a1 + 2*x*a2 = 0
b=[yg(1) yg(end) 0]'; %'
p=A\b;
x_fit=[xp(1):0.1:xp(end)];
y_fit=power(x_fit,2).*p(3)+x_fit.*p(2)+p(1);
figure
plot(xp,yp,'*')
hold on
plot(x_fit,y_fit,'r')
And then I get this parabola which is completely wrong. It doesn't fit the data at all! Can someone please tell me what's wrong with the code?
My parabola
Well, I think the primary problem is some mistake in your calculation. I think you should use three points on the parabola to obtain a system of linear equations. There is no need to calculate the derivative of your function as you do with
dy/dx = a1 + 2*x*a2 = 0
Instead of a point on the derivative you choose another point in your scatter plot, e.g. the maximum: PT = [xp_max yp_max]; and use it for your matrix A and b.
The equation dy/dx = a1 + 2*x*a2 = 0 does not fulfill the basic scheme of your system of linear equations: a0 + a1*x + a2*x^2 = y;
By the way: If you don't have to calculate your parabola necessarily in this way, you can maybe have a look at the Matlab/Octave-function polyfit() which calculates the least squares solution for your problem. This would result in a simple implementation:
p = polyfit(x, y, 2);
y2 = polyval(p, x);
figure(); plot(x, y, '*'); hold on;
plot(x, y2, 'or');
Related
I have 2 vectors x and y to which I want to fit a polynomial as y = f(x) in MATLAB.
I could have used polyfit. However, I want to fit only selective power terms of the polynomial. For example, y = f(x) = a*x^3 + b*x + c. Notice that I don't have the x^2 term in there. Is there any built-in function in MATLAB to achieve this?
I am not sure if simply ignoring the coefficient that MATLAB gives for x^2 is same as fitting the polynomial without x^2 term.
If you don't have the curve fitting tool box (see #thewaywewalk's comment), or anyway, it is easy to use mldivide:
x=rand(10,1); % your x data
y=5*x.^3+2*x+7+rand(10,1)*.01; % some y data with noise
[x.^3 x ones(size(x))]\y % least squares solve to y = a*x^3 + b*x + c
gives
ans =
4.9799
2.0211
6.9980
Note that "simply ignoring the coefficient that MATLAB gives for x^2" is definitely not the "same as fitting the polynomial without x^2 term".
It sounds like you have the fitting toolbox and want to just remove a possible coefficient. If that's the case, here's a way to do it.
%What is the degree of the polynomial (cubic)
polyDegree = 3;
%What powers do you want to skip (x^2 and x)
skipPowers = [2, 1];
%This sets up the options
opts = fitoptions( 'Method', 'LinearLeastSquares' );
%All coefficients of degrees not specified between x^n and x^0 can have any value
opts.Lower = -inf(1, polyDegree + 1);
opts.Upper = inf(1, polyDegree + 1);
%The coefficients you want to skip have a range from 0 to 0.
opts.Lower(polyDegree + 1 - skipPowers) = 0;
opts.Upper(polyDegree + 1 - skipPowers) = 0;
%Do the fit using the specified polynomial degree.
[fitresult, gof] = fit( x, y, ['poly', num2str(polyDegree)] , opts );
In recent Matlab versions you can do it easily with a GUI by selecting the APPS tab and then the Curve Fitting Tool.
For example, I define:
x = 1:10;
y = x + randn(1,10);
I then select those variables as X data, Y data in the tool and I choose a custom equation defined as a*x^3 + b*x + c. The results are:
I need to fit data e.g. x, y, CI (where CI is confidence index of y) in Matlab.
Now, I use this code:
pf = polyfit(x, y, 2);
x1 = min(x):.1:max(x);
y1 = polyval(pf, x1);
figure
hold on
errorbar(x, y, CI, 'ko');
plot(x1, y1, 'k');
hold off
Of course, the fit comes out of some errors bars, and it's correct.
I would like obtain a fit curve closer to the points with a low confidence index, and discard the points with a high confidence index.
Thank you and bye,
Giacomo
What you are looking for are Weighted Least Squares. You can compute them with the function lscov. There is a nice example in its help page, but I'll try to make it clearer.
Let us construct a simple parabola, with a corrupted point
x = (0:0.1:1)';
y = 0.5*x.^2;
y(5) = 3*y(5);
and give some weights
w = ones(size(y));
w(5) = 0.1;
Next build the Vandermonde matrix (see here for the code) and solve the system
%// V = [x.^2 x ones(size(x))];
V = bsxfun(#power, x, 2:-1:0);
coeff = lscov(V, y, w);
The estimated coefficients, with and without the weights, are
x^2 x 1
with weights [0.4797 0.0186 -0.0004]
no weights [0.3322 0.1533 -0.0034]
Note that in your case w will have to be inverted.
If you don't like to build the Vandermonde matrix, and you have a license for the Curve Fitting Toolbox, you can use the following code
ft = fittype('poly2');
opts = fitoptions('Method', 'LinearLeastSquares');
opts.Weights = w;
fitresult = fit(x, y, ft, opts);
and you'll obtain the same result.
%Clear memory
clear;
%Number of points
N = 10000;
%Preallocate memory
x = zeros(1,N);
y1 = zeros(1,N);
x = -5 + (5+5)*rand(1,N);
y1^2 = x^2 + 1;
plot(x,y1), grid on;
I get the following error:
The expression to the left of the equals sign is not a valid target for an assignment.
How can I plot this first on x,y axis, then on x^2 and y^2?
Thanks in advance.
Your function is "multi-valued". If you solve for y1, you'll have a solution in terms of +/- a square root. Therefore, you'll have to break your function into two and plot each branch. Some may also suggest using ezplot, which is convenient, but not as flexible:
ezplot('y1^2 = x^2 + 1')
The other answer gives the solution using ezplot which is an acceptable way of doing it. I want to throw in mine which aims to correct yours:
%Clear memory
clear;
%Number of points
N = 10000;
x = -5:0.01:5; %This creates a vector from -5 to 5 with an interval of 0.01.
y1 = sqrt(x.^2 + 1); %Since you want to solve the equation y^2=x^2+1, one of the solution is the positive root of x^2+1 and the other is the negative.
plot(x,y1); %Plots positive root
hold on;
plot(x,-y1), grid on; %Plots negative root
I have been trying to find a parabola in an image. For the starting purposes I took an image with a black parabola on a white background. Then I found the black pixels on the image using the find command by
[yi xi] = find(im<10); % im is the image with black parabola and white background
After that I randomly took 3 points from the collection and solved the equation for the parabola using the symbolic toolbox using
syms x y;
%solve them for the parabola equation
A = [ x^2 x y 1 ;x0^2 x0 y0 1; x1^2 x1 y1 1; x2^2 x2 y2 1];
where
%(x0,y0) = (104,137)
%(x1,y1) = (244,161)
%(x2,y2) = (300,229)
S = solve(det(A),y);
Then I get the coeffcients a,b,c as
a = 0.0100
b = -1.6800
c = 254.1900
where a, b and c are
a*x^2 + b*x + c = y;
Since now I have got the eqn I plot the parabola by putting the coefficient values
and taking
xx = 1:300;
yy = a*xx.^2 + b*xx +c ;
then I plot the parabola on the image as
plot(xx,yy,'-');
For the confirmation that I have taken correct points I also plot the selected points on the image and they lie exactly on the parabola in the image. So that is not the problem.
The problems are :
The parabola that I plot (blue) doesn't lie on the parabola of the image(black).
When I put the value of x co-ordinates in the above equation. The value of y is not the same as of the y co-ordinate.
for eg: (104,137)
0.0100*104*104 -1.68*104 + 254.19 = 108.16 - 174.72 + 254.19 = 187.63
whereas it should be 137
My parabola is wrong. Any help will be appreciated. The images are
I think you must be rounding somewhere in your calculation of a, b, and c.
I had a go with the three points you mentioned using the function fit (with fit type poly2) and my a,b,c were 0.0053, -1.6802 and 254.1895 (or add more decimal places when using format long).
Similarly, when using same the code you give, the output of solve is:
S = (73*x^2)/13720 - (5763*x)/3430 + 87187/343
S2 = double(coeffs(S))
S2 =
254.1895 -1.6802 0.0053
This gives me the same a, b, and c as with fit/poly2 (just from looking at it, the output of 73/13720 cannot be 0.01). Also, if I plot this curve over the original points using the same code you give, it works fine. So the only remaining source of error that I can see is some sort of rounding in whatever code you use to extract values of a, b, and c from the output of solve.
The following works. The habit of imshow to swap the intuitive meaning of ordinate and abscissa as used in plot can make overlays problematic at times. Your problem might stem from how you define your coordinate system when plotting with different function (imshow or related image display routines versus plot) in particular the position of the origin in the image. Here's my code:
% create image of parabola
k =[-0.3 10 10];
x = [1:0.1:50];
y = round(k(1)*x.^2 + k(2)*x + k(3));
crd = unique(round([x(:) y(:)]),'rows');
Nx = 100;
Ny = 100;
img = ones(Nx,Ny)*255;
iok = find((crd(:,1)>0) & (crd(:,1)<=Nx) & (crd(:,2)>0) & (crd(:,2)<=Ny));
indx = sub2ind([Nx Ny],crd(iok,1),crd(iok,2));
img(indx) = 0;
imshow(img)
Next we fit the parabola
% pick three points:
x0 = crd(1,1);
y0 = crd(1,2);
x1 = crd(80,1);
y1 = crd(80,2);
x2 = crd(160,1);
y2 = crd(160,2);
% plot these on the original image
hold on
plot(y0,x0,y1,x1,y2,x2,'Markersize',20,'Linestyle','none','Marker','x','Color','r')
Solved the equation precisely as you showed, then the result is
S = -1094/3627*x^2+12073/1209*x+37415/3627
Overlay the fitted equation
a= -1094/3627;
b = 12073/1209;
c = 37415/3627;
xx = 1:100;
yy = a*xx.^2 + b*xx +c ;
% then I plot the parabola on the image as
plot(yy,xx,'g--');
A not so pretty plot (green= fit, red crosses=picked points, blue=parabola plotted without swapping order of x and y):
Does anyone know how to obtain a mean curve having a matrix with the correspondent x,y points from the original plot? I mean, I pretend a medium single curve.
Any code or just ideas would be very very helpful for me since I am new with matlab.
Thank you very much!
Well, one thing you can do is fit a parametric curve. Here's an example on how to do this for a figure-8 with noise on it:
function findParamFit
clc, clf, hold on
%# some sample data
noise = #(t) 0.25*rand(size(t))-0.125;
x = #(t) cos(t) + noise(t);
y = #(t) sin(2*t) + noise(t);
t = linspace(-100*rand, +100*rand, 1e4);
%# initial data
plot(x(t), y(t), 'b.')
%# find fits
options = optimset(...
'tolfun', 1e-12,...
'tolx', 1e-12);
a = lsqcurvefit(#myFun_x, [1 1], t, x(t), -10,10, options);
b = lsqcurvefit(#myFun_y, [1 2], t, y(t), -10,10, options);
%# fitted curve
xx = myFun_x(a,t);
yy = myFun_y(b,t);
plot(xx, yy, 'r.')
end
function F = myFun_x(a, tt)
F = a(1)*cos(a(2)*tt);
end
function F = myFun_y(b, tt)
F = b(1)*sin(b(2)*tt);
end
Note that this is a particularly bad way to fit parametric curves, as is apparent here by the extreme sensitivity of the solution to the quality of the initial values to lsqcurvefit. Nevertheless, fitting a parametric curve will be the way to go.
There's your google query :)