Is this code for nonlinear regression in MATLAB right? - matlab

I am trying to solve this example in MATLAB but I can't get the right answer.
i use this code
clear all;clc;
x=[4 2.25 1.45 1.0 0.65 0.25 0.006];
y=[ 0.398 0.298 0.238 0.198 0.158 0.098 0.048];
n=length(x);
sumx=sum(log10(x));
sumy=sum(log10(y));
sum2x=sum(log10(x));
sum3x=sum(log10(y));
sum4x=sum(log10(x.*y));
sumxy=sum(log10(x.^2));
sumx2y=sum(log10((x.^2) .*y));
m1=[n sumx sum2x;sumx sum2x sum3x;sum2x sum3x sum4x]
m2=[sumy;sumxy;sumx2y]
m3=inv(m1)*m2;
plot(x,y)

Isn't this just to make a power fit of the data (-r and C) to obtain the constants n and k?
If so, you can just use the Curve Fitting app in MATLAB to do so:
General model Power1:
f(x) = a*x^b
Coefficients (with 95% confidence bounds):
a = 0.2003 (0.1826, 0.2179)
b = 0.4889 (0.4028, 0.5751)
Goodness of fit:
SSE: 0.001052
R-square: 0.9877
Adjusted R-square: 0.9852
RMSE: 0.0145

Related

Magnitude ratio fitting of a first order system with cftool

I am trying to plot the magnitude ratio of a first order system using cftool, I'm aware there are other ways to do that but I need to get to the solution through this method.
I have simulated an RC circuit and, after having applied a sine input at several frequencies, I have measured the output of the system;
Here are the vectors I have created in MATLAB with the data I have measured:
f = [1 10 100 120 130 150 160 170 1000 2000 3000 10000];
Vi = zeros(1,12);
Vi(1,:) = 1; %amplitude
Vo = [0.99 0.99 0.85 0.79 0.77 0.73 0.7 0.68 0.16 0.08 0.05 0.02]; %amplitudes
Vdb = 20*log10(Vo./Vi); %Vo converted to dB
Now, given that an RC circuit is a first order system, I know that the relationship beetween magnitude ratio and frequency can be written as:
M(omega) = 1/(sqrt(1 + (omega * tau)^2))
So, opening cftool in MATLAB, I have set:
X data: f
Y data: Vdb
Custom Equation: 1/sqrt(1 + (2*pi*a*x)^2) %omega = 2*pi*f
Using these settings, however, cftool doesn't plot what I expected to see, so I would like to figure out where my mistakes are.
I believe the Y-data should be V0, not Vdb.
If you want the curvefit for the relationship between the voltage gain in dB and the frequency, then you need to alter the custom equation.

decay rate of data in matlab

I want to know how quickly some data returns to baseline after an initial peak (here at ca x=5);
The quadratic fit looks about right (from the figures option of matlab, shown below) - but I'm looking for a concise quantification of this curve, therefore I presume the 'decay rate' of the exponential function would be one very straightforward.
Is this assumption correct?
If yes, I looked at the formula on wiki for this, and attempted shamelessly to find a solve for the time constant (but unsuccessfully so). Can someone help me out, or is this actually a not so trivial problem?
edit: I was planning to find the peak using MathWorks' findpeaks() function, and the lowest point of the curve using the 'inverse' findpeaks() (as in: -y)
%approx data values of the curves below
y= [0 0.07 0.08 0.08 0.08 0.06 0.06 0.05 0.04 0.05 0.04 0.02 0.01 0.02 0.01 0.01 0.03 0.02 0.02 0.02 0.03 0.01 0.02 0.01 0.01 0.03 0.02 0.01 0.02 0.01];
x=1:numel(y);
plot(x,y);
These are the two options I was looking for, maybe someone can elaborate / improve this answer about the differences for these approaches - for me this is good enough, thanks for the comments. Before this step, using the data provided in the example of the question, the local maximum and minimum has to be extracted, this can be done easily using findpeaks()
approach 1) requires the curve toolbox from Matlab [Source]
%Fit a Single-Term Exponential Model, copy from Mathworks documentation, all credits go there
x = (0:0.2:5)';
y = 2*exp(-0.2*x) + 0.1*randn(size(x));
f = fit(x,y,'exp1')
f =
General model Exp1:
f(x) = a*exp(b*x)
Coefficients (with 95% confidence bounds):
a = 2.021 (1.89, 2.151)
b = -0.1812 (-0.2104, -0.152)
plot(f,x,y)
or approach 2) requires the optimizaion toolbox from Matlab [Source]
%copy from Mathworks documentation, all credits go there
rng default % for reproducibility
d = linspace(0,3);
y = exp(-1.3*d) + 0.05*randn(size(d));
fun = #(r)exp(-d*r)-y;
x0 = 4;
x = lsqnonlin(fun,x0)
plot(d,y,'ko',d,exp(-x*d),'b-')
legend('Data','Best fit')
xlabel('t')
ylabel('exp(-tx)')

Curve fitting a strongly varying data

I am using Matlab curve-fitting tool cftool to fit my data. The issue is that the y values are largely varying (strongly decreasing) with respect to x-axis. A sample is given below,
x y
0.1 237.98
1 25.836
10 3.785
30 1.740
100 0.804
300 0.431
1000 0.230
2000 0.180
The fitted format is: y=a/x^b+c/x^d with a,b,c, and d as constants. The curve-fit from matlab is quite accurate for large y-values (that's at lower x-range) with less than 0.1% deviation. However, at higher x-values, the accuracy of the fit is not good (around 11% deviation). I would like also to include % deviation in the curve-fitting iteration to make sure the data is captured exactly. The plot is given for the fit and data reference.
Can anyone suggest me for better ways to fit the data?
The most common way to fit a curve is to do a least squares fit, which minimizes the sum of the square differences between the data and the fit. This is why your fit is tighter when y is large: an 11% deviation on a value of 0.18 is only a squared error of 0.000392, while a 0.1% deviation on a value of 240 is a squared error of 0.0576, much more significant.
If what you care about is deviations rather than absolute (squared) errors, then you can either reformulate the fitting algorithm, or transform your data in a clever way. The second way is a common and useful tool to know.
One way to do this in your case is fit the log(y) instead of y. This will have the effect of making small errors much more significant:
data = [0.1 237.98
1 25.836
10 3.785
30 1.740
100 0.804
300 0.431
1000 0.230
2000 0.180];
x = data(:,1);
y = data(:,2);
% Set up fittype and options.
ft = fittype( 'a/x^b + c/x^d', 'independent', 'x', 'dependent', 'y' );
opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Display = 'Off';
opts.StartPoint = [0.420712466925742 0.585539298834167 0.771799485946335 0.706046088019609];
%% Usual least-squares fit
[fitresult] = fit( x, y, ft, opts );
yhat = fitresult(x);
% Plot fit with data.
figure
semilogy( x, y );
hold on
semilogy( x, yhat);
deviation = abs((y-yhat))./y * 100
%% log-transformed fit
[fitresult] = fit( x, log(y), ft, opts );
yhat = exp(fitresult(x));
% Plot fit with data.
figure
semilogy( x, y );
hold on
semilogy( x, yhat );
deviation = abs((y-yhat))./y * 100
One approach would be to fit to the lowest sum-of-squared relative error, rather than the lowest sum-of-squared absolute error. When I use the data posted in your question, fitting to lowest sum-of-squared relative error yields +/- 4 percent error - so this may be a useful option. To verify if you might want to consider this approach, here are the coefficients I determined from your posted data using this method:
a = 2.2254477037465399E+01
b = 1.0038013513610324E+00
c = 4.1544917994119190E+00
d = 4.2684956973959676E-01

3d surface plot with vectors in matlab

Could you please help me with the following issue: I would like to 3d plot 3 vectors in MATLAB. I know that it translates into points in a 3d space, but is there a way to obtain actually the surface? I tried similar approaches found in different answers, but they don't seem to work for my data, i.e. I obtain an empty graph. Thanks a lot! Below the codes I tried:
%Method 1:
[X2,Y2]=meshgrid(a1,a2);
Z2=griddata(a1,a2,z,X2,Y2);
surf(a1,a2,Z2); % I obtain an empty graph in this case
%Method 2:
trisurf(delaunay(a1,a2),a1,a2,z) %In this case I obtain a graph but it seems unrealistic
%Method 3: using scatter3, I obtain a line, but I would like to have a surface
scatter3(a1,a2,z)
a1 and a2 are 1x100 vectors with values in (0,1) and z is a complicated function of these two vectors, also having dimension 1x100.
Thanks for your help!
Edit:
a1 = [0.05 0.06 0.07 0.08 0.09 0.1 ...]
a2 = [0.9 0.89 0.88 0.87 0.86 0.85 ...]
z = [-0.0009 -0.0011 -0.0012 -0.0014 -0.0016 -0.0017 ...]

Matlab custom curve fitting fail

I'm using cftool for a custom fit of Mössbauer spectroscopy data. There are two coefficients, Gamma and N0.
N = f(v)
= (299792458000^2*Gamma^2*N0)/(299792458000^2*Gamma^2+4*v^2*(4.29383292e-15)^2)
Using default settings (Trust-region, robust off, etc.) I get the following fit:
Fit computation did not converge:
Fitting stopped because the number of iterations or function evaluations exceeded the specified maximum.
Fit found when optimization terminated:
General model:
f(v) = (299792458000^2*Gamma^2*N0)/(299792458000^2*Gamma^2+4*v^2*(4.29383292e-
15)^2)
Coefficients (with 95% confidence bounds):
Gamma = 0.9137 (-Inf, Inf)
N0 = 2.454e+04 (2.059e+04, 2.849e+04)
Goodness of fit:
SSE: 6.41e+11
R-square: -2068
Adjusted R-square: -2073
RMSE: 4.013e+04
Warning: A negative R-square is possible if the model does not contain a constant term and the fit is poor (worse than just fitting the mean). Try changing the model or using a different StartPoint.
If I switch to Levenberg-Marquardt, I get a straight line through the data:
General model:
f(v) = (299792458000^2*Gamma^2*N0)/(299792458000^2*Gamma^2+4*v^2*(4.29383292e-
15)^2)
Coefficients (with 95% confidence bounds):
Gamma = 0.793 (-Inf, Inf)
N0 = 6.456e+04 (6.447e+04, 6.465e+04)
Goodness of fit:
SSE: 3.098e+08
R-square: 2.22e-16
Adjusted R-square: -0.002513
RMSE: 882.3
Why is this failing so badly in both cases?
f(v) simplifies to f(v)=N0/(1+(2.8645e-26*(v/Gamma))^2) so the 1 in the denominator dominates until (v/Gamma) starts getting as big as 10^25. With your Gamma of 0.793 and your v of |15| I think matlab might have a hard time of converging to anything other than N0