%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% File: http://web.mit.edu/8.13/matlab/fittemplate11.m
% Contributors: JLAB Staff, Jim Kiger, William McGehee, Xuangcheng Shao
% Last Updated: 2011-Oct-25
%
%
% This Matlab script is intended to be used for linear and non-linear
% fitting problems in experimental physics.
%
% It closely follows the material presented in
% "Data Reduction and Error Analysis for the Physical Sciences: 3rd Editon"
% by Philip R. Bevington and D. Keith Robinson
% Students are responsible for understanding the underlying methods,
% DO NOT USE THIS SCRIPT AS A BLACK BOX!!!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear; % Clears all variables from memory
%load bev81.txt; % Loads sample data set 'bev81.txt' (Bevington Table 8.1, pg. 144)
%x = bev81(:,1); % Assigns the first column of mydata to a vector called 'x'
%y = bev81(:,2); % Assigns the second column of mydata to a vector called 'y'
%sig = sqrt(y); % sets sigma to the square root of 'y' for Poisson statistics
load gauss3.dat; % Load and prepare the gauss1.txt NIST dataset
x=gauss3(:,1);
y=gauss3(:,2);
sig=ones(size(x))*sqrt(6.25); % Constant error value, variance (=sigma^2) of gauss3 data set is 6.25
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% There are other ways to assign your error vector
% You might want to use one of these three methods below.
% sigma = 2.5; % Constant error value, variance of gauss3 data set is 6.25.
% sig = ones(size(x))*sigma; % creates a constant vector the same size as 'x' w/value sigma
% sig = mydata(:,3); % Assigns the third column to a vector called
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% At this point you should know whether you want to perform a 'linear' or 'non-linear fit
% to your data set. Select and uncomment the appropriate section to proceed
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ** LINEAR FITS **
% If you wish to fit your data to a straight line, you can use the function
% 'fitlin.m' This function is also taken directly from the pages of Bevington
% and Robinson Ch. 6 (p. 114) and it is very easy to follow. The usage of
% fitlin is as follows:
%
% [a,aerr,chisq,yfit] = fitlin(x,y,sig)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ** NON-LINEAR FITTING **
% For non-linear fits, you will need to create a Matlab 'function handle' for your model
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Some useful non-linear model functions are listed here:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function_sin = @(x,a) a(1)*sin(a(2)*x+a(3));
function_gaussian = @(x,a) a(1)*(exp(-((x-a(2))/a(3)).^2));
function_lorentzian = @(x,a) a(1)*(a(2)/(2*pi)./(((x-a(3)).^2)+(a(2)^2/4)));
function_poisson = @(x,a) a(1)*exp(x*log(a(2))-a(2)-gammaln(x+1));
function_NIST = @(x,a) a(1)*exp(-a(2)*x) +...
a(3)*exp(-((x-a(4)).^2)/a(5)^2) +...
a(6)*exp(-((x-a(7)).^2)/a(8)^2);
function_bev82 = @(x,a) a(1)+a(2)*exp(-x/a(4))+a(3)*exp(-x/a(5));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% For non-linear fits using the Levenberg-Marquardt method, if you want to use
% analytic expressions instead of numerical derivatives (not very common), enter the
% expressions for derivatives in the following cell dYda, and let sgn be 1
% instead of 0. Note: the definition of dYda cannot be commented out since
% it has to be defined no matter whether it is actually used.
sgn=0; % Ignore the following analytic derivatives and calculate them numerically instead (usual case)
dYda={@(x,a) 1,
@(x,a) exp(-x/a(4)),
@(x,a) exp(-x/a(5)),
@(x,a) a(2)/a(4)^2*exp(-x/a(4)).*x,
@(x,a) a(3)/a(5)^2*exp(-x/a(5)).*x};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create a vector containing your initial estimates for the model
% parameters. It is important to make good estimates of these initial
% values to ensure that you chi-squared minimization doesn't occur in a
% 'local' minimum.
% a0 = [10 900 80 27 225]; % Initial parameter estimates for Bevington Ch. 8 Dataset
% a0 = [94 .0105 99 63 25 71 180 20]; % Initial estimates for gauss1.txt
a0 = [94 .009 90.1 113 20 73.8 140 20]; % Initial estimates for gauss3.txt
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Selection of Chi-Squared Minimization Algorithm
% Junior Lab Techincal Staff have written two Matlab fitting scripts which you may find useful:
% 'levmar.m' implements the Marquardt Method:
% Bevington and Robinson Section 8.6, Page 161
% 'gradsearch.m' implements the Gradient-Search Method
% Bevington and Robinson Section 8.4, Page 153
%
% To select the 'levmar.m' fitting script, set algselect=0.
% To select the 'gradsearch.m' fitting script, set algselect=1.
%
algselect=0;
if algselect==0
[a,aerr,chisq,yfit,corr] = levmar(x,y,sig,function_NIST,a0,dYda,sgn);
else
[a,aerr,chisq,yfit] = gradsearch(x,y,sig,function_NIST,a0);
end
%
% Inputs: x -- the x data to fit
% y -- the y data to fit
% sig -- the uncertainties on the data points
% fitfun -- the name of the function to fit to
% a0 -- the initial guess at the parameters
% dYda -- analytical derivatives for fitting function
% sgn -- flat to select analytical or numerical derivatives
%
% Outputs: a -- the best fit parameters
% aerr -- the errors on these parameters
% chisq -- the final value of chi-squared
% yfit -- the value of the fitted function at the points in x
% corr -- error matrix = inverse of the curvature matrix alpha (output from levmar.m only)
%
%
% IMPORTANT NOTE - since both levmar.m and gradsearch.m are iterative algorithms, you can vary
% the step size and tolerance used in the fitting process. These are
% specified within the subroutines 'levmar.m' and 'gradsearch.m'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Always report statistics on the 'Goodness of Fit'
%%% More information can be found in Bevington's Appendix C and
%%% in Matlab's Online Help searching under 'goodness of fit'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fprintf(1,'Uncertainties');
fprintf(1,'%9.1e',aerr);
fprintf(1,'\n');
level = 0.68; % confidence level 68% =1-sigma, 95% = 2-sigma
dof = length(x) - length(a);
fprintf(1,'Degrees of Freedom = %5d \n',dof);
RChiSquare = chisq/dof';
prob=100*(1-chi2cdf(chisq,dof)); % This is the Matlab equivalent of Bevington Table C.4
fprintf(1,'Reduced Chi-Squared=%6.2f; probability=%6.1f percent \n',RChiSquare,prob);
if algselect==0
fprintf(1,'\n Elements of the error matrix (Marquardt method only)\n');
for i=1:length(a)
fprintf(1,'%10.4f',corr(i,:));
fprintf(1,'\n');
end
end
residuals = y-yfit;
rss=sum(residuals.^2);
rstd=std(residuals);
fprintf(1,'Residual Sum of Squares = %8.3f \n',rss);
fprintf(1,'Residual Standard Deviation = %8.3f \n',rstd);
fprintf(1,'\n');
[a',aerr']
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ** Confidence Intervals **
% The confidence bounds for fitted coefficients are given by
% [b-t*sqrt(S),b+t*sqrt(S)].
% b are the coefficients produced by the fit, t depends on the confidence
% level, and is computed using the inverse of student's t cumulative
% distribution function, S is the vector of diagonal elements from the
% estimated covariance matrix. 'aerr' is the square root of 'S'
width = tinv(level,dof)*aerr;
lower = a - width;
upper = a + width;
FitResults = [a', lower', upper'];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Here we plot the basic figure. Be sure to double check the sizes and colors
% of all your labels and markers to ensure their visibility to your audience.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure();
subplot(2,1,1);
errorbar(x,y,sig,'.');
axis([0 250 0 150]);
hold on;
plot(x,yfit,'r','LineWidth',3);
xlabel('X-Axis Label [units]','fontsize',12);
ylabel('Y-Axis Label [units]','fontsize',12);
title({'Descriptive Title: e.g. Determination of Fine Structure Splitting in Hydrogen'},'fontsize',12);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% A certain amount of information placed on a graph can make it more easily
% understood by an audience who perhaps cannot hear everything you say
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
str1=num2str(RChiSquare,2);
text(5,25,['\chi^2_{\nu-1} = ' str1]);
text(5,10,['Probability =' num2str(prob,2),' %']);
str1=num2str(a(7)-a(4),3);
str2=num2str(sqrt(width(7)^2+width(4)^2),1);
text(115,55,['Fit Energy Splitting']);
text(122,45,['(b_3 - b_2)']);
text(119,35,[str1 ' \pm ' str2 ' nm']);
plot(zeros(size(y))+a(4),y,'r-');
plot(zeros(size(y))+a(7),y,'r-');
text(5,125,'Model: y(x) =a_1e^{-b_1x}+a_2*e^{-((x-b_2)/c_2)^2}+a_3*e^{-((x-b_3)/c_3)^2}','fontsize',12);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Residuals qualitatively demonstrate the agreement between the model
% function with the best fit parameters and the data being modeled
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
subplot(2,1,2);
plot(x,zeros(size(x)),'r-');
hold on;
plot(x,residuals,'b.');
xlabel('X-Axis Label [units]','fontsize',12);
ylabel('Y-Axis Label [units]','fontsize',12);
title({'Residuals should have \mu = 0 and lack structure','Use graphics to guide the eye...e.g. red line at zero'},'fontsize',12);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Other scientific disciplines use a variety of related terms related to
% the evaluation of 'Goodness of Fit'. In order to help you understand
% some of these terms, found in the Matlab help system, we list here some
% useful definitions.
% --------------------- Curve Fitting Definitions ------------------
%
% y = response value
% y_tilde = fit to the response value
% y_bar = mean
%
% SSE (Sum of Squares due to Error) = weighted sum of (y - y_tilde)^2.
% SSR (Sum of Squares of Regression) = weighted sum of (y_tilde - y_bar)^2
% SST (Total Sum of Squares) = weighted sum of (y - y_bar)^2
% It follows then that SST = SSE + SSR
%
% R-square = SSR / SST = 1 - (SSE / SST)
% v - degree of freedom
% v = n - m (number of response values - number of fitted coefficients)
%
% Adjusted R-square = 1 - (SSE / SST) * (n - 1) / v
% Adjusted R-square <= 1
% 1, with a value closer to 1 indicating a better fit.
% MSE (Mean Squared Error) = SSE / v (the same as R-chi-square)
% RMSE (Root Mean Squared Error) = sqrt(MSE)
% A RMSE value closer to 0 indicates a better fit.
% ----------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%