aboutsummaryrefslogtreecommitdiff
path: root/R/linearity.R
blob: 8970fe70e6a955f441a1f1243085407a0d6e2cdb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#' Assess the linearity of a calibration curve
#' 
#' A function to create diagnostic plots for the assessment of the linearity of 
#' calibration data based on their point-to-point slope or the curvature. 
#' The underlying methods follow ISO 84 66-1:2021 and DIN 32 402-51:2017 
#' (German Industrial Norm).
#' 
#' The point-to-point slope method is based on the assumption that the slope 
#' between two points should not vary greatly within the linear range. 
#' 
#' The curvature method is similar to the point-to-point slope method. Here, 
#' the ratio between the instrument signal and the concentration of the 
#' calibration standard is assumed not to vary greatly within the linear range.
#' 
#' The use of the Mandel test is discouraged due to its limitations in the 
#' identification of non-linear behaviour of calibration curves (Andrade and 
#' Gomes-Carracedo, 2013). 
#' 
#' @param x numeric vector of independent values (usually concentrations).
#' @param y numeric vector of dependent values (usually the signal of the 
#' analytical device).
#' @param method character string. Supported methods are "slope" and 
#' "curvature".
#' @param tolerance numeric value between 0 and 1, describing the acceptable
#' deviation from the median of the slopes or the signal-to-concentration
#' ratio. The default tolerance is 10%.
#' @return returns a diagnostic plot 
#' 
#' @author Anil Axel Tellbüscher
#' 
#' @importFrom graphics abline
#' @importFrom graphics lines
#' @importFrom stats median
#' 
#' @examples
#' data(din32645)
#' # Point-to-point slope plot
#' linearity(din32645$x, din32645$y, method = "slope")
#' 
#' # Curvature plot
#' linearity(din32645$x, din32645$y, method = "curvature", tolerance = 0.2)
#' 
#' @references ISO 8466-1:2021. Water quality — Calibration and evaluation of 
#' analytical methods — Part 1: Linear calibration function
#' 
#' J. M. Andrade and M. P. Gomez-Carracedo (2013) Notes on the use of 
#' Mandel's test to check for nonlinearity in laboratory calibrations. 
#' Analytical Methods 5(5), 1145 - 1149.
#' 
#' @export
# Function to assess linearity of data using either slope or curvature method
linearity <- function(x, y, method = c("slope", "curvature"), tolerance = 0.1) {
  
  # Check data integrity
  # Ensure that x and y vectors have the same length
  stopifnot("x and y must have the same length!" = length(x) == length(y))
  
  method <- match.arg(method)
  
  # Calculate the 'result' based on the chosen method
  if (method == "slope") {
    # For the 'slope' method, calculate the difference between consecutive points
    x_diff = diff(x)        # Difference in x values
    y_diff = diff(y)        # Difference in y values
    result = y_diff / x_diff # Point-to-point slope (rate of change)
  } else if (method == "curvature") {
    # For the 'curvature' method, calculate the signal-to-concentration ratio
    result = y / x # Element-wise division of y by x
  }
  
  # Calculate the median of the results for tolerance check
  result_median <- median(result)
  
  # Define upper and lower tolerance boundaries
  upper_tolerance <- result_median + tolerance * result_median
  lower_tolerance <- result_median - tolerance * result_median
  
  # Create a data frame to store the result and corresponding indices
  df <- data.frame(result = result, index = 1:length(result))
  
  # Identify points that fall outside the tolerance range
  outside_tolerance <- rbind(
    subset(df, result > upper_tolerance), # Points above the upper tolerance
    subset(df, result < lower_tolerance)  # Points below the lower tolerance
  )
  
  # Basic scatter plot of the result against the index
  plot(result ~ index, data = df, 
       main = "linearity assessment", ylab = method, 
       pch = 16)
  
  # Draw a line connecting all the points to visualize the trend
  lines(df$index, df$result, col = "blue") # Blue line connecting points
  
  # Highlight points that are outside the tolerance range in red
  points(x = outside_tolerance$index, y = outside_tolerance$result, 
         pch = 16, col = "red")
  
  # Add a horizontal line at the median value of the result
  abline(h = result_median, col = "red")
  
  # Add dashed horizontal lines at the upper and lower tolerance limits
  abline(h = upper_tolerance, col = "red", lty = 3) # Upper tolerance
  abline(h = lower_tolerance, col = "red", lty = 3) # Lower tolerance
}

Contact - Imprint