This code determines the limit of detection from experimental data in a linear calibration curve. See the following article for details:
Hans-Peter Loock and Peter D. Wentzell, Detection limits of chemical sensors: Applications and misapplications, Sensors and Actuators B: Chemical, 173 (2012) 157–163, https://doi.org/10.1016/j.snb.2012.06.071
% Created by: Arthur Giron Santos % Date: June, 2022% Created by: Arthur Giron Santos % Date: June, 2022 % Version: 1.0 % Objective: to calculate the Limit of Detection of chemical sensors using the following data provided: % x (concentration), y (signal), t (Student t-function) and k (number of replicate measurements). % cite as: % A.G. Santos, and H-P Loock (2022) Limit of Detection source code (Version 1.0) [Source code] <link_website> % based on: Hans-Peter Loock and Peter D. Wentzell, Detection limits of chemical sensors: Applications and misapplications, % Sensors and Actuators B: Chemical, 173 (2012) 157–163, https://doi.org/10.1016/j.snb.2012.06.071 % In the code below we refer to equation numbers in Sens. Act. B: Chem, 173, (2012) 157–163. % Sample data => replace those row vectors with your data x = [1,4,9,16,25,36,49,64,81,100]; % y = [10.967044171877, 13.7266562041864, 21.6774325819587, 28.0501802939311, 36.0314926863133, 51.9667357806104, 55.1532263138279, 74.6573287557684, 71.9956136960292, 129.299736817629]; y= [0.948765056,2.868444736,9.156115413,10.93922882,29.1421891,35.21056678,47.19701892,78.89101104,71.86086959,88.32222164]; t = 3; % Value of Student T-function k = 1; % number of replicates for each value [LOD_1 LOD_2 LOD_3] = calculateLOD(x,y,t,k); output = "LOD_1 = "+LOD_1+ " (dashed blue line; see equation (11)" output = "LOD_2 = "+LOD_2+ " (solid black line; Currie and Svehla (1994); see equation (15)" output = "LOD_3 = "+LOD_3+ " (dashed red line; should not be used)"function [LOD_1 LOD_2 LOD_3] = calculateLOD(x,y,t,k) % -------------------------------------------------------------------------- % Calculate LOD % -------------------------------------------------------------------------- % Assuming that X and Y are ROW ARRAYS n = length(x); % perform linear regression x_aux = ones(size(x',1),1); % auxiliary variable x_aux = [x_aux x']; % [c,~,~,~,stats] = regress(y',x_aux); % calculates: a (slope), b (intersept), R^2 (correlation coefficient) c = polyfit(x,y,1); a = c(1); % slope (=sensitivity) b = c(2); % R = stats(1); % Calculate determinant in the denominator of equations (4) and (5) sum_x = sum(x); % auxiliary variable sum_x2 = sum(x.^2); % auxiliary variable D = sum_x2*n - sum_x*sum_x; % equation (6) d = zeros(1,length(x)); for i = 1:length(x) % numerator in (8) d(i) = (y(i) - (a*x(i) + b))^2; end sigma_y = sqrt( sum(d)/(n-2) ); % equation (8) aux = (n/D - ((abs(a)/t)/sigma_y)^2); % given for completeness, but should not be used: LOD_3 = t*sigma_y / a; % LOD_2 from equation (11): LOD_2 = (2/D)/aux * ( sum_x - sqrt( sum_x^2 - D^2 * aux * (sum_x2/D + 1/k) ) ); % LOD_3 determined from equation (15), which is derived from % Currie and Svehla, Nomenclature for the presentation of results of chemicalanalysis, Pure and Applied Chemistry 66 (1994) 595–608 LOD_1 = 2*t*sigma_y / ( n * (t*sigma_y)^2 - D*a^2 ) * ( t*sigma_y*sum_x - sqrt(D*a^2 * sum_x2 + (a*D)^2) ); % -------------------------------------------------------------------------- % PLOT GRAPHIC % -------------------------------------------------------------------------- x_fit = zeros(1,length(x)); y_fit = zeros(1,length(x)); sigma_x = zeros(1,length(x)); % for ERROR BARS in x MpC_x = zeros(1,length(x)); % mean_x + conf_x MmC_x = zeros(1,length(x)); % mean_x - conf_x MpS_x = zeros(1,length(x)); % mean_x + sigma_x MmS_x = zeros(1,length(x)); % mean_x - sigma_x y_fit = [y_fit, 0]; % "y_fit" needs an extra element for the plot y_fit(1) = b; for i = 1:length(x) x_fit(i) = (y(i) - b)/a; % x-values of the fit line sigma_x(i) = sigma_y/abs(a) * sqrt( 1/k + ( x_fit(i)^2 * n + sum_x2 - 2*x_fit(i)*sum_x )/D ); % equation (9) MpC_x(i) = x_fit(i) + t*sigma_x(i); MmC_x(i) = x_fit(i) - t*sigma_x(i); MpS_x(i) = x_fit(i) + sigma_x(i); MmS_x(i) = x_fit(i) - sigma_x(i); y_fit(i+1) = a*x(i) + b; end %auxiliary variables x0 = [0, x]; aux = b+a*LOD_2/2; LOD3x = [0, LOD_3, LOD_3]; LOD3y = [aux, aux, 0]; LOD2x = [0, LOD_2, LOD_2]; LOD2y = [aux, aux, 0]; aux = b+a*LOD_1/2; LOD1x = [0, LOD_1, LOD_1]; LOD1y = [aux, aux, 0]; %plots f = figure; f.Position = [10 10 960 540]; plot(x,y,"ko") ylim([0 inf]) ax = gca; ax.YAxisLocation = 'origin'; title('Limit of Detection') xlabel("Concentration") ylabel("Signal") txt={"LOD_1 = "+LOD_1+" (dashed blue line; eq'n [11]) ", "LOD_2 = "+LOD_2+ " (solid black line; eq'n [15])","LOD_3 = "+LOD_3+ " (dashed red line; don't use)"}; dim = [.6 .2 .1 .1]; annotation("textbox",dim,'String',txt,'FitBoxToText','on','BackgroundColor','white') hold on %errorbars yerr = zeros(1,length(x)) + sigma_y; xerr = sigma_x; errorbar(x,y,yerr,yerr,xerr,xerr,'.') %red line - linear fit plot(x0,y_fit,"r-") %blue lines - confidence intervals plot(MmC_x,y,"b-",MpC_x,y,"b-") %LOD points plot(LOD3x,LOD3y,"r--",LOD2x,LOD2y,"k-",LOD1x,LOD1y,"b--") hold off end
