How to linearize this data for a specific relationship in MATLAB? - matlab

I have data that y and x don't have a linear trend. The data as follows and if you plot y as a function of x, the plot is nonlinear.
x= [45.5976, 45.6311, 45.6599, 45.679, 45.703, 45.7461, 45.7749]
y = [0.17, 1.7, 5.1, 17, 51, 170, 510]
plot(x,y,'o')
My goal is to find an optimum value of b to make log(y) behavior with respect to log((x-b)/b) a linear relation. In other words, plot(log((x-b)/b),log(y) should produce a linear function.

Since I don't have enough reputation to add a comment to clarify the question, I'm trying to help in an answer. Also, typically when transforming data to fit a linear regression, if your original model is: y = b0 + b1x, then taking logs of both the predictor and response gives a new model y* = b0 + b1x* where y* = ln(y) and x* = ln(x). Why did you decide your model should be of the form: ln(y) = ln((x-b)/b)?
In any case, to find the optimal beta values for such a model in Matlab you would do something like the following:
x= [45.5976, 45.6311, 45.6599, 45.679, 45.703, 45.7461, 45.7749]';
y = [0.17, 1.7, 5.1, 17, 51, 170, 510]';
figure(1), plot(x,y,'o');
ln_y = log(y);
ln_x = log(x);
figure(2), plot(ln_x, ln_y, 'x');
ln_X = [ones(length(ln_x),1) ln_x];
B = ln_X\ln_y;
ln_y_fitted = ln_X*B;
figure(2),
hold on
plot(ln_x, ln_y_fitted, '--', 'Color', 'r');
Given the above code, if you want to plot the various results for log(y) = log((x-b)/b), you can use something like this:
for b = 0.1:0.1:4
ln_x = log((x-b)/b);
figure, plot(ln_x, ln_y, 'x');
end

Related

Easy logaritmic curve fitting

I am trying to model some measures (lux, ohm) that behave as a logarithmic function.
In order to do it, I've tried to model it with MATLAB by projecting the real values using natural logarithms, then use polyfit to get a linear expression. Then, I want to isolate the variable lux.
What I have so far is:
% Original values from spreadsheet
lux = [1, 5, 10, 50, 100]';
ohm = [100, 35, 25, 8, 6]';
% Logaritmic fitting
x_ = log(lux);
y_ = log(ohm);
p = polyfit(x_,y_,1)
x1 = linspace(x_(1), x_(end), 1000);
y1 = polyval(p,x1);
plot(x_,y_,'ob');
hold on;
plot(x1,y1, 'r');
hold on;
% Get expression
% y = -0.6212x + 4.5944
% log(ohm) = -0.6212 * log(lux) + 4.5944
lfun = #(ohm, a, b) ((ohm/exp(b)).^(1/a));
a = p(1);
b = p(2);
t = linspace(1,100,100);
plot(log(t), log(lfun(t, a, b)), '--g');
hold off;
legend({'points','poly','eq'});
Since I got p = -0.6212 4.5944, I assume that the equation is log(ohm) = -0.6212 * log(lux) + 4.5944. Isolating the variable lux results in:
lux = (ohm / exp(4.5944) ).^(-1/0.6212)
However, as can be seen in the green line, it is not working!
What am I doing wrong?
You're doing everything right except for the plotting, in the first plot you defined the x-axis to be log(lux) and the y-axis to be log(ohm), but to adhere to that in the second case you need to flip the arguments:
plot(log(lfun(t, a, b)), log(t), '--g')
t refers to 'ohm' and must therefore be displayed on the y-axis to coincide with the first plot.

how to center and scale data using ployfit

I have a set of points in which i want to fit a line through. In most cases i end up getting Inf or -Inf especially when the lines are either vertical or horizontal. I have seen Matlab's description of centering and scaling, but i do not seem to understand how apply this to my data. Below is an example code, but please note however that it isn't exactly the one with the issue. I have used this because the main code will just be too long to follow.
x = [0, 1.81, 3.64, 5.45, 7.27];
y = [1, -0.82, -2.64, -4.45, -6.27];
fitline = polyfit([y(1), y(2), y(3), y(4)], [x(1), x(2), x(3), x(4)], 1);
%plot the data
k = linspace(0, 10, 5);
fk = (fitline(1)*k) + fitline(2);
figure, plot(k, fk, 'Color', 'r', 'linewidth', 1);
Looking forward to any help/suggestions/advice. Thanks!
MATLAB's function set polyfit and polyval will handle the centering (calculation of the mean) and scaling (calculation of the standard deviation) for you. Use the third output of polyfit to get the parameters:
x = [0, 1.81, 3.64, 5.45, 7.27];
y = [1, -0.82, -2.64, -4.45, -6.27];
[fitline,~,mu] = polyfit(y(1:4),x(1:4), 1);
And pass them to polyval:
k = linspace(0, 10, 5);
fk = y = polyval(fitline,k,[],mu);

How to fit an elliptic cone to a set of data?

I have a set of 3d data (300 points) that create a surface which looks like two cones or ellipsoids connected to each other. I want a way to find the equation of a best fit ellipsoid or cone to this dataset. The regression method is not important, the easier it is the better. I basically need a way, a code or a matlab function to calculate the constants of the elliptic equation for these data.
You can also try with fminsearch, but to avoid falling on local minima you will need a good starting point given the amount of coefficients (try to eliminate some of them).
Here is an example with a 2D ellipse:
% implicit equation
fxyc = #(x, y, c_) c_(1)*x.^2 + c_(2).*y.^2 + c_(3)*x.*y + c_(4)*x + c_(5).*y - 1; % free term locked to -1
% solution (ellipse)
c_ = [1, 2, 1, 0, 0]; % x^2, y^2, x*y, x, y (free term is locked to -1)
[X,Y] = meshgrid(-2:0.01:2);
figure(1);
fxy = #(x, y) fxyc(x, y, c_);
c = contour(X, Y, fxy(X, Y), [0, 0], 'b');
axis equal;
grid on;
xlabel('x');
ylabel('y');
title('solution');
% we sample the solution to have some data to fit
N = 100; % samples
sample = unique(2 + floor((length(c) - 2)*rand(1, N)));
x = c(1, sample).';
y = c(2, sample).';
x = x + 5e-2*rand(size(x)); % add some noise
y = y + 5e-2*rand(size(y));
fc = #(c_) fxyc(x, y, c_); % function in terms of the coefficients
e = #(c) fc(c).' * fc(c); % squared error function
% we start with a circle
c0 = [1, 1, 0, 0, 0];
copt = fminsearch(e, c0)
figure(2);
plot(x, y, 'rx');
hold on
fxy = #(x, y) fxyc(x, y, copt);
contour(X, Y, fxy(X, Y), [0, 0], 'b');
hold off;
axis equal;
grid on;
legend('data', 'fit');
xlabel('x'); %# Add an x label
ylabel('y');
title('fitted solution');
The matlab function fit can take arbitrary fit expressions. It takes a bit of figuring out the parameters but it can be done.
You would first create a fittype object that has a string representing your expected form. You'll need to work out the expression yourself that best fits what you're expecting, I'm going to take a cone expression from the Mathworld site for an example and rearrange it for z
ft = fittype('sqrt((x^2 + y^2)/c^2) + z_0', ...
'independent', {'x', 'y'}, 'coeff', {'c', 'z_0'});
If it's a simple form matlab can work out which are the variables and which the coefficients but with something more complex like this you'd want to give it a hand.
The 'fitoptions' object holds the configuration for the methods: depending on your dataset you might have to spend some time specifying upper and lower bounds, starting values etc.
fo = fitoptions('Upper', [one, for, each, of, your, coeffs, in, the, order, they, appear, in, the, string], ...
'Lower', [...], `StartPoint', [...]);
then get the output
[fitted, gof] = fit([xvals, yvals], zvals, ft, fo);
Caveat: I've done this plenty with 2D datasets and the docs state it works for three but I haven't done that myself so the above code might not work, check the docs to make sure you've got your syntax right.
It might be worth starting with a simple fit expression, something linear, so that you can get your code working. Then swap the expression out for the cone and play around until you get something that looks like what you're expecting.
After you've got your fit a good trick is that you can use the eval function on the string expression you used in your fit to evaluate the contents of the string as if it was a matlab expression. This means you need to have workspace variables with the same names as the variables and coefficients in your string expression.

Matlab find the best constants for a fitting model

Please find the data in the link below, or if you can send me your private email, I can send you the data
https://dl.dropboxusercontent.com/u/5353938/test_matlab_lefou.xlsx
In the excel sheet, the first column is y, the second is x and the third is t, I hope this will make things much more clear, and many thanks for the help.
I need to use the following model because it is the one that fits best my data, but what I don't know is how to find the best values of a and b, that will allow me to get the best fit, (I can attach a file if you need the values), I already have the values of y, x and t:
y= a*sqrt(x).exp(b.t)
Thanks
Without the dependency on the curve fitting toolbox, this problem can also be solved by using fminsearch. I first generate some data, which you already have but didn't share with us. An initial guess on the parameters a and b must be made (p0). Then I do the optimiziation by minizmizing the squared errors between data and fit resulting in the vector p_fit, which contains the optimized parameters for a and b. In the end, the result is visualized.
% ----- Generating some data for x, y and t (which you already got)
N = 10; % num of data points
x = linspace(0,5,N);
t = linspace(0,10,N);
% random parameters
a = rand()*5; % a between 0 and 5
b = (rand()-1); % b between -1 and 0
y = a*sqrt(x).*exp(b*t) + rand(size(x))*0.1; % noisy data
% ----- YOU START HERE WITH YOUR PROBLEM -----
% put x and t into a 2 row matrix for simplicity
D(1,:) = x;
D(2,:) = t;
% create model function with parameters p(1) = a and p(2) = b
model = #(p, D) p(1)*sqrt(D(1,:)).*exp(p(2)*D(2,:));
e = #(p) sum((y - model(p,D)).^2); % minimize squared errors
p0 = [1,-1]; % an initial guess (positive a and probably negative b for a decay)
[p_fit, r1] = fminsearch(e, p0); % Optimize
% ----- VISUALIZATION ----
figure
plot(x,y,'ko')
hold on
X = linspace(min(x), max(x), 100);
T = linspace(min(t), max(t), 100);
plot(X, model(p_fit, [X; T]), 'r--')
legend('data', sprintf('fit: y(t,x) = %.2f*sqrt(x)*exp(%.2f*t)', p_fit))
The result can look like
UPDATE AFTER MANY MANY COMMENTS
Your data are column vectors, my solution used row vectors. The error occured when the errorfunction tryed to compute the difference of a column vector (y) and a row-vector (result of the model-function). Easy hack: make them all to row vectors and use my approach. The result is: a = 0.5296 and b = 0.0013.
However, the Optimization depends on the initial guess p0, you might want to play around with it a little bit.
clear variables
load matlab.mat
% put x and t into a 2 row matrix for simplicity
D(1,:) = x;
D(2,:) = t;
y = reshape(y, 1, length(y)); % <-- also y is a row vector, now
% create model function with parameters p(1) = a and p(2) = b
model = #(p, D) p(1)*sqrt(D(1,:)).*exp(p(2)*D(2,:));
e = #(p) sum((y - model(p,D)).^2); % minimize squared errors
p0 = [1,0]; % an initial guess (positive a and probably negative b for a decay)
[p_fit, r1] = fminsearch(e, p0); % Optimize
% p_fit = nlinfit(D, y, model, p0) % as a working alternative with dependency on the statistics toolbox
% ----- VISUALIZATION ----
figure
plot(x,y,'ko', 'markerfacecolor', 'black', 'markersize',5)
hold on
X = linspace(min(x), max(x), 100);
T = linspace(min(t), max(t), 100);
plot(X, model(p_fit, [X; T]), 'r-', 'linewidth', 2)
legend('data', sprintf('fit: y(t,x) = %.2f*sqrt(x)*exp(%.2f*t)', p_fit))
The result doesn't look too satisfying though. But that mainly is because of your data. Have a look here:
With the cftool-command (curve fitting toolbox) you can fit to your own functions, returning the variables that you need (a,b). Make sure your x-data and y-data are in separate variables. you can also specify weights for your measurements.

matlab determine curve

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 :)