Least Squares Method to fit parameters - matlab

I am asked to use the least squares method to fit the parameters α and β in y = α*exp(-β*x),
given the points:
x = [1 2 3 4 5 6 7]
y = [9 6 4 2 4 6 9]
I am having trouble determining what my matrix should look like. I know I should take the natural logarithm of both sides of the function in order to get rid of the exponential, and also obtain the natural logarithm of the y-values, which are:
ln_y = [2.19 1.79 1.39 0.69 1.39 1.79 2.19]
However what should my matrix look like, because what I am left with is
ln(y) = ln(α) - β*x?
So the -β column consists of ones and the x column will be my x values, but what should the α column contain?
This is what I assume I should get:
A = [1 1 1 1 1 1 1; 1 2 3 4 5 6 7]
Am I thinking correctly?

The first thing we can do is to take the natural logarithm ln (log in Matlab)) on both sides of the equation:
y = α * e^(-β * x)
becomes:
ln(y) = ln(α * e^(-β * x))
// Law of logarithms
ln(x * y) = ln(x) + ln(y)
// thus:
ln(y) = ln(α) + ln(e^(-β * x))
Simplifying:
ln(y) = -β * x + ln(α)
Now we have ln(y) as a linear function of x and the problem reduces to finding the linear regression in the least square sense. Let's define lny = log(y), and A = ln(α) and we can rewrite the problem as
lny = -β * x + A
Where
x = [1 2 3 4 5 6 7]
lny = [2.19 1.79 1.39 0.69 1.39 1.79 2.19]
For each x_i in x we can evaluate lny as follows (rewritten in ascending power of x):
lny(x1) = A - β * x1
lny(x2) = A - β * x2
...
lny(xn) = A - β * xn
In matrix form
LNY = X * [A β]'
Or,
X * [A β]' = LNY
// let Coefs = [A β]'
Coefs = X^-1 * LNY
In Matlab
x = [1 2 3 4 5 6 7];
y = [9 6 4 2 4 6 9];
lny = log(y);
X = [ones(length(y), 1), -x']; % design matrix
coefs = X\lny'
% A = coefs(1) and β = coefs(2)
% ln(α) = A thus α = exp(A)
alpha = exp(coefs(1));
beta = coefs(2)

You almost had it. The second row should be -x.
x = [1 2 3 4 5 6 7]
y = [9 6 4 2 4 6 9]
logy = log(y)
n = length(x);
A = [ones(1,n); -x]
c = logy/A; %Solve for coefficients
alpha = exp(c(1))
beta = c(2);

In this example, deriving the least squares estimator is a good idea. The other answers take this approach.
There is quick and dirty approach that is flexible and handy.
Just to it numerically. You can use fminsearch to get the job done.
% MATLAB R2019a
x = [1 2 3 4 5 6 7];
y = [9 6 4 2 4 6 9];
% Create anonymous function (your supposed model to fit)
fh =#(params) params(1).*exp(-params(2).*x);
% Create anonymous function for Least Squares Error with single input
SSEh =#(params) sum((fh(params)-y).^2); % Sum of Squared Error (SSE)
p0 = [1 0.5]; % Initial guess for [alpha, beta]
[p, SSE] = fminsearch(SSEh,p0);
alpha = p(1); % 5.7143
beta = p(2); % 1.2366e-08 (AKA zero)
It is always a good idea to plot the results as a sanity check (I screw up often and this saves me time and time again).
yhath=#(params,xval) params(1).*exp(-params(2).*xval);
Xrng = min(x)-1:.2:max(x)+1;
figure, hold on, box on
plot(Xrng,p(1).*exp(-p(2).*Xrng),'r--','DisplayName','Fit')
plot(x,y,'ks','DisplayName','Data')
legend('show')
A Note on Nonlinearity:
This works fine with linear models due to convexity. If your error function is nonlinear but convex, as Sum of Squared Error (SSE), then this also returns the global optimum.
Note that a non-convex function would require multiple start points to attempt to capture many local optima, then taking the best one would still carry no guarantees of optimality. Adding constraints to the solution would require penalty functions or switching to the constrained solver since fminsearch solves the unconstrained problem (unless you penalize it properly).
Easy to Modify:
It is easy to modify the model and the error function. For example, if you wanted to minimize the sum of the absolute error instead, it is straightforward using abs.
% Create anonymous function for Least Absolute Error with single input
SAEh =#(params) sum(abs(fh(params)-y)); % Sum of Absolute Error

Related

finding the difference between elements of a vector

I have a random vector in matlab of length 200 x1
x= rand(200,1)
I want to write a line of code that finds the difference between every 3 elements of x, that is
x(4)-x(1) and then x(5)-x(2) and then x(6)-x(3).
for i=1:length(x)-3
diff= x(i+3)- x(i)
end
Would this work? thank you
First, don't use diff as a variable name, since it is an in built function which you are shadowing.
You are close, but you are repeatedly overwriting the output, you need to store it in an array during your loop
delta = NaN(size(x)); % preallocate the output for better memory management
for ii = 1:length(x)-3
delta(ii) = x(ii+3) - x(ii) % assign to output array
end
You can do this more concisely in MATLAB using array operations, specifically
delta = x(4:end) - x(1:end-3);
Another way to do this is via convolution:
x = [4 7 8 9 6 8];
N = 3; % difference stride
y = conv(x, [1 zeros(1,N-1) -1], 'valid');
Example:
x =
4 7 8 9 6 8
y =
5 -1 0

MATLAB Curve Fitting with Slopes

Background
I am currently working on a lecture for my Engineering in MATLAB course and stumbled upon a problem I would like to present to the class. I have made many different attempts to solve this problem, but my graphs keep coming out incorrect. I will describe the problem below and all the steps I took to try to solve this problem.
Problem
Find the coefficients of the fourth-degree polynomial
P(x) = ax^4 + bx^3 + cx^2 + dx + e
whose graph goes through the points (0, 1), (1, 1), (-1,3), and
whose slopes at x = -1 is 20 and at x = 1 is 9.
Check your answer visually.
Attempt
I began by creating a matrix of the above x-values that I have derived as follows:
A = [0^4 0^3 0^2 0 1; 1^4 1^3 1^2 1 1; (-1)^4 (-1)^3 (-1)^2 -1 1];
A = [0 0 0 0 1; 1 1 1 1 1; 1 -1 1 -1 1];
This creates a 5 column by 3 row matrix that I may use to plot the polynomial.
My issue is that I am unable to get the last row of x-values, since each row is an equation in the system of equations and there must be as many equations as there are unknowns (4: a, b, c, and d are unknown, but e always equals 1 as you can see).
Ignoring this issue for a moment, I can continue to create a vertical matrix of y-values so that I may solve the system of equations. These y values are already given, so all I have to do is type this code in:
y = [1 1 3]';
Once again, there should be a fourth y-value to go along with the system of equations, but I have been unable to derive it using just the slopes of the points at x = -1 and x = 1.
Once both the x-values and the y-values are derived, we can proceed to using the backslash operator (/) to solve the system of linear equations A*x = y.
p = A\y;
mldivide is more info on the mldivide function for anyone who needs reference.
From here on out, the following code which creates a polynomial from this system of equations and graphs it, should stay the same.
u = -1:.01:1;
v = polyval(p, u);
plot(u,v);
In this code, u is the domain of x-values from -1 to 1 with a 0.01 interval. This is needed by us to use the polyval function, which creates a polynomial from a system of equations we derived at p on the interval u.
Lastly, plot simply graphs our derived polynomial using MATLAB's GUI on the interval u.
As you can see, the only missing pieces I have are one more row of x-values in my matrix A and one y-value in matrix y that I need to find the four unknowns a, b, c, and d. I believe you must use the two slopes given in the problem to find each point. I have tried using the polyder function to get the derivative of the matrix p by doing,
q = polyder(p);
but I am still confused as to how to continue from there. Any help will be greatly appreciated.
I would calculate the derivative of the polynomial:
dP(x) = 4ax^3 + 3bx^2 + 2cx + d
Now, you know that dP(-1)=20 and dP(1)=9 so you have 5 equations with 5 unknowns:
e = 1
a + b + c + d + e = 1
a - b + c - d + e = 3
-4*a + 3*b - 2*c + d = 20
4*a + 3*b + 2*c + d = 9
So you can construct a 5x5 matrix and solve the system, as you did with A\y.
The code to construct this 5x5 matrix is:
A = [0 0 0 0 1 ; 1 1 1 1 1 ; 1 -1 1 -1 1 ; -4 3 -2 1 0 ; 4 3 2 1 0];
y = [1 1 3 20 9]';
You can then check the results on a plot:
p=A\y;
u = -1:.01:1;
v = polyval(p, u);
data_pts = [0, 1; 1, 1;-1, 3]
plot(u,v,data_pts(:,1),data_pts(:,2),'rx')
which gives the following plot:
You can do the same with the derivative and checks it goes through the points (-1,20) and (1,9).

What is a quick way to compute the euclidean norm of two sets of vectors?

I know that MATLAB works better when most or everything is vectorized. I have two set of vectors X and T. For every vector x in X I want to compute:
this is because I want to compute:
which can be easily expressed as MATLAB linear algebra operations as I wrote above with a dot product. I am hoping that I can speed this up by having those vectors, instead of computing each f(x) with a for loop. Ideally I could have it all vectorized and compute:
I've been think about this for some time now, but it doesn't seem to be a a nice way were a function takes two vectors and computes the norm between each one of them, with out me having to explicitly write the for loop.
i.e. I've implemented the trivial code:
function [ f ] = f_start( x, c, t )
% Computes f^*(x) = sum_i c_i exp( - || x_i - t_i ||^2)
% Inputs:
% x = data point (D x 1)
% c = weights (K x 1)
% t = centers (D x K)
% Outputs:
% f = f^*(x) = sum_k c_k exp( - || x - t_k ||^2)
[~, K] = size(t);
f = 0;
for k=1:K
c_k = c(k);
t_k = t(:, k);
norm_squared = norm(x - t_k, 2)^2;
f = f + c_k * exp( -1 * norm_squared );
end
end
but I was hoping there was a less naive way to do this!
I think you want pdist2 (Statistics Toolbox):
X = [1 2 3;
4 5 6];
T = [1 2 3;
1 2 4;
7 8 9];
result = pdist2(X,T);
gives
result =
0 1.0000 10.3923
5.1962 4.6904 5.1962
Equivalently, if you don't have that toolbox, use bsxfun as follows:
result = squeeze(sqrt(sum(bsxfun(#minus, X, permute(T, [3 2 1])).^2, 2)));
Another method just for kicks
X = [1 2 3;
4 5 6].';
T = [1 2 3;
1 2 4;
7 8 9].';
tT = repmat(T,[1,size(X,2)]);
tX = reshape(repmat(X,[size(T,2),1]),size(tT));
res=reshape(sqrt(sum((tT-tX).^2)).',[size(T,2),size(X,2)]).'

Create Matrix using Matrix Indices

In matlab, how could you create a matrix M using its indices to populate the values? For example, say I want to create a 3x3 matrix M such that
M(i,j) = i+j --> [ 2 3 4; 3 4 5; 4 5 6]
I tried making vectors: x = 1:3', y = 1:3 and then
M = x(:) + y(:)
but it didn't work as expected.
Any thoughts on how this can be done?
Thanks!
UPDATE
The M I actually desire is:
M(i,j) = -2^(-i - j).
One way would be
x = 1:3;
z = ones(1,3);
N = z'*x + x'*z
M = -2 .^ -(z'*x + x'*z)
% Or simply
% M = -2 .^ -N
Output:
N =
2 3 4
3 4 5
4 5 6
M =
-0.250000 -0.125000 -0.062500
-0.125000 -0.062500 -0.031250
-0.062500 -0.031250 -0.015625
You should use bsxfun to find the sum:
M=bsxfun(#plus, (1:3).', 1:3)
and for the second formula:
M=-2.^(-bsxfun(#plus, (1:3).', 1:3))
bsxfun(#(x,y)(-2.^(-x-y)), (1:3).', 1:3)
This uses the Answer of Mohsen Nosratinia with the function you wished.

Obtain matrix of indices in octave / matlab

Given some multidimensional matrix A in Octave / Matlab,
What's the easiest way to get a matrix of the same size as A where all elements are replaced by their index along the k'th dimension
ie for the matrix
A =
ans(:,:,1) =
0.095287 0.191905
0.226278 0.749100
ans(:,:,2) =
0.076826 0.131639
0.862747 0.699016
I want a function f such that
f(A,1) =
ans(:,:,1) =
1 1
2 2
ans(:,:,2) =
1 1
2 2
f(A,2) =
ans(:,:,1) =
1 2
1 2
ans(:,:,2) =
1 2
1 2
and
f(A, 3) =
ans(:,:,1) =
1 1
1 1
ans(:,:,2) =
2 2
2 2
Also, given a sparse matrix B
What's the easiest way to get another sparse matrix of the same size where the nonzero elements are replaced by their index along the k'th dimension? (so same problem as above, but for only the nonzero elements)
Ideally I'm looking for a way which is well-vectorized for octave (meaning it doesn't explicitly loop over anything)
CLARIFICATION: For the sparse matrix one, I'm looking for a solution which does not involve creating a full size(B) matrix at any point
ndgrid() does what you want, although not in the format you are looking for. If you know the dims of the input A beforehand, you can use the following line to create the N-dimentional mesh grid:
% for matrix a where ndims(a) == 3
[x, y, z] = ndgrid (1:size(a,1), 1:size(a,2), 1:size(a,3));
% x is like f(a, 1)
% y is like f(a, 2)
% z is like f(a, 3)
You may be able to write a custom wrapper around ndgrid() to convert it to the function format you are looking for.
In case anyone's curious, since I didn't know about ndgrid, here's the answer I came up with:
function [y] = indices(a,k)
s = size(a);
n = s(k);
D = length(s);
x = permute(a,[k,1:(k-1),(k+1):D]);
y = reshape(x,n,[]);
y = diag(1:n) * ones(size(y));
y = reshape(y,size(x));
y = permute(y,[(2:k),1,(k+1):D]);
endfunction
function [y] = spindices(a,k)
s = size(a);
n = s(k);
D = length(s);
x = permute(a,[k,1:(k-1),(k+1):D]);
y = reshape(x,n,[]);
y = spdiag(1:n) * spones(y);
y = reshape(y,size(x));
y = permute(y,[(2:k),1,(k+1):D]);
endfunction