R/lod.R

Defines functions lod.lm lod.default lod

Documented in lod lod.default lod.lm

#' Estimate a limit of detection (LOD)
#' 
#' The decision limit (German: Nachweisgrenze) is defined as the signal or
#' analyte concentration that is significantly different from the blank signal
#' with a first order error alpha (one-sided significance test).  The detection
#' limit, or more precise, the minimum detectable value (German:
#' Erfassungsgrenze), is then defined as the signal or analyte concentration
#' where the probability that the signal is not detected although the analyte
#' is present (type II or false negative error), is beta (also a one-sided
#' significance test).
#' 
#' @aliases lod lod.lm lod.rlm lod.default
#' @param object A univariate model object of class \code{\link{lm}} or
#' \code{\link[MASS:rlm]{rlm}} with model formula \code{y ~ x} or \code{y ~ x -
#' 1}, optionally from a weighted regression.
#' @param \dots Placeholder for further arguments that might be needed by
#' future implementations.
#' @param alpha The error tolerance for the decision limit (critical value).
#' @param beta The error tolerance beta for the detection limit.
#' @param method The \dQuote{default} method uses a prediction interval at the
#' LOD for the estimation of the LOD, which obviously requires iteration. This
#' is described for example in Massart, p. 432 ff.  The \dQuote{din} method
#' uses the prediction interval at x = 0 as an approximation.
#' @param tol When the \dQuote{default} method is used, the default tolerance
#' for the LOD on the x scale is the value of the smallest non-zero standard
#' divided by 1000. Can be set to a numeric value to override this.
#' @return A list containig the corresponding x and y values of the estimated
#' limit of detection of a model used for calibration.
#' @note 
#' * The default values for alpha and beta are the ones recommended by IUPAC.
#' * The estimation of the LOD in terms of the analyte amount/concentration xD
#' from the LOD in the signal domain SD is done by simply inverting the
#' calibration function (i.e. assuming a known calibration function).
#' * The calculation of a LOD from weighted calibration models requires a
#' weights argument for the internally used \code{\link{predict.lm}}
#' function, which is currently not supported in R.
#' @seealso Examples for \code{\link{din32645}}
#' @references Massart, L.M, Vandenginste, B.G.M., Buydens, L.M.C., De Jong,
#' S., Lewi, P.J., Smeyers-Verbeke, J. (1997) Handbook of Chemometrics and
#' Qualimetrics: Part A, Chapter 13.7.8
#' 
#' J. Inczedy, T. Lengyel, and A.M. Ure (2002) International Union of Pure and
#' Applied Chemistry Compendium of Analytical Nomenclature: Definitive Rules.
#' Web edition.
#' 
#' Currie, L. A. (1997) Nomenclature in evaluation of analytical methods
#' including detection and quantification capabilities (IUPAC Recommendations
#' 1995).  Analytica Chimica Acta 391, 105 - 126.
#' @importFrom stats optimize predict
#' @examples
#' 
#' m <- lm(y ~ x, data = din32645)
#' lod(m) 
#' 
#' # The critical value (decision limit, German Nachweisgrenze) can be obtained
#' # by using beta = 0.5:
#' lod(m, alpha = 0.01, beta = 0.5)
#' 
#' @export
lod <- function(object, ..., alpha = 0.05, beta = 0.05, method = "default", tol = "default")
{
  UseMethod("lod")
}

#' @export
lod.default <- function(object, ..., alpha = 0.05, beta = 0.05, method = "default", tol = "default")
{
  stop("lod is only implemented for univariate lm objects.")
}

#' @export
lod.lm <- function(object, ..., alpha = 0.05, beta = 0.05, method = "default", tol = "default")
{
  if (length(object$weights) > 0) {
    stop(paste(
      "\nThe detemination of a lod from calibration models obtained by",
      "weighted linear regression requires confidence intervals for",
      "predicted y values taking into account weights for the x values",
      "from which the predictions are to be generated.",
      "This is not supported by the internally used predict.lm method.",
      sep = "\n"
    ))
  }
  xname <- names(object$model)[[2]]
  xvalues <- object$model[[2]]
  yname <- names(object$model)[[1]]
  newdata <- data.frame(0)
  names(newdata) <- xname
  y0 <- predict(object, newdata, interval = "prediction", 
    level = 1 - 2 * alpha)
  yc <- y0[[1,"upr"]]
  if (method == "din") {
    y0.d <- predict(object, newdata, interval = "prediction",
      level = 1 - 2 * beta)
    deltay <- y0.d[[1, "upr"]] - y0.d[[1, "fit"]]
    lod.y <- yc + deltay 
    lod.x <- inverse.predict(object, lod.y)$Prediction
  } else {
    f <- function(x) {
      newdata <- data.frame(x)
      names(newdata) <- xname
      pi.y <- predict(object, newdata, interval = "prediction",
        level = 1 - 2 * beta)
      yd <- pi.y[[1,"lwr"]]
      (yd - yc)^2
    }
    if (tol == "default") tol = min(xvalues[xvalues !=0]) / 1000
    lod.x <- optimize(f, interval = c(0, max(xvalues) * 10), tol = tol)$minimum
    newdata <- data.frame(x = lod.x)
    names(newdata) <- xname
    lod.y <-  predict(object, newdata)
    names(lod.y) <- NULL
  }
  lod <- list(lod.x, lod.y)
  names(lod) <- c(xname, yname)
  return(lod)
}

Try the chemCal package in your browser

Any scripts or data that you put into this service are public.

chemCal documentation built on April 1, 2022, 5:08 p.m.