R script for calculating Limit of Detection and Limit of Quantification
Note: the data and theory on calibration curve were with reference from: Statistics and Chemometrics for Analytical Chemistry, James N. Miller and Jane Charlotte Miller, 6th edition, Chapter 5
Chemists often work with calibration data using standards of known concentrations and putting them through instrumental analysis. When plotting a calibration curve, it is of interest to calculate the limit of detection (LOD) and limit of quantification (LOQ) of the method.
The fluorescence intensities of standard aqueous fluorescein solutions were analysed with a spectrophotometer, and the fluorescence results are shown below:
# A tibble: 7 x 2
conc_pgml fluo
<dbl> <dbl>
1 0 2.1
2 2 5
3 4 9
4 6 12.6
5 8 17.3
6 10 21
7 12 24.7
Let’s fit a linear model to get the slope (b) and intercept(a).
\[ y = a + bx \]
Call:
lm(formula = fluo ~ conc_pgml, data = data)
Residuals:
1 2 3 4 5 6 7
0.58214 -0.37857 -0.23929 -0.50000 0.33929 0.17857 0.01786
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.5179 0.2949 5.146 0.00363 **
conc_pgml 1.9304 0.0409 47.197 8.07e-08 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.4328 on 5 degrees of freedom
Multiple R-squared: 0.9978, Adjusted R-squared: 0.9973
F-statistic: 2228 on 1 and 5 DF, p-value: 8.066e-08
From above, we can see that slope = 1.9304, and intercept = 1.5179.
The limit of detection is defined as:
\[ LOD = \gamma_B + 3_{SB} \] where LOD is the analyte concentration wich gives a signal equal to the blank signal plus three standard deviations of the blank.
A function was created to calculate LOD and LOQ:
calcLOD_y <- function(model) {
SSE <- sum(model$residuals**2)
n <- length(model$residuals) -2
Syx <- sqrt(SSE/n)
intercept <- as.numeric(model$coefficients[1])
calculated_y <- intercept + 3*Syx
names(calculated_y) <- "calculated_y"
print(calculated_y)
chemCal::inverse.predict(model,
newdata = calculated_y,
alpha = 0.05)
}
calcLOQ_y <- function(model) {
SSE <- sum(model$residuals**2)
n <- length(model$residuals) -2
Syx <- sqrt(SSE/n)
intercept <- as.numeric(model$coefficients[1])
calculated_y <- intercept + 10*Syx
names(calculated_y) <- "calculated_y"
print(calculated_y)
chemCal::inverse.predict(model,
newdata = calculated_y,
alpha = 0.05)
}
Inserting the linear model from the fluorescence data:
LOD_x <- calcLOD_y(fl_mod)
calculated_y
2.8164
LOD_x$Prediction
[1] 0.6726958
LOQ_x <- calcLOQ_y(fl_mod)
calculated_y
5.846334
LOQ_x$Prediction
[1] 2.242319
To predict the concentration of fluorescein that has fluorescence units of 2.9, we use the function inverse.predict():
chemCal::inverse.predict(fl_mod,
newdata = 2.9,
alpha = 0.05)
$Prediction
[1] 0.7160037
$`Standard Error`
[1] 0.2645698
$Confidence
[1] 0.6800982
$`Confidence Limits`
[1] 0.03590545 1.39610195
For attribution, please cite this work as
lruolin (2021, Jan. 15). pRactice corner: Calibration Curves Data. Retrieved from https://lruolin.github.io/myBlog/posts/20210118_calibration curves/
BibTeX citation
@misc{lruolin2021calibration, author = {lruolin, }, title = {pRactice corner: Calibration Curves Data}, url = {https://lruolin.github.io/myBlog/posts/20210118_calibration curves/}, year = {2021} }