I would like to fit a curve on the form y=a+b*sin(2*pi*x)+c*cos(2*pi*x) to some data points in Matlab. I've been trying to use 'fit' but then I only get this message 'if isa( fittypeobj, 'fittype' )'
This is my code:
L = load('file.mat');
x = filedata(:,1);
ft = fittype('a+b*sin(2*pi*x)+c*cos(2*pi*x)');
fit(x, filedata(:,3), ft)
Can somebody please tell me what I'm doing wrong?
Here is how to do the fit 'by hand' in a least-squares way:
x = x(:); %make sure we have column vectors
y = y(:);
f0 = 1;
M = [ones(size(x)), sin(2*pi*f0*x), cos(2*pi*f0*x)];
%design matrix, columns are base vectors
% least square approximation of x = k(1)*M(:,1) + k(2)*M(:,2) + k(3)*M(:,3);
% see help mldivide
k = M \ y;
a = k(1);
b = k(2);
c = k(3);
Quick test to see if it works:
>> x = linspace(0,10,1000)'; % note transpose to make column
>> y = 3 + 1.5 * sin(2*pi*x) + 8 * cos(2*pi*x) + randn(size(x)); % add some noise
>> f0 = 1;
>> M = [ones(size(x)), sin(2*pi*f0*x), cos(2*pi*f0*x)];
>> k = M \ y
k =
3.0383
1.5264
7.9385
>> plot(x, y, x, M*k, 'r'); legend('measurement', 'fit')
Related
In MATLAB, I am trying to write a program that will take 3 coordinates on a graph, (x,y), use those values to solve a system of equations that will find the coefficients of a polynomial equation, y = ax^2 + bx + c, which I can then use to plot a parabola.
To test my code, I figured I could start with a polynomial, graph it, find the minimum location of the parabola, use its immediate neighbors for my other 2 locations, then run those 3 locations through my code which should spit out the coefficients of my original polynomial. But for some reason, my resulting parabola is right shifted and my values for b and c are incorrect.
Does anyone see where my issue is? I am out of ideas
clear all; close all;
x = -10:10;
%Original Polynomial
y = 6.*x.^2 + 11.*x -35;
% Find 3 Locations
[max_n, max_i] = min(y)
max_il = max_i - 1 % left neighbor of max_ni
max_nl = y(max_il) % value at max_il
max_ir = max_i + 1 % left neighbor of max_ni
max_nr = y(max_ir) % value at max_ir
% Solve for coefficients
syms a b c
equ = (a)*(max_i)^2 + (b)*(max_i) + (c) == (max_n);
equ_l = (a)*(max_il)^2 + (b)*(max_il) + (c) == (max_nl);
equ_r = (a)*(max_ir)^2 + (b)*(max_ir) + (c) == (max_nr);
sol = solve([equ, equ_l, equ_r],[a, b, c]);
Sola = sol.a
Solb = sol.b
Solc = sol.c
% New Polynomial
p = (sol.a).*(x).^2 + (sol.b).*(x) +(sol.c);
%Plot
plot(x,y); grid on; hold on;
plot(x, p);
axis([-10 10 -41 40])
[max_np, max_ip] = min(p)
legend('OG', 'New')
You are confusing the index into your array y, and the corresponding x coordinate.
x = -10:10;
y = 6.*x.^2 + 11.*x -35;
[max_n, max_i] = min(y)
Here. max_i is the index into the y array, the corresponding x coordinate would be x(max_i).
I suggest you find three data points to fit your curve to as follows:
[~, max_i] = min(y);
pts_x = x(max_i + (-1:1));
pts_y = y(max_i + (-1:1));
then use pts_x(i) and pts_y(i) as your x and y values:
syms a b c
equ = a * pts_x.^2 + b * pts_x + c == pts_y;
sol = solve(equ, [a, b, c]);
I am trying to test streamline with a very simple 3D vector field. I fill in a mesh using 3 for loops (not the best, but this is reminescent of a different expression for "v" which I couldn't easily vectorise). Then I define the vector field v as r. Simple radial field. My full code is below. quiver3 is fine, but unfortunately streamline gives me the following error:
Error using griddedInterpolant
Interpolation requires at least two
sample points in each dimension.
N = 5;
L = 2;
dl = 2*L/N;
for i = 1:N+1
for j = 1:N+1
for k = 1:N+1
x = -L + (i-1)*dl;
y = -L + (j-1)*dl;
z = -L + (k-1)*dl;
X(i,j,k) = x;
Y(i,j,k) = y;
Z(i,j,k) = z;
r = [x,y,z];
v = r-r0;
Vx(i,j,k) = v(1);
Vy(i,j,k) = v(2);
Vz(i,j,k) = v(3);
end
end
end
quiver3(X,Y,Z,Vx,Vy,Vz);
[sx,sy,sz] = meshgrid(0:0.1:1,0:1:5,0:1:5);
streamline(X,Y,Z,Vx,Vy,Vz,sx,sy,sz);
hold on;
streamslice(X,Y,Z,Vx,Vy,Vz,[],[],5);
pbaspect([1,1,1])
It returns back to gridded X, Y variables, if you use transposed version of X and Y, you will not get this interpolation error in the streamline function. To transpose a N-D array in MATLAB, use permute function, like:
X = permute(X, [2,1,3]); % to rearrange X and Y dimension
or just define correct form of X and Y at the first place [in the loop]
Could you please help me with the following question:
I want to solve a second order equation with two unknowns and use the results to plot an ellipse.
Here is my function:
fun = #(x) [x(1) x(2)]*V*[x(1) x(2)]'-c
V is 2x2 symmetric matrix, c is a positive constant and there are two unknowns, x1 and x2.
If I solve the equation using fsolve, I notice that the solution is very sensitive to the initial values
fsolve(fun, [1 1])
Is it possible to get the solution to this equation without providing an exact starting value, but rather a range? For example, I would like to see the possible combinations for x1, x2 \in (-4,4)
Using ezplot I obtain the desired graphical output, but not the solution of the equation.
fh= #(x1,x2) [x1 x2]*V*[x1 x2]'-c;
ezplot(fh)
axis equal
Is there a way to have both?
Thanks a lot!
you can take the XData and YData from ezplot:
c = rand;
V = rand(2);
V = V + V';
fh= #(x1,x2) [x1 x2]*V*[x1 x2]'-c;
h = ezplot(fh,[-4,4,-4,4]); % plot in range
axis equal
fun = #(x) [x(1) x(2)]*V*[x(1) x(2)]'-c;
X = fsolve(fun, [1 1]); % specific solution
hold on;
plot(x(1),x(2),'or');
% possible solutions in range
x1 = h.XData;
x2 = h.YData;
or you can use vector input to fsolve:
c = rand;
V = rand(2);
V = V + V';
x1 = linspace(-4,4,100)';
fun2 = #(x2) sum(([x1 x2]*V).*[x1 x2],2)-c;
x2 = fsolve(fun2, ones(size(x1)));
% remove invalid values
tol = 1e-2;
x2(abs(fun2(x2)) > tol) = nan;
plot(x1,x2,'.b')
However, the easiest and most straight forward approach is to rearrange the ellipse matrix form in a quadratic equation form:
k = rand;
V = rand(2);
V = V + V';
a = V(1,1);
b = V(1,2);
c = V(2,2);
% rearange terms in the form of quadratic equation:
% a*x1^2 + (2*b*x2)*x1 + (c*x2^2) = k;
% a*x1^2 + (2*b*x2)*x1 + (c*x2^2 - k) = 0;
x2 = linspace(-4,4,1000);
A = a;
B = (2*b*x2);
C = (c*x2.^2 - k);
% solve regular quadratic equation
dicriminant = B.^2 - 4*A.*C;
x1_1 = (-B - sqrt(dicriminant))./(2*A);
x1_2 = (-B + sqrt(dicriminant))./(2*A);
x1_1(dicriminant < 0) = nan;
x1_2(dicriminant < 0) = nan;
% plot
plot(x1_1,x2,'.b')
hold on
plot(x1_2,x2,'.g')
hold off
I tried to fit a line using 2D points. By using the SVD, I've successfully fit lines. However, in the one case, I failed to fit lines. Please, give me some advise and let me know the reasons.
% Data
X = [487,520.40,553.81,587.22,620.63,654.04,500,533.01,566.02,599.03,642,674.61,707.22,517,552.35,587.71,647,682.35,717.71,522,555.97,589.94,623.91,657.88];
Y = [521,558.20,595.40,632.60,669.80,707.00,533,570.55,608.10,645.66,695,732.90,770.80,564,599.35,634.71,694,729.35,764.71,562,598.68,635.37,672.06,708.75];
% b1*x + b2*y +b3 = 0
M = [X', Y', ones(size(X'))];
[~, ~, V] = svd(M);
B = V(:, 3);
b1 = B(1);
b2 = B(2);
b3 = B(3);
% Draw points
figure(111)
hold on;
plot(X, Y, 'ro');
% Draw line
minX = min(X);
maxX = max(X);
valY_minX = -(b3 + b1*minX) / b2;
valY_maxX = -(b3 + b1*maxX) / b2;
Pt1 = [minX; valY_minX];
Pt2 = [maxX; valY_maxX];
plot([Pt1(1), Pt2(1)], [Pt1(2), Pt2(2)]);
I've got succeed fit the line when I changed to 'V(:, 2)'. Why the second largest eigen value brings the best solution??
Here is the code:
fs = 22050;
x = rand(fs,1);
x = x - mean(x); % get rid of DC offeset
% set comb-filter coefficients
f = 220; % fundamental
L = round(fs/f); % delay length
coef = 0.99; % IIR coefficient
% build delay vector and filter
b = [1 zeros(1,L-1) coef];
y = filter(1, b, x);
% create amplitude envelope for output
decay = 8;
expEnv = exp ((0:(length(y)-1))/length(y));
expEnv = (1./expEnv).^ decay;
fprintf('%d\n',length(expEnv));
fprintf('%d\n', length(y));
% envelope output signal
z = y .* expEnv;
sound(z , fs); % play sound
It refuses to execute the z = y.*expEnv line. The printf lines shows that both y and expEnv have the same length (22050)
Yes...the dangers of the length command.
although they have the same number of elements, they do not have the same shape:
>> size(y)
ans =
22050 1
>> size(expEnv)
ans =
1 22050
This will fix that:
z = y .* expEnv.';