| 1 |
# FIND PEAKS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname peaks_find |
|
| 7 |
#' @aliases peaks_find,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "peaks_find", |
|
| 10 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, method = "MAD", SNR = 2, m = NULL, ...) {
|
|
| 12 |
## Validation |
|
| 13 | ! |
assert_length(y, length(x)) |
| 14 | ! |
method <- match.arg(method, several.ok = FALSE) |
| 15 | ! |
if (is.null(m)) {
|
| 16 | ! |
m <- as.integer(length(x) * 0.05) |
| 17 | ! |
if (m %% 2 == 0) m <- m + 1 |
| 18 |
} |
|
| 19 | ||
| 20 |
## Noise threshold |
|
| 21 | ! |
threshold <- NULL |
| 22 | ! |
if (SNR != 0) {
|
| 23 | ! |
noise <- switch ( |
| 24 | ! |
method, |
| 25 | ! |
MAD = stats::mad |
| 26 |
) |
|
| 27 | ! |
threshold <- noise(y, ...) * SNR |
| 28 | ! |
index_noise <- y < threshold |
| 29 | ! |
y[index_noise] <- 0 |
| 30 |
} |
|
| 31 | ||
| 32 |
## Windows |
|
| 33 | ! |
shape <- diff(sign(diff(y, na.pad = FALSE))) |
| 34 | ! |
win <- window_sliding(length(x), m, i = which(shape < 0) + 1L) |
| 35 | ||
| 36 |
## Peaks detection |
|
| 37 | ! |
pks <- vapply( |
| 38 | ! |
X = win, |
| 39 | ! |
FUN = function(w, k, data) {
|
| 40 | ! |
i <- length(w) - k # Middle of the window |
| 41 | ! |
p <- if (all(data[w[-i]] <= data[w[i]])) w[i] else 0 |
| 42 | ! |
return(p) |
| 43 |
}, |
|
| 44 | ! |
FUN.VALUE = numeric(1), |
| 45 | ! |
k = (m - 1) / 2, |
| 46 | ! |
data = y |
| 47 |
) |
|
| 48 | ||
| 49 | ! |
xy <- list(x = x[pks], y = y[pks]) |
| 50 | ! |
attr(xy, "noise") <- threshold |
| 51 | ! |
xy |
| 52 |
} |
|
| 53 |
) |
|
| 54 | ||
| 55 |
#' @export |
|
| 56 |
#' @rdname peaks_find |
|
| 57 |
#' @aliases peaks_find,ANY,missing-method |
|
| 58 |
setMethod( |
|
| 59 |
f = "peaks_find", |
|
| 60 |
signature = signature(x = "ANY", y = "missing"), |
|
| 61 |
definition = function(x, method = "MAD", SNR = 2, m = NULL, ...) {
|
|
| 62 | ! |
xy <- grDevices::xy.coords(x) |
| 63 | ! |
methods::callGeneric(x = xy$x, y = xy$y, method = method, SNR = SNR, |
| 64 | ! |
m = m, ...) |
| 65 |
} |
|
| 66 |
) |
|
| 67 | ||
| 68 |
#' @export |
|
| 69 |
#' @rdname peaks_fwhm |
|
| 70 |
#' @aliases peaks_fwhm,numeric,numeric-method |
|
| 71 |
setMethod( |
|
| 72 |
f = "peaks_fwhm", |
|
| 73 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 74 |
definition = function(x, y, center) {
|
|
| 75 | 1x |
assert_length(y, length(x)) |
| 76 | ||
| 77 | 1x |
i <- which_nearest(x, center) |
| 78 | 1x |
peak_height <- y[i] |
| 79 | 1x |
half_max <- peak_height / 2 |
| 80 | ||
| 81 | 1x |
scale_for_roots <- y - half_max |
| 82 | 1x |
root_indices <- which(diff(sign(scale_for_roots)) != 0) |
| 83 | ||
| 84 | 1x |
tmp <- sort(c(root_indices, i)) |
| 85 | 1x |
k <- which(tmp == i) |
| 86 | ||
| 87 | 1x |
root_left <- root_indices[k - 1] |
| 88 | 1x |
root_right <- root_indices[k] |
| 89 | ||
| 90 | 1x |
HWHM_left <- x[i] - x[root_left] |
| 91 | 1x |
HWHM_right <- x[root_right] - x[i] |
| 92 | ||
| 93 |
# xy <- list(x = c(x[root_left], x[root_right]), y = c(half_max, half_max)) |
|
| 94 | ||
| 95 | 1x |
FWHM <- 2 * min(c(HWHM_left, HWHM_right)) |
| 96 | 1x |
return(FWHM) |
| 97 |
} |
|
| 98 |
) |
|
| 99 | ||
| 100 |
#' @export |
|
| 101 |
#' @rdname peaks_fwhm |
|
| 102 |
#' @aliases peaks_fwhm,ANY,missing-method |
|
| 103 |
setMethod( |
|
| 104 |
f = "peaks_fwhm", |
|
| 105 |
signature = signature(x = "ANY", y = "missing"), |
|
| 106 |
definition = function(x, center) {
|
|
| 107 | 1x |
xy <- grDevices::xy.coords(x) |
| 108 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, center = center) |
| 109 |
} |
|
| 110 |
) |
| 1 |
# BASELINE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Linear ======================================================================= |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname baseline_linear |
|
| 8 |
#' @aliases baseline_linear,numeric,numeric-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "baseline_linear", |
|
| 11 |
signature = c(x = "numeric", y = "numeric"), |
|
| 12 |
definition = function(x, y, points = range(x)) {
|
|
| 13 | 1x |
assert_length(y, length(x)) |
| 14 | ||
| 15 |
## Find the nearest value |
|
| 16 | 1x |
z <- vapply(X = points, FUN = function(i, x) which_nearest(x, i), |
| 17 | 1x |
FUN.VALUE = numeric(1), x = x) |
| 18 | 1x |
zi <- which(x >= min(points) & x <= max(points)) |
| 19 | ||
| 20 | 1x |
data <- data.frame(x, y) |
| 21 | 1x |
fit <- stats::lm(y ~ x, data = data, subset = z) |
| 22 | 1x |
pred <- stats::predict(fit, data.frame(x = x[zi])) |
| 23 | ||
| 24 | 1x |
bsl <- rep(NA_real_, length(x)) |
| 25 | 1x |
bsl[zi] <- pred |
| 26 | ||
| 27 | 1x |
xy <- list(x = x, y = bsl) |
| 28 | 1x |
attr(xy, "method") <- "linear baseline" |
| 29 | 1x |
xy |
| 30 |
} |
|
| 31 |
) |
|
| 32 | ||
| 33 |
#' @export |
|
| 34 |
#' @rdname baseline_linear |
|
| 35 |
#' @aliases baseline_linear,ANY,missing-method |
|
| 36 |
setMethod( |
|
| 37 |
f = "baseline_linear", |
|
| 38 |
signature = c(x = "ANY", y = "missing"), |
|
| 39 |
definition = function(x, points = range(x)) {
|
|
| 40 | 1x |
xy <- grDevices::xy.coords(x) |
| 41 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, points = points) |
| 42 |
} |
|
| 43 |
) |
|
| 44 | ||
| 45 |
# Polynomial =================================================================== |
|
| 46 |
#' @export |
|
| 47 |
#' @rdname baseline_polynomial |
|
| 48 |
#' @aliases baseline_polynomial,numeric,numeric-method |
|
| 49 |
setMethod( |
|
| 50 |
f = "baseline_polynomial", |
|
| 51 |
signature = c(x = "numeric", y = "numeric"), |
|
| 52 |
definition = function(x, y, d = 3, tolerance = 0.001, stop = 100) {
|
|
| 53 | 1x |
assert_length(y, length(x)) |
| 54 | ||
| 55 | 1x |
polynom <- cbind(1 / sqrt(length(x)), stats::poly(x, degree = d)) |
| 56 | ||
| 57 | 1x |
old_y <- y |
| 58 | 1x |
start <- 0 |
| 59 | 1x |
convergence <- FALSE |
| 60 | 1x |
while (!convergence) {
|
| 61 | 839x |
new_y <- polynom %*% crossprod(polynom, old_y) |
| 62 | 839x |
new_y <- pmin(y, new_y) |
| 63 | ||
| 64 | 839x |
criterion <- sum(abs((new_y - old_y) / old_y), na.rm = TRUE) |
| 65 | 839x |
convergence <- criterion < tolerance |
| 66 | ||
| 67 | 839x |
old_y <- new_y |
| 68 | 839x |
start <- start + 1 |
| 69 | 839x |
if (start >= stop) {
|
| 70 | ! |
warning(tr_("Convergence not reached."), call. = FALSE)
|
| 71 | ! |
break |
| 72 |
} |
|
| 73 |
} |
|
| 74 | ||
| 75 | 1x |
xy <- list(x = x, y = new_y) |
| 76 | 1x |
attr(xy, "method") <- "polynomial baseline" |
| 77 | 1x |
xy |
| 78 |
} |
|
| 79 |
) |
|
| 80 | ||
| 81 |
#' @export |
|
| 82 |
#' @rdname baseline_polynomial |
|
| 83 |
#' @aliases baseline_polynomial,ANY,missing-method |
|
| 84 |
setMethod( |
|
| 85 |
f = "baseline_polynomial", |
|
| 86 |
signature = c(x = "ANY", y = "missing"), |
|
| 87 |
definition = function(x, d = 3, tolerance = 0.001, stop = 100) {
|
|
| 88 | 1x |
xy <- grDevices::xy.coords(x) |
| 89 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, d = d, tolerance = tolerance, |
| 90 | 1x |
stop = stop) |
| 91 |
} |
|
| 92 |
) |
|
| 93 | ||
| 94 |
# Rolling Ball ================================================================= |
|
| 95 |
#' @export |
|
| 96 |
#' @rdname baseline_rollingball |
|
| 97 |
#' @aliases baseline_rollingball,numeric,numeric-method |
|
| 98 |
setMethod( |
|
| 99 |
f = "baseline_rollingball", |
|
| 100 |
signature = c(x = "numeric", y = "numeric"), |
|
| 101 |
definition = function(x, y, m, s) {
|
|
| 102 | 1x |
n <- length(x) |
| 103 | 1x |
assert_length(y, n) |
| 104 | ||
| 105 |
## Windows |
|
| 106 | 1x |
win_minmax <- window_sliding(n, m) |
| 107 | 1x |
win_smooth <- window_sliding(n, s) |
| 108 | ||
| 109 |
## Minimize |
|
| 110 | 1x |
T1 <- vapply( |
| 111 | 1x |
X = win_minmax, |
| 112 | 1x |
FUN = function(i, data) {
|
| 113 | 2989x |
min(data[i]) |
| 114 |
}, |
|
| 115 | 1x |
FUN.VALUE = numeric(1), |
| 116 | 1x |
data = y |
| 117 |
) |
|
| 118 | ||
| 119 |
## Maximize |
|
| 120 | 1x |
T2 <- vapply( |
| 121 | 1x |
X = win_minmax, |
| 122 | 1x |
FUN = function(i, data) {
|
| 123 | 2989x |
max(data[i]) |
| 124 |
}, |
|
| 125 | 1x |
FUN.VALUE = numeric(1), |
| 126 | 1x |
data = T1 |
| 127 |
) |
|
| 128 | ||
| 129 |
## Smooth |
|
| 130 | 1x |
T3 <- vapply( |
| 131 | 1x |
X = win_smooth, |
| 132 | 1x |
FUN = function(i, data) {
|
| 133 | 2989x |
mean(data[i]) |
| 134 |
}, |
|
| 135 | 1x |
FUN.VALUE = numeric(1), |
| 136 | 1x |
data = T2 |
| 137 |
) |
|
| 138 | ||
| 139 | 1x |
xy <- list(x = x, y = T3) |
| 140 | 1x |
attr(xy, "method") <- "rolling ball baseline" |
| 141 | 1x |
xy |
| 142 |
} |
|
| 143 |
) |
|
| 144 | ||
| 145 |
#' @export |
|
| 146 |
#' @rdname baseline_rollingball |
|
| 147 |
#' @aliases baseline_rollingball,ANY,missing-method |
|
| 148 |
setMethod( |
|
| 149 |
f = "baseline_rollingball", |
|
| 150 |
signature = c(x = "ANY", y = "missing"), |
|
| 151 |
definition = function(x, m, s) {
|
|
| 152 | 1x |
xy <- grDevices::xy.coords(x) |
| 153 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, m = m, s = s) |
| 154 |
} |
|
| 155 |
) |
|
| 156 | ||
| 157 |
# Rubberband =================================================================== |
|
| 158 |
#' @export |
|
| 159 |
#' @rdname baseline_rubberband |
|
| 160 |
#' @aliases baseline_rubberband,numeric,numeric-method |
|
| 161 |
setMethod( |
|
| 162 |
f = "baseline_rubberband", |
|
| 163 |
signature = c(x = "numeric", y = "numeric"), |
|
| 164 |
definition = function(x, y, noise = 0, spline = TRUE, ...) {
|
|
| 165 | 1x |
assert_length(y, length(x)) |
| 166 | ||
| 167 |
## (chull returns points in clockwise order) |
|
| 168 | 1x |
pts <- grDevices::chull(x, y) |
| 169 | ||
| 170 |
## Check that ncol(y) is a position 1, |
|
| 171 |
## if not, rotate chull vertex index so that ncol(y) is at position 1 |
|
| 172 |
## then keep only index from ncol(y) to 1 (i.e. lower part of the hull) |
|
| 173 | 1x |
v_max <- which.max(pts) - 1 |
| 174 | 1x |
if (v_max > 0) pts <- c(pts[-seq_len(v_max)], pts[seq_len(v_max)]) |
| 175 | 1x |
pts <- pts[1:which.min(pts)] |
| 176 |
# First and last point must be minima, if not remove them |
|
| 177 | ! |
if (pts[2] == pts[1] - 1) pts <- pts[-1] # Last point |
| 178 | 1x |
pts <- rev(pts) # Sort in ascending order |
| 179 | ! |
if (pts[2] == pts[1] + 1) pts <- pts[-1] # First point |
| 180 | ||
| 181 | 1x |
bsl <- stats::approx(x = x[pts], y = y[pts], xout = x, method = "linear")$y |
| 182 | 1x |
if (spline) {
|
| 183 | 1x |
pts <- which(y <= bsl + noise) |
| 184 | 1x |
if (length(pts) > 3) {
|
| 185 | 1x |
spl <- stats::smooth.spline(x[pts], y[pts], ...) |
| 186 | 1x |
tmp <- stats::predict(spl, x, 0)$y |
| 187 |
} else {
|
|
| 188 | ! |
tmp <- stats::spline(x[pts], y[pts], xout = x)$y |
| 189 |
} |
|
| 190 |
} |
|
| 191 | ||
| 192 |
## Check baseline |
|
| 193 | 1x |
if (anyNA(tmp)) {
|
| 194 | ! |
msg <- tr_("Failed to estimate the baseline, please check your parameters.")
|
| 195 | ! |
stop(msg, call. = FALSE) |
| 196 |
} |
|
| 197 | ||
| 198 | 1x |
xy <- list(x = x, y = bsl) |
| 199 | 1x |
attr(xy, "method") <- "rubberband baseline" |
| 200 | 1x |
xy |
| 201 |
} |
|
| 202 |
) |
|
| 203 | ||
| 204 |
#' @export |
|
| 205 |
#' @rdname baseline_rubberband |
|
| 206 |
#' @aliases baseline_rubberband,ANY,missing-method |
|
| 207 |
setMethod( |
|
| 208 |
f = "baseline_rubberband", |
|
| 209 |
signature = c(x = "ANY", y = "missing"), |
|
| 210 |
definition = function(x, noise = 0, spline = TRUE, ...) {
|
|
| 211 | 1x |
xy <- grDevices::xy.coords(x) |
| 212 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, noise = noise, spline = spline, ...) |
| 213 |
} |
|
| 214 |
) |
|
| 215 | ||
| 216 |
# SNIP ========================================================================= |
|
| 217 |
#' @export |
|
| 218 |
#' @rdname baseline_snip |
|
| 219 |
#' @aliases baseline_snip,numeric,numeric-method |
|
| 220 |
setMethod( |
|
| 221 |
f = "baseline_snip", |
|
| 222 |
signature = c(x = "numeric", y = "numeric"), |
|
| 223 |
definition = function(x, y, LLS = FALSE, decreasing = FALSE, n = 100) {
|
|
| 224 | 3x |
assert_length(y, length(x)) |
| 225 | ||
| 226 |
## LLS operator |
|
| 227 | 3x |
y <- if (LLS) LLS(y) else y |
| 228 | ||
| 229 | 3x |
k <- length(y) |
| 230 | 3x |
iter <- if (decreasing) rev(seq_len(n)) else seq_len(n) |
| 231 | ||
| 232 | 3x |
tmp <- y |
| 233 | 3x |
for (p in iter) {
|
| 234 | 200x |
for (i in p:(k - p)) {
|
| 235 | 582800x |
a <- y[i] |
| 236 | 582800x |
b <- (y[i - p] + y[i + p]) / 2 |
| 237 | 582800x |
tmp[i] <- min(a, b) |
| 238 |
} |
|
| 239 | 200x |
y <- tmp |
| 240 |
} |
|
| 241 | ||
| 242 |
## Inverse LLS operator |
|
| 243 | 3x |
bsl <- if (LLS) inverseLLS(y) else y |
| 244 | ||
| 245 |
## Check baseline |
|
| 246 | 3x |
if (anyNA(bsl)) {
|
| 247 | ! |
msg <- tr_("Failed to estimate the baseline, please check your parameters.")
|
| 248 | ! |
stop(msg, call. = FALSE) |
| 249 |
} |
|
| 250 | ||
| 251 | 3x |
xy <- list(x = x, y = bsl) |
| 252 | 3x |
attr(xy, "method") <- "SNIP" |
| 253 | 3x |
xy |
| 254 |
} |
|
| 255 |
) |
|
| 256 | ||
| 257 |
#' @export |
|
| 258 |
#' @rdname baseline_snip |
|
| 259 |
#' @aliases baseline_snip,ANY,missing-method |
|
| 260 |
setMethod( |
|
| 261 |
f = "baseline_snip", |
|
| 262 |
signature = c(x = "ANY", y = "missing"), |
|
| 263 |
definition = function(x, LLS = FALSE, decreasing = FALSE, n = 100) {
|
|
| 264 | 3x |
xy <- grDevices::xy.coords(x) |
| 265 | 3x |
methods::callGeneric(x = xy$x, y = xy$y, LLS = LLS, decreasing = decreasing, |
| 266 | 3x |
n = n) |
| 267 |
} |
|
| 268 |
) |
|
| 269 | ||
| 270 |
#' LLS operator |
|
| 271 |
#' |
|
| 272 |
#' @param x A [`numeric`] vector. |
|
| 273 |
#' @keywords internal |
|
| 274 |
#' @noRd |
|
| 275 |
LLS <- function(x) {
|
|
| 276 | 2x |
log(log(sqrt(x + 1) + 1) + 1) |
| 277 |
} |
|
| 278 |
inverseLLS <- function(x) {
|
|
| 279 | 2x |
(exp(exp(x) - 1) - 1)^2 - 1 |
| 280 |
} |
|
| 281 | ||
| 282 |
# AsLS ========================================================================= |
|
| 283 |
#' @export |
|
| 284 |
#' @rdname baseline_asls |
|
| 285 |
#' @aliases baseline_asls,numeric,numeric-method |
|
| 286 |
setMethod( |
|
| 287 |
f = "baseline_asls", |
|
| 288 |
signature = c(x = "numeric", y = "numeric"), |
|
| 289 |
definition = function(x, y, p = 0.01, lambda = 10^4, stop = 100) {
|
|
| 290 | 1x |
assert_Matrix() |
| 291 | 1x |
assert_length(y, length(x)) |
| 292 | ||
| 293 | 1x |
m <- length(y) |
| 294 | 1x |
E <- Matrix::Diagonal(m) |
| 295 | 1x |
D <- Matrix::diff(E, lag = 1, differences = 2) |
| 296 | ||
| 297 | 1x |
w <- rep(1, m) # weights |
| 298 | ||
| 299 | 1x |
start <- 0 |
| 300 | 1x |
convergence <- FALSE |
| 301 | 1x |
while (!convergence) {
|
| 302 | 8x |
W <- Matrix::Diagonal(x = w) |
| 303 | 8x |
C <- Matrix::chol(W + (lambda * Matrix::t(D) %*% D)) |
| 304 | 8x |
z <- Matrix::solve(C, Matrix::solve(Matrix::t(C), w * y)) |
| 305 |
## Prior to v1.6, Matrix::solve(a=<Matrix>, b=<vector>) returns a matrix |
|
| 306 | 8x |
z <- as.numeric(z) |
| 307 | 8x |
w0 <- p * (y > z) + (1 - p) * (y < z) |
| 308 | 1x |
if (isTRUE(all.equal(w, w0))) convergence <- TRUE |
| 309 | ||
| 310 | 8x |
w <- w0 |
| 311 | 8x |
start <- start + 1 |
| 312 | 8x |
if (start >= stop) {
|
| 313 | ! |
warning(tr_("Convergence not reached."), call. = FALSE)
|
| 314 | ! |
break |
| 315 |
} |
|
| 316 |
} |
|
| 317 | ||
| 318 | 1x |
xy <- list(x = x, y = z) |
| 319 | 1x |
attr(xy, "method") <- "AsLS" |
| 320 | 1x |
xy |
| 321 |
} |
|
| 322 |
) |
|
| 323 | ||
| 324 |
#' @export |
|
| 325 |
#' @rdname baseline_asls |
|
| 326 |
#' @aliases baseline_asls,ANY,missing-method |
|
| 327 |
setMethod( |
|
| 328 |
f = "baseline_asls", |
|
| 329 |
signature = c(x = "ANY", y = "missing"), |
|
| 330 |
definition = function(x, p = 0.01, lambda = 10^4, stop = 100) {
|
|
| 331 | 1x |
xy <- grDevices::xy.coords(x) |
| 332 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, lambda = lambda, p = p, stop = stop) |
| 333 |
} |
|
| 334 |
) |
|
| 335 | ||
| 336 |
# Peak Filling ================================================================= |
|
| 337 |
#' @export |
|
| 338 |
#' @rdname baseline_peakfilling |
|
| 339 |
#' @aliases baseline_peakfilling,numeric,numeric-method |
|
| 340 |
setMethod( |
|
| 341 |
f = "baseline_peakfilling", |
|
| 342 |
signature = c(x = "numeric", y = "numeric"), |
|
| 343 |
definition = function(x, y, n, m, by = 10, lambda = 1600, d = 2, sparse = FALSE) {
|
|
| 344 | 1x |
assert_length(y, length(x)) |
| 345 | ||
| 346 |
## Number of bucket intervals |
|
| 347 | 1x |
by <- length(x) / by |
| 348 | ||
| 349 |
## Exponential decrease in interval width |
|
| 350 | 1x |
win <- m |
| 351 | 1x |
if (n != 1) {
|
| 352 | 1x |
i <- log10(m) * (1 - (0:(n - 2)) / (n - 1)) |
| 353 | 1x |
win <- ceiling((10)^c(i, 0)) |
| 354 |
} |
|
| 355 | ||
| 356 |
## Smoothing |
|
| 357 | 1x |
smoothed <- smooth_whittaker(x, y, lambda = lambda, d = d, sparse = sparse) |
| 358 | 1x |
x <- smoothed$x |
| 359 | 1x |
y <- smoothed$y |
| 360 | ||
| 361 |
## Subsampling |
|
| 362 | 1x |
breaks <- cut(seq_along(x), breaks = by, labels = FALSE) |
| 363 | 1x |
mid <- unlist(tapply(X = x, INDEX = breaks, FUN = mean, simplify = FALSE)) |
| 364 | 1x |
bin <- unlist(tapply(X = y, INDEX = breaks, FUN = min, simplify = FALSE)) |
| 365 | ||
| 366 |
## Suppression |
|
| 367 | 1x |
iter <- seq_len(n) |
| 368 | 1x |
for (i in iter) {
|
| 369 | 10x |
win_0 <- win[i] # Current half window width |
| 370 | ||
| 371 |
## Interval cut-off close to edges |
|
| 372 | 10x |
k <- seq(from = 2, to = by - 1, by = 1) |
| 373 | 10x |
j <- by - k + 1 |
| 374 | 10x |
v <- pmin(k - 1, win_0, by - k) |
| 375 | ||
| 376 |
## Point-wise iteration to the right |
|
| 377 | 2960x |
a <- mapply(function(k, v, bin) { mean(bin[(k - v):(k + v)]) }, k, v,
|
| 378 | 10x |
MoreArgs = list(bin = bin)) |
| 379 | 10x |
bin[k] <- pmin(a, bin[k]) # Baseline suppression |
| 380 | ||
| 381 |
## Point-wise iteration to the left |
|
| 382 | 2960x |
a <- mapply(function(j, v, bin) { mean(bin[(j - v):(j + v)]) }, j, v,
|
| 383 | 10x |
MoreArgs = list(bin = bin)) |
| 384 | 10x |
bin[j] <- pmin(a, bin[j]) # Baseline suppression |
| 385 |
} |
|
| 386 | ||
| 387 |
## Stretch |
|
| 388 | 1x |
mid[1] <- x[1] |
| 389 | 1x |
mid[by] <- x[length(x)] |
| 390 | 1x |
bsl <- stats::approx(mid, bin, x)$y |
| 391 | ||
| 392 | 1x |
xy <- list(x = x, y = bsl) |
| 393 | 1x |
attr(xy, "method") <- "4S Peak Filling" |
| 394 | 1x |
xy |
| 395 |
} |
|
| 396 |
) |
|
| 397 | ||
| 398 |
#' @export |
|
| 399 |
#' @rdname baseline_peakfilling |
|
| 400 |
#' @aliases baseline_peakfilling,ANY,missing-method |
|
| 401 |
setMethod( |
|
| 402 |
f = "baseline_peakfilling", |
|
| 403 |
signature = c(x = "ANY", y = "missing"), |
|
| 404 |
definition = function(x, n, m, by = 10, lambda = 1600, d = 2, sparse = FALSE) {
|
|
| 405 | 1x |
xy <- grDevices::xy.coords(x) |
| 406 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, n = n, m = m, by = by, |
| 407 | 1x |
lambda = lambda, d = d, sparse = sparse) |
| 408 |
} |
|
| 409 |
) |
| 1 |
# HELPERS |
|
| 2 | ||
| 3 |
## https://michaelchirico.github.io/potools/articles/developers.html |
|
| 4 |
tr_ <- function(...) {
|
|
| 5 | 3x |
enc2utf8(gettext(paste0(...), domain = "R-alkahest")) |
| 6 |
} |
|
| 7 | ||
| 8 |
#' Find the Nearest Value in a Vector |
|
| 9 |
#' |
|
| 10 |
#' @param x A [`numeric`] vector. |
|
| 11 |
#' @param value A [`numeric`] value. |
|
| 12 |
#' @return An [`integer`]. |
|
| 13 |
#' @keywords internal |
|
| 14 |
#' @noRd |
|
| 15 |
which_nearest <- function(x, value) {
|
|
| 16 | 7x |
which.min(abs(x - value)) |
| 17 |
} |
|
| 18 | ||
| 19 |
#' Check Odd Numbers |
|
| 20 |
#' |
|
| 21 |
#' @param x A [`numeric`] vector. |
|
| 22 |
#' @return |
|
| 23 |
#' Throws an error, if any, and returns `x` invisibly otherwise. |
|
| 24 |
#' @keywords internal |
|
| 25 |
#' @noRd |
|
| 26 |
assert_odd <- function(x) {
|
|
| 27 | 7x |
arg <- deparse(substitute(x)) |
| 28 | 7x |
if (any(round(x) %% 2 == 0)) {
|
| 29 | 1x |
msg <- sprintf(tr_("%s must be an odd integer (%g)."), sQuote(arg), x)
|
| 30 | 1x |
stop(msg, call. = FALSE) |
| 31 |
} |
|
| 32 | 6x |
invisible(x) |
| 33 |
} |
|
| 34 | ||
| 35 |
#' Check Object Length |
|
| 36 |
#' |
|
| 37 |
#' @param x An object to be checked. |
|
| 38 |
#' @param expected An appropriate expected value. |
|
| 39 |
#' @return |
|
| 40 |
#' Throws an error, if any, and returns `x` invisibly otherwise. |
|
| 41 |
#' @keywords internal |
|
| 42 |
#' @noRd |
|
| 43 |
assert_length <- function(x, expected) {
|
|
| 44 | 39x |
arg <- deparse(substitute(x)) |
| 45 | 39x |
if (length(x) != expected) {
|
| 46 | 1x |
str <- tr_("%s must be of length %d; not %d.")
|
| 47 | 1x |
msg <- sprintf(str, sQuote(arg), expected, length(x)) |
| 48 | 1x |
stop(msg, call. = FALSE) |
| 49 |
} |
|
| 50 | 38x |
invisible(x) |
| 51 |
} |
|
| 52 | ||
| 53 |
assert_Matrix <- function() {
|
|
| 54 | 2x |
if (!requireNamespace("Matrix", quietly = TRUE)) {
|
| 55 | ! |
msg <- tr_("The Matrix package is required. Please install it.")
|
| 56 | ! |
stop(msg, call. = FALSE) |
| 57 |
} |
|
| 58 | 2x |
invisible() |
| 59 |
} |
| 1 |
# XRD |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname ka2_strip_penalized |
|
| 7 |
#' @aliases ka2_strip_penalized,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "ka2_strip_penalized", |
|
| 10 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, lambda, wave = c(1.54060, 1.54443), tau = 0.5, |
|
| 12 |
nseg = 1, progress = interactive()) {
|
|
| 13 | ! |
assert_Matrix() |
| 14 | ! |
assert_length(y, length(x)) |
| 15 | ||
| 16 |
## Calculate doublet distance |
|
| 17 | ! |
wl1 <- wave[1] |
| 18 | ! |
wl2 <- wave[2] |
| 19 | ! |
wr <- wl2 / wl1 |
| 20 | ! |
d2r <- pi / 180 |
| 21 | ! |
u <- sin(d2r * x / 2) / wl1 |
| 22 | ! |
delta <- 2 * asin(wr * sin(d2r * x / 2)) / d2r - x |
| 23 | ||
| 24 | ! |
m <- length(y) |
| 25 | ! |
n <- length(lambda) |
| 26 | ! |
if (nseg == 1) nseg <- signif(m * 0.50, 1) |
| 27 | ||
| 28 |
## Empty matrices to store the results |
|
| 29 | ! |
aic <- phi <- psi <- rep(0, n) |
| 30 | ! |
Yhat <- Yhat2 <- matrix(data = 0, nrow = m, ncol = n) |
| 31 | ! |
mu <- matrix(data = 0, nrow = m, ncol = n) |
| 32 | ! |
sr <- matrix(data = 0, nrow = m, ncol = n) |
| 33 | ||
| 34 |
## Prepare model matrices |
|
| 35 | ! |
xr <- max(x) |
| 36 | ! |
xl <- min(x) - 1 |
| 37 | ! |
xd <- x - delta |
| 38 | ! |
dx <- (xr - xl) / nseg |
| 39 | ! |
knots <- seq(xl - 3 * dx, xr + 3 * dx, by = dx) |
| 40 | ||
| 41 | ! |
B1 <- sbase(x, lower = xl, upper = xr, n = nseg, deg = 3) |
| 42 | ! |
B2 <- sbase(xd, lower = xl, upper = xr, n = nseg, deg = 3) |
| 43 | ||
| 44 | ! |
B <- Matrix::rbind2(B1, B2) |
| 45 | ! |
Cb <- Matrix::Diagonal(m) |
| 46 | ! |
C <- Matrix::cbind2(Cb, tau * Cb) |
| 47 | ||
| 48 |
## Prepare penalty |
|
| 49 | ! |
E <- Matrix::Diagonal(ncol(B1)) |
| 50 | ! |
D <- Matrix::diff(E, diff = 2) |
| 51 | ||
| 52 |
## Initialize |
|
| 53 | ! |
bst <- Matrix::solve( |
| 54 | ! |
Matrix::crossprod(B1, B1) + 1e-4 * E, |
| 55 | ! |
Matrix::crossprod(B1, log((y + 1) / 2)) |
| 56 |
) |
|
| 57 | ||
| 58 | ! |
if (progress) pb <- utils::txtProgressBar(min = 0, max = n, style = 3) |
| 59 | ||
| 60 |
## Estimate for all lambda |
|
| 61 | ! |
n_lambda <- seq_len(n) |
| 62 | ! |
for (i in n_lambda) {
|
| 63 | ! |
M <- penalized_strip_ka2(y, B, C, D, bst, lambda[i]) |
| 64 | ! |
aic[i] <- M$aic |
| 65 | ! |
Yhat[, i] <- M$yhat |
| 66 | ! |
Yhat2[, i] <- M$yhat2 |
| 67 | ! |
sr[,i] <- M$res |
| 68 | ! |
mu[, i] <- M$mu |
| 69 | ! |
phi[i] <- M$phi |
| 70 | ! |
psi[i] <- M$psi |
| 71 | ! |
if (progress) utils::setTxtProgressBar(pb, i) |
| 72 |
} |
|
| 73 | ||
| 74 | ! |
if (progress) close(pb) |
| 75 | ||
| 76 |
## Find optimal model and report |
|
| 77 | ! |
k <- which.min(aic) |
| 78 | ! |
mu <- mu[, k] |
| 79 | ! |
op_lamb <- lambda[k] |
| 80 | ! |
op_aic <- aic[k] |
| 81 | ! |
yhat <- Yhat[, k] |
| 82 | ! |
yhat2 <- Yhat2[, k] |
| 83 | ! |
sr <- sr[, k] |
| 84 | ||
| 85 | ! |
xy <- list(x = x, y = yhat, ka2 = yhat2, smoothed = mu) |
| 86 | ! |
xy |
| 87 |
} |
|
| 88 |
) |
|
| 89 | ||
| 90 |
#' @export |
|
| 91 |
#' @rdname ka2_strip_penalized |
|
| 92 |
#' @aliases ka2_strip_penalized,ANY,missing-method |
|
| 93 |
setMethod( |
|
| 94 |
f = "ka2_strip_penalized", |
|
| 95 |
signature = signature(x = "ANY", y = "missing"), |
|
| 96 |
definition = function(x, lambda, wave = c(1.54060, 1.54443), tau = 0.5, |
|
| 97 |
nseg = 1, progress = interactive()) {
|
|
| 98 | ! |
xy <- grDevices::xy.coords(x) |
| 99 | ! |
methods::callGeneric(x = xy$x, y = xy$y, lambda = lambda, wave = wave, |
| 100 | ! |
tau = tau, nseg = nseg, progress = progress) |
| 101 |
} |
|
| 102 |
) |
|
| 103 | ||
| 104 | ||
| 105 |
#' Truncated p-th Power |
|
| 106 |
#' |
|
| 107 |
#' @param x A [`numeric`] vector on which the basis is calculated. |
|
| 108 |
#' @param knot A [`numeric`] scalar giving the truncation point. |
|
| 109 |
#' @param p A [`numeric`] scalar specifying the power for the basis |
|
| 110 |
#' (e.g. \eqn{p = 3} for cubic TPF).
|
|
| 111 |
#' @return |
|
| 112 |
#' A [`numeric`] vector (piece-wise defined basis functions for \eqn{x > t}).
|
|
| 113 |
#' @author P. Eilers |
|
| 114 |
#' @note |
|
| 115 |
#' Slightly modified from P. Eilers' [JOPS::tpower()]. |
|
| 116 |
#' @references |
|
| 117 |
#' Eilers, P. H. C. and Marx, B. D. (2021). *Practical Smoothing, The Joys of |
|
| 118 |
#' P-splines.* Cambridge: Cambridge University Press. |
|
| 119 |
#' @keywords internal |
|
| 120 |
#' @noRd |
|
| 121 |
tpower <- function(x, knot, p) {
|
|
| 122 | ! |
(x - knot) ^ p * (x > knot) |
| 123 |
} |
|
| 124 | ||
| 125 |
#' B-spline Design Matrix |
|
| 126 |
#' |
|
| 127 |
#' Computes a B-spline design matrix using evenly spaced knots. |
|
| 128 |
#' @param x A [`numeric`] vector of values at which the B-spline basis functions |
|
| 129 |
#' are to be evaluated. |
|
| 130 |
#' @param lower A [`numeric`] vector giving the lower limit of the domain of `x`. |
|
| 131 |
#' @param upper A [`numeric`] vector giving the upper limit of the domain of `x`. |
|
| 132 |
#' @param n A [`numeric`] scalar specifying the number of equally sized segments |
|
| 133 |
#' between `lower` and `upper`. |
|
| 134 |
#' @param deg A [`numeric`] scalar specifyingthe degree of the splines, |
|
| 135 |
#' usually 1, 2, or 3 (the default). |
|
| 136 |
#' @return |
|
| 137 |
#' A `matrix` with `length(x)` rows and `n + deg` columns. |
|
| 138 |
#' @note |
|
| 139 |
#' Slightly modified from P. Eilers and B. Marx's [JOPS::bbase()]. |
|
| 140 |
#' @author P. Eilers, B. Marx |
|
| 141 |
#' @references |
|
| 142 |
#' Eilers, P. H. C. and Marx, B. D. (2021). *Practical Smoothing, The Joys of |
|
| 143 |
#' P-splines.* Cambridge: Cambridge University Press. |
|
| 144 |
#' @keywords internal |
|
| 145 |
#' @noRd |
|
| 146 |
bbase <- function(x, lower = min(x), upper = max(x), n = 10, deg = 3) {
|
|
| 147 |
## Check domain and adjust it if necessary |
|
| 148 | ! |
if (lower > min(x)) {
|
| 149 | ! |
lower <- min(x) |
| 150 | ! |
warning(sprintf(tr_("Left boundary adjusted to %g."), lower), call. = FALSE)
|
| 151 |
} |
|
| 152 | ! |
if (upper < max(x)) {
|
| 153 | ! |
upper <- max(x) |
| 154 | ! |
warning(sprintf(tr_("Right boundary adjusted to %g."), upper), call. = FALSE)
|
| 155 |
} |
|
| 156 | ||
| 157 |
## Function for B-spline basis |
|
| 158 | ! |
dx <- (upper - lower) / n |
| 159 | ! |
knots <- seq(lower - deg * dx, upper + deg * dx, by = dx) |
| 160 | ! |
P <- outer(x, knots, tpower, deg) |
| 161 | ! |
n <- dim(P)[[2L]] |
| 162 | ! |
D <- diff(diag(n), diff = deg + 1) / (gamma(deg + 1) * dx ^ deg) |
| 163 | ! |
B <- (-1) ^ (deg + 1) * P %*% t(D) |
| 164 | ||
| 165 |
## Make B-splines exactly zero beyond their end knots |
|
| 166 | ! |
nb <- ncol(B) |
| 167 | ! |
sk <- knots[(1:nb) + deg + 1] |
| 168 | ! |
mask <- outer(x, sk, '<') |
| 169 | ! |
B <- B * mask |
| 170 | ||
| 171 | ! |
B |
| 172 |
} |
|
| 173 | ||
| 174 |
#' Sparse B-spline Basis Matrix |
|
| 175 |
#' |
|
| 176 |
#' Computes a B-spline basis matrix using evenly spaced knots. |
|
| 177 |
#' @param x A [`numeric`] vector of values at which the B-spline basis functions |
|
| 178 |
#' are to be evaluated. |
|
| 179 |
#' @param lower A [`numeric`] vector giving the lower limit of the domain of `x`. |
|
| 180 |
#' @param upper A [`numeric`] vector giving the upper limit of the domain of `x`. |
|
| 181 |
#' @param n A [`numeric`] scalar specifying the number of equally sized segments |
|
| 182 |
#' between `lower` and `upper`. |
|
| 183 |
#' @param deg A [`numeric`] scalar specifyingthe degree of the splines, |
|
| 184 |
#' usually 1, 2, or 3 (the default). |
|
| 185 |
#' @return |
|
| 186 |
#' A [sparse matrix][Matrix::sparseMatrix()] with `length(x)` rows and |
|
| 187 |
#' `n + deg` columns. |
|
| 188 |
#' @note |
|
| 189 |
#' Slightly modified from P. Eilers [JOPS::spbase()]. |
|
| 190 |
#' @author P. Eilers, B. Marx |
|
| 191 |
#' @references |
|
| 192 |
#' Eilers, P. H. C. and Marx, B. D. (2021). *Practical Smoothing, The Joys of |
|
| 193 |
#' P-splines.* Cambridge: Cambridge University Press. |
|
| 194 |
#' @keywords internal |
|
| 195 |
#' @noRd |
|
| 196 |
sbase <- function(x, lower = min(x), upper = max(x), n = 10, deg = 3) {
|
|
| 197 |
## Check domain and adjust it if necessary |
|
| 198 | ! |
if (lower > min(x)) {
|
| 199 | ! |
lower <- min(x) |
| 200 | ! |
warning(sprintf(tr_("Left boundary adjusted to %g."), lower), call. = FALSE)
|
| 201 |
} |
|
| 202 | ! |
if (upper < max(x)) {
|
| 203 | ! |
upper <- max(x) |
| 204 | ! |
warning(sprintf(tr_("Right boundary adjusted to %g."), upper), call. = FALSE)
|
| 205 |
} |
|
| 206 | ||
| 207 |
## Reduce x to first interval between knots |
|
| 208 | ! |
dx <- (upper - lower) / n |
| 209 | ! |
ix <- floor((x - lower) / (1.0000001 * dx)) |
| 210 | ! |
xx <- (x - lower) - ix * dx |
| 211 | ||
| 212 |
## Full basis for reduced x |
|
| 213 | ! |
Br <- bbase(xx, lower = 0, upper = 0 + dx, n = 1, deg = deg) |
| 214 | ||
| 215 |
## Compute proper rows, columns |
|
| 216 | ! |
m <- length(x) |
| 217 | ! |
p <- ncol(Br) |
| 218 | ! |
i_row <- rep(seq_len(m), each = p) |
| 219 | ! |
i_col <- rep(seq_len(p), times = m) + rep(ix, each = p) |
| 220 | ||
| 221 |
## Sparse matrix |
|
| 222 | ! |
b <- as.vector(t(Br)) |
| 223 | ! |
Bs <- Matrix::sparseMatrix(i = i_row, j = i_col, x = b, dims = c(m, n + deg)) |
| 224 | ! |
Bs <- Bs[, -ncol(Bs), drop = FALSE] # ??? |
| 225 | ||
| 226 | ! |
Bs |
| 227 |
} |
|
| 228 | ||
| 229 |
#' @param y description |
|
| 230 |
#' @param B description |
|
| 231 |
#' @param C description |
|
| 232 |
#' @param D description |
|
| 233 |
#' @param beta description |
|
| 234 |
#' @param lambda description |
|
| 235 |
#' @author J. J. de Rooi *et al.* (original R code). |
|
| 236 |
#' @references |
|
| 237 |
#' de Rooi, J. J., van der Pers, N. M., Hendrikx, R. W. A., Delhez, R., |
|
| 238 |
#' Böttger A. J. and Eilers, P. H. C. (2014). Smoothing of X-ray diffraction |
|
| 239 |
#' data and Ka2 elimination using penalized likelihood and the composite link |
|
| 240 |
#' model. *Journal of Applied Crystallography*, 47: 852-860. |
|
| 241 |
#' \doi{10.1107/S1600576714005809}
|
|
| 242 |
#' @keywords internal |
|
| 243 |
#' @noRd |
|
| 244 |
penalized_strip_ka2 <- function(y, B, C, D, beta, lambda) {
|
|
| 245 | ! |
m <- length(y) |
| 246 | ||
| 247 |
## Penalty |
|
| 248 | ! |
P <- lambda * Matrix::t(D) %*% D |
| 249 | ||
| 250 |
## Fit iteratively |
|
| 251 | ! |
delta_beta <- 1 |
| 252 | ! |
limit <- 1 # Stop condition to prevent infinite loop |
| 253 | ! |
while (delta_beta >= 0.0001 & limit <= 30) {
|
| 254 | ! |
eta <- B %*% beta |
| 255 | ! |
gam <- exp(eta) |
| 256 | ! |
mu <- as.numeric(C %*% gam) |
| 257 | ||
| 258 | ! |
M <- Matrix::Diagonal(x = 1 / mu) |
| 259 | ! |
X <- (M %*% C %*% Matrix::Diagonal(x = as.numeric(gam))) %*% B |
| 260 | ! |
W <- Matrix::Diagonal(x = mu) |
| 261 | ! |
G <- Matrix::crossprod(X, W) %*% X |
| 262 | ||
| 263 | ! |
new_beta <- Matrix::solve(G + P, Matrix::crossprod(X, (y - mu)) + G %*% beta) |
| 264 | ! |
delta_beta <- max(abs(new_beta - beta)) |
| 265 | ! |
beta <- new_beta |
| 266 | ||
| 267 | ! |
limit <- limit + 1 |
| 268 |
} |
|
| 269 | ||
| 270 |
## AIC |
|
| 271 | ! |
H <- Matrix::solve(G + P, G) |
| 272 | ! |
ed <- sum(Matrix::diag(H)) |
| 273 | ! |
dev <- 2 * sum(y * log((y + (y == 0)) / mu)) |
| 274 | ! |
aic <- dev + 2 * ed |
| 275 | ||
| 276 |
## Standardized residuals for counts |
|
| 277 | ! |
sr <- (y - mu) / sqrt(mu) |
| 278 | ! |
sdr <- sqrt(sum(sr^2) / (m - ed)) |
| 279 | ||
| 280 |
## L-curve |
|
| 281 | ! |
phi <- log(sum((D %*% beta)^2)) |
| 282 | ! |
psi <- log(sum(sr^2)) |
| 283 | ||
| 284 |
## estimated signals |
|
| 285 | ! |
yhat <- gam[1:m] |
| 286 | ! |
yhat2 <- gam[(m + 1):length(gam)] * 0.5 |
| 287 | ||
| 288 |
## return values |
|
| 289 | ! |
list(yhat = yhat, yhat2 = yhat2, mu = mu, aic = aic, res = sr, |
| 290 | ! |
beta = beta, phi = phi, psi = psi) |
| 291 |
} |
| 1 |
# SMOOTHING |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Unweighted smoothing ========================================================= |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname smooth_rectangular |
|
| 8 |
#' @aliases smooth_rectangular,numeric,numeric-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "smooth_rectangular", |
|
| 11 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 12 |
definition = function(x, y, m = 3) {
|
|
| 13 | ! |
assert_length(y, length(x)) |
| 14 | ||
| 15 |
## Windows |
|
| 16 | ! |
index <- window_sliding(length(x), m) |
| 17 | ||
| 18 | ! |
y <- vapply( |
| 19 | ! |
X = index, |
| 20 | ! |
FUN = function(i, data) mean(data[i]), |
| 21 | ! |
FUN.VALUE = numeric(1), |
| 22 | ! |
data = y |
| 23 |
) |
|
| 24 | ||
| 25 | ! |
xy <- list(x = x, y = y) |
| 26 | ! |
attr(xy, "method") <- "unweighted smoothing" |
| 27 | ! |
xy |
| 28 |
} |
|
| 29 |
) |
|
| 30 | ||
| 31 |
#' @export |
|
| 32 |
#' @rdname smooth_rectangular |
|
| 33 |
#' @aliases smooth_rectangular,ANY,missing-method |
|
| 34 |
setMethod( |
|
| 35 |
f = "smooth_rectangular", |
|
| 36 |
signature = signature(x = "ANY", y = "missing"), |
|
| 37 |
definition = function(x, m) {
|
|
| 38 | ! |
xy <- grDevices::xy.coords(x) |
| 39 | ! |
methods::callGeneric(x = xy$x, y = xy$y, m = m) |
| 40 |
} |
|
| 41 |
) |
|
| 42 | ||
| 43 |
# Weighted smoothing =========================================================== |
|
| 44 |
#' @export |
|
| 45 |
#' @rdname smooth_triangular |
|
| 46 |
#' @aliases smooth_triangular,numeric,numeric-method |
|
| 47 |
setMethod( |
|
| 48 |
f = "smooth_triangular", |
|
| 49 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 50 |
definition = function(x, y, m = 3) {
|
|
| 51 |
# Validation |
|
| 52 | ! |
assert_length(y, length(x)) |
| 53 | ! |
assert_odd(m) |
| 54 | ||
| 55 |
# Index |
|
| 56 | ! |
k <- (m - 1) / 2 |
| 57 | ! |
index_k <- seq_len(k) |
| 58 | ! |
index_y <- seq_along(y) |
| 59 | ! |
index_m <- c(index_k, rep_len(k + 1, length(y) - 2 * k), rev(index_k)) - 1 |
| 60 | ||
| 61 | ! |
y <- mapply( |
| 62 | ! |
FUN = function(i, k, data) {
|
| 63 | ! |
j <- seq_len(k) |
| 64 | ! |
w <- c(j, k + 1, rev(j)) |
| 65 | ! |
index <- seq(from = i - k, to = i + k, by = 1) |
| 66 | ! |
stats::weighted.mean(x = data[index], w = w) |
| 67 |
}, |
|
| 68 | ! |
i = index_y, k = index_m, |
| 69 | ! |
MoreArgs = list(data = y) |
| 70 |
) |
|
| 71 | ||
| 72 | ! |
xy <- list(x = x, y = y) |
| 73 | ! |
attr(xy, "method") <- "weighted smoothing" |
| 74 | ! |
xy |
| 75 |
} |
|
| 76 |
) |
|
| 77 | ||
| 78 |
#' @export |
|
| 79 |
#' @rdname smooth_triangular |
|
| 80 |
#' @aliases smooth_triangular,ANY,missing-method |
|
| 81 |
setMethod( |
|
| 82 |
f = "smooth_triangular", |
|
| 83 |
signature = signature(x = "ANY", y = "missing"), |
|
| 84 |
definition = function(x, m) {
|
|
| 85 | ! |
xy <- grDevices::xy.coords(x) |
| 86 | ! |
methods::callGeneric(x = xy$x, y = xy$y, m = m) |
| 87 |
} |
|
| 88 |
) |
|
| 89 | ||
| 90 |
# Loess smoothing ============================================================== |
|
| 91 |
#' @export |
|
| 92 |
#' @rdname smooth_loess |
|
| 93 |
#' @aliases smooth_loess,numeric,numeric-method |
|
| 94 |
setMethod( |
|
| 95 |
f = "smooth_loess", |
|
| 96 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 97 |
definition = function(x, y, span = 0.75, ...) {
|
|
| 98 | ! |
assert_length(y, length(x)) |
| 99 | ||
| 100 | ! |
points <- data.frame(x, y) |
| 101 | ! |
fit <- stats::loess(y ~ x, data = points, span = span, ...) |
| 102 | ! |
bsl <- stats::predict(fit) |
| 103 | ||
| 104 | ! |
xy <- list(x = x, y = bsl) |
| 105 | ! |
attr(xy, "method") <- "loess smoothing" |
| 106 | ! |
xy |
| 107 |
} |
|
| 108 |
) |
|
| 109 | ||
| 110 |
#' @export |
|
| 111 |
#' @rdname smooth_loess |
|
| 112 |
#' @aliases smooth_loess,ANY,missing-method |
|
| 113 |
setMethod( |
|
| 114 |
f = "smooth_loess", |
|
| 115 |
signature = signature(x = "ANY", y = "missing"), |
|
| 116 |
definition = function(x, span = 0.75, ...) {
|
|
| 117 | ! |
xy <- grDevices::xy.coords(x) |
| 118 | ! |
methods::callGeneric(x = xy$x, y = xy$y, span = span, ...) |
| 119 |
} |
|
| 120 |
) |
|
| 121 | ||
| 122 |
# Savitzky-Golay filter ======================================================== |
|
| 123 |
#' @export |
|
| 124 |
#' @rdname smooth_savitzky |
|
| 125 |
#' @aliases smooth_savitzky,numeric,numeric-method |
|
| 126 |
setMethod( |
|
| 127 |
f = "smooth_savitzky", |
|
| 128 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 129 |
definition = function(x, y, m = 3, p = 2) {
|
|
| 130 |
## Validation |
|
| 131 | ! |
assert_length(y, length(x)) |
| 132 | ! |
assert_odd(m) |
| 133 | ||
| 134 | ! |
k <- (m - 1) / 2 |
| 135 | ! |
i <- seq(from = -k, to = k, by = 1) |
| 136 | ! |
j <- utils::head(utils::tail(seq_along(y), n = -k), n = -k) |
| 137 | ! |
conv <- coef_savitzky(m, p) |
| 138 | ||
| 139 | ! |
smoothed <- vapply( |
| 140 | ! |
X = j, |
| 141 | ! |
FUN = function(j, i, conv, data) {
|
| 142 | ! |
sum(conv * data[j + i]) |
| 143 |
}, |
|
| 144 | ! |
FUN.VALUE = double(1), |
| 145 | ! |
i = i, |
| 146 | ! |
conv = conv, |
| 147 | ! |
data = y |
| 148 |
) |
|
| 149 | ! |
y[j] <- smoothed |
| 150 | ||
| 151 | ! |
xy <- list(x = x, y = y) |
| 152 | ! |
attr(xy, "method") <- "Savitzky-Golay filter" |
| 153 | ! |
xy |
| 154 |
} |
|
| 155 |
) |
|
| 156 | ||
| 157 |
#' @export |
|
| 158 |
#' @rdname smooth_savitzky |
|
| 159 |
#' @aliases smooth_savitzky,ANY,missing-method |
|
| 160 |
setMethod( |
|
| 161 |
f = "smooth_savitzky", |
|
| 162 |
signature = signature(x = "ANY", y = "missing"), |
|
| 163 |
definition = function(x, m, p) {
|
|
| 164 | ! |
xy <- grDevices::xy.coords(x) |
| 165 | ! |
methods::callGeneric(x = xy$x, y = xy$y, m = m, p = p) |
| 166 |
} |
|
| 167 |
) |
|
| 168 | ||
| 169 |
coef_savitzky <- function(m, p = 2) {
|
|
| 170 | ! |
k <- (m - 1) / 2 |
| 171 | ! |
z <- seq(from = -k, to = k, by = 1) |
| 172 | ! |
J <- vapply(X = c(0, p), FUN = function(p, z) z^p, z, FUN.VALUE = double(m)) |
| 173 | ! |
(solve(t(J) %*% J) %*% t(J))[1, , drop = TRUE] |
| 174 |
} |
|
| 175 | ||
| 176 |
# Whittaker smoothing ========================================================== |
|
| 177 |
#' @export |
|
| 178 |
#' @rdname smooth_whittaker |
|
| 179 |
#' @aliases smooth_whittaker,numeric,numeric-method |
|
| 180 |
setMethod( |
|
| 181 |
f = "smooth_whittaker", |
|
| 182 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 183 |
definition = function(x, y, lambda = 1600, d = 2, sparse = FALSE) {
|
|
| 184 | 1x |
assert_length(y, length(x)) |
| 185 | 1x |
m <- length(y) |
| 186 | ||
| 187 | 1x |
if (sparse) {
|
| 188 | 1x |
assert_Matrix() |
| 189 | ||
| 190 | 1x |
E <- Matrix::Diagonal(m) |
| 191 | 1x |
D <- Matrix::diff(E, lag = 1, differences = d) |
| 192 | 1x |
B <- Matrix::chol(E + (lambda * Matrix::t(D) %*% D)) |
| 193 | 1x |
z <- Matrix::solve(B, Matrix::solve(Matrix::t(B), y)) |
| 194 |
## Prior to v1.6, Matrix::solve(a=<Matrix>, b=<vector>) returns a matrix |
|
| 195 | 1x |
z <- as.numeric(z) |
| 196 |
} else {
|
|
| 197 | ! |
E <- diag(m) |
| 198 | ! |
D <- diff(E, lag = 1, differences = d) |
| 199 | ! |
B <- E + (lambda * t(D) %*% D) |
| 200 | ! |
z <- solve(B, y) |
| 201 |
} |
|
| 202 | ||
| 203 | 1x |
xy <- list(x = x, y = z) |
| 204 | 1x |
attr(xy, "method") <- "Whittaker smoothing" |
| 205 | 1x |
xy |
| 206 |
} |
|
| 207 |
) |
|
| 208 | ||
| 209 |
#' @export |
|
| 210 |
#' @rdname smooth_whittaker |
|
| 211 |
#' @aliases smooth_whittaker,ANY,missing-method |
|
| 212 |
setMethod( |
|
| 213 |
f = "smooth_whittaker", |
|
| 214 |
signature = signature(x = "ANY", y = "missing"), |
|
| 215 |
definition = function(x, lambda = 1600, d = 2, sparse = FALSE) {
|
|
| 216 | ! |
xy <- grDevices::xy.coords(x) |
| 217 | ! |
methods::callGeneric(x = xy$x, y = xy$y, lambda = lambda, d = d, |
| 218 | ! |
sparse = sparse) |
| 219 |
} |
|
| 220 |
) |
|
| 221 | ||
| 222 |
# Penalized likelihood smoothing =============================================== |
|
| 223 |
#' @export |
|
| 224 |
#' @rdname smooth_likelihood |
|
| 225 |
#' @aliases smooth_likelihood,numeric,numeric-method |
|
| 226 |
setMethod( |
|
| 227 |
f = "smooth_likelihood", |
|
| 228 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 229 |
definition = function(x, y, lambda, d = 2, SE = FALSE, |
|
| 230 |
progress = interactive()) {
|
|
| 231 | ! |
assert_Matrix() |
| 232 | ! |
assert_length(y, length(x)) |
| 233 | ||
| 234 | ! |
m <- length(y) |
| 235 | ! |
n <- length(lambda) |
| 236 | ||
| 237 | ! |
aic <- rep(0, n) |
| 238 | ! |
mu <- matrix(data = 0, nrow = m, ncol = n) |
| 239 | ! |
se <- matrix(data = 0, nrow = m, ncol = n) |
| 240 | ! |
res <- matrix(data = 0, nrow = m, ncol = n) |
| 241 | ||
| 242 | ! |
if (progress) pb <- utils::txtProgressBar(min = 0, max = n, style=3) |
| 243 | ||
| 244 | ! |
n_lambda <- seq_len(n) |
| 245 | ! |
for (i in n_lambda) {
|
| 246 | ! |
like <- penalized_likelihood(y, lambda = lambda[i], d = d, SE = SE) |
| 247 | ||
| 248 | ! |
aic[i] <- like$aic |
| 249 | ! |
mu[, i] <- like$mu |
| 250 | ! |
se[, i] <- like$se |
| 251 | ! |
res[, i] <- like$res |
| 252 | ||
| 253 | ! |
if (progress) utils::setTxtProgressBar(pb, i) |
| 254 |
} |
|
| 255 | ||
| 256 | ! |
if (progress) close(pb) |
| 257 | ||
| 258 | ! |
k <- which.min(aic) |
| 259 | ! |
mu <- mu[, k] |
| 260 | ! |
se <- se[, k] |
| 261 | ! |
res <- res[, k] |
| 262 | ! |
op_lambda <- lambda[k] |
| 263 | ! |
op_aic <- aic[k] |
| 264 | ||
| 265 | ! |
xy <- list(x = x, y = mu, aic = aic, se = se, residuals = res, |
| 266 | ! |
optimal_aic = op_aic, optimal_lambda = op_lambda) |
| 267 | ! |
attr(xy, "method") <- "penalized likelihood smoothing" |
| 268 | ! |
xy |
| 269 |
} |
|
| 270 |
) |
|
| 271 | ||
| 272 |
#' @export |
|
| 273 |
#' @rdname smooth_likelihood |
|
| 274 |
#' @aliases smooth_likelihood,ANY,missing-method |
|
| 275 |
setMethod( |
|
| 276 |
f = "smooth_likelihood", |
|
| 277 |
signature = signature(x = "ANY", y = "missing"), |
|
| 278 |
definition = function(x, lambda, d = 2, SE = FALSE, |
|
| 279 |
progress = interactive()) {
|
|
| 280 | ! |
xy <- grDevices::xy.coords(x) |
| 281 | ! |
methods::callGeneric(x = xy$x, y = xy$y, lambda = lambda, d = d, |
| 282 | ! |
SE = SE, progress = progress) |
| 283 |
} |
|
| 284 |
) |
|
| 285 | ||
| 286 |
penalized_likelihood <- function(y, lambda, d = 2, SE = FALSE) {
|
|
| 287 | ||
| 288 | ! |
m <- length(y) |
| 289 | ! |
z <- log(y + 1) |
| 290 | ! |
E <- Matrix::Diagonal(m) |
| 291 | ! |
D <- Matrix::diff(E, differences = d) |
| 292 | ! |
P <- lambda * Matrix::crossprod(D, D) |
| 293 | ! |
mu <- exp(z) |
| 294 | ||
| 295 | ! |
delta_mu <- 1 |
| 296 | ! |
limit <- 1 # Stop condition to prevent infinite loop |
| 297 | ! |
while (delta_mu >= 0.0001 & limit <= 10) {
|
| 298 | ! |
W <- Matrix::Diagonal(x = mu) |
| 299 | ! |
z <- Matrix::solve(W + P, y - mu + mu * z) |
| 300 | ! |
new_mu <- exp(as.numeric(z)) # Drop S4 class |
| 301 | ! |
delta_mu <- max(abs(new_mu - mu)) |
| 302 | ! |
mu <- new_mu |
| 303 | ||
| 304 | ! |
limit <- limit + 1 |
| 305 |
} |
|
| 306 | ||
| 307 |
## AIC |
|
| 308 | ! |
H <- Matrix::solve(W + P) %*% W |
| 309 | ! |
ed <- sum(Matrix::diag(H)) |
| 310 | ! |
dev <- 2 * sum(y * log((y + 1e-10) / mu)) |
| 311 | ! |
aic <- dev + 2 * ed * m / (m - ed) |
| 312 | ||
| 313 |
## Standard error |
|
| 314 | ! |
se <- rep(NA_real_, m) |
| 315 | ! |
if (isTRUE(SE)) {
|
| 316 | ! |
HH <- Matrix::tcrossprod(H %*% W, H) |
| 317 | ! |
se <- sqrt(Matrix::diag(HH)) |
| 318 |
} |
|
| 319 | ||
| 320 |
## Standardized residuals for counts |
|
| 321 | ! |
res <- (y - mu) / sqrt(mu) |
| 322 | ||
| 323 | ! |
list(mu = mu, aic = aic, se = se, res = res) |
| 324 |
} |
| 1 |
# SIGNAL PROCESSING |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Bind ========================================================================= |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname signal_bind |
|
| 8 |
#' @aliases signal_bind,ANY-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "signal_bind", |
|
| 11 |
signature = "ANY", |
|
| 12 |
definition = function(...) {
|
|
| 13 | ||
| 14 | ! |
signal <- list(...) |
| 15 | ! |
coords <- lapply(X = signal, FUN = grDevices::xy.coords) |
| 16 | ||
| 17 | ! |
y <- lapply(X = coords, FUN = `[[`, i = "y") |
| 18 | ! |
ny <- lengths(y) |
| 19 | ! |
if (any(ny != mean(ny))) {
|
| 20 | ! |
msg <- tr_("All objects must have the same number of data points.")
|
| 21 | ! |
stop(msg, call. = FALSE) |
| 22 |
} |
|
| 23 | ! |
y <- do.call(rbind, y) |
| 24 | ||
| 25 |
## Get names |
|
| 26 | ! |
subst <- substitute(list(...))[-1] |
| 27 | ! |
arg_names <- vapply(X = subst, FUN = deparse, FUN.VALUE = character(1)) |
| 28 | ! |
rownames(y) <- arg_names |
| 29 | ! |
y |
| 30 |
} |
|
| 31 |
) |
|
| 32 | ||
| 33 |
# Mean ========================================================================= |
|
| 34 |
#' @export |
|
| 35 |
#' @rdname signal_mean |
|
| 36 |
#' @aliases signal_mean,ANY-method |
|
| 37 |
setMethod( |
|
| 38 |
f = "signal_mean", |
|
| 39 |
signature = "ANY", |
|
| 40 |
definition = function(...) {
|
|
| 41 | ||
| 42 | ! |
signal <- list(...) |
| 43 | ! |
coords <- lapply(X = signal, FUN = grDevices::xy.coords) |
| 44 | ||
| 45 | ! |
x <- lapply(X = coords, FUN = `[[`, i = "x") |
| 46 | ! |
nx <- lengths(x) |
| 47 | ||
| 48 | ! |
y <- lapply(X = coords, FUN = `[[`, i = "y") |
| 49 | ! |
ny <- lengths(y) |
| 50 | ||
| 51 | ! |
if (any(nx != mean(nx)) | any(ny != mean(ny))) {
|
| 52 | ! |
msg <- tr_("All objects must have the same number of data points.")
|
| 53 | ! |
stop(msg, call. = FALSE) |
| 54 |
} |
|
| 55 | ||
| 56 | ! |
x <- do.call(cbind, x) |
| 57 | ! |
x <- rowMeans(x) |
| 58 | ! |
y <- do.call(cbind, y) |
| 59 | ! |
y <- rowMeans(y) |
| 60 | ||
| 61 | ! |
xy <- list(x = x, y = y) |
| 62 | ! |
xy |
| 63 |
} |
|
| 64 |
) |
|
| 65 | ||
| 66 |
# Subset ======================================================================= |
|
| 67 |
#' @export |
|
| 68 |
#' @rdname subset |
|
| 69 |
#' @aliases signal_select,numeric,numeric-method |
|
| 70 |
setMethod( |
|
| 71 |
f = "signal_select", |
|
| 72 |
signature = c(x = "numeric", y = "numeric"), |
|
| 73 |
definition = function(x, y, from, to) {
|
|
| 74 | 2x |
assert_length(y, length(x)) |
| 75 | ||
| 76 |
## Find the nearest value |
|
| 77 | 2x |
from <- x[which_nearest(x, from)] |
| 78 | 2x |
to <- x[which_nearest(x, to)] |
| 79 | 2x |
idx <- which(x >= from & x <= to) |
| 80 | ||
| 81 | 2x |
xy <- list(x = x[idx], y = y[idx]) |
| 82 | 2x |
xy |
| 83 |
} |
|
| 84 |
) |
|
| 85 | ||
| 86 |
#' @export |
|
| 87 |
#' @rdname subset |
|
| 88 |
#' @aliases signal_select,ANY,missing-method |
|
| 89 |
setMethod( |
|
| 90 |
f = "signal_select", |
|
| 91 |
signature = c(x = "ANY", y = "missing"), |
|
| 92 |
definition = function(x, from, to) {
|
|
| 93 | 2x |
xy <- grDevices::xy.coords(x) |
| 94 | 2x |
methods::callGeneric(x = xy$x, y = xy$y, from = from, to = to) |
| 95 |
} |
|
| 96 |
) |
|
| 97 | ||
| 98 |
#' @export |
|
| 99 |
#' @rdname subset |
|
| 100 |
#' @aliases signal_slice,numeric,numeric-method |
|
| 101 |
setMethod( |
|
| 102 |
f = "signal_slice", |
|
| 103 |
signature = c(x = "numeric", y = "numeric"), |
|
| 104 |
definition = function(x, y, subset) {
|
|
| 105 | 1x |
assert_length(y, length(x)) |
| 106 | ||
| 107 | 1x |
i <- as.integer(subset) |
| 108 | ||
| 109 | 1x |
if (!all(i > 0) & !all(i < 0)) {
|
| 110 | ! |
msg <- tr_("A vector of strictly positive or negative integers is expected.")
|
| 111 | ! |
stop(msg, call. = FALSE) |
| 112 |
} |
|
| 113 | ||
| 114 | 1x |
xy <- list(x = x[i], y = y[i]) |
| 115 | 1x |
xy |
| 116 |
} |
|
| 117 |
) |
|
| 118 | ||
| 119 |
#' @export |
|
| 120 |
#' @rdname subset |
|
| 121 |
#' @aliases signal_slice,ANY,missing-method |
|
| 122 |
setMethod( |
|
| 123 |
f = "signal_slice", |
|
| 124 |
signature = c(x = "ANY", y = "missing"), |
|
| 125 |
definition = function(x, subset) {
|
|
| 126 | 1x |
xy <- grDevices::xy.coords(x) |
| 127 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, subset = subset) |
| 128 |
} |
|
| 129 |
) |
|
| 130 | ||
| 131 |
# Shift ======================================================================== |
|
| 132 |
#' @export |
|
| 133 |
#' @rdname signal_shift |
|
| 134 |
#' @aliases signal_shift,numeric,numeric-method |
|
| 135 |
setMethod( |
|
| 136 |
f = "signal_shift", |
|
| 137 |
signature = c(x = "numeric", y = "numeric"), |
|
| 138 |
definition = function(x, y, lag) {
|
|
| 139 | 1x |
assert_length(y, length(x)) |
| 140 | ||
| 141 | 1x |
x <- x + lag |
| 142 | 1x |
xy <- list(x = x, y = y) |
| 143 | 1x |
xy |
| 144 |
} |
|
| 145 |
) |
|
| 146 | ||
| 147 |
#' @export |
|
| 148 |
#' @rdname signal_shift |
|
| 149 |
#' @aliases signal_shift,ANY,missing-method |
|
| 150 |
setMethod( |
|
| 151 |
f = "signal_shift", |
|
| 152 |
signature = c(x = "ANY", y = "missing"), |
|
| 153 |
definition = function(x, lag) {
|
|
| 154 | 1x |
xy <- grDevices::xy.coords(x) |
| 155 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, lag = lag) |
| 156 |
} |
|
| 157 |
) |
|
| 158 | ||
| 159 |
# Drift ======================================================================== |
|
| 160 |
#' @export |
|
| 161 |
#' @rdname signal_drift |
|
| 162 |
#' @aliases signal_drift,numeric,numeric,numeric-method |
|
| 163 |
setMethod( |
|
| 164 |
f = "signal_drift", |
|
| 165 |
signature = c(x = "numeric", y = "numeric", lag = "numeric"), |
|
| 166 |
definition = function(x, y, lag) {
|
|
| 167 | 2x |
assert_length(y, length(x)) |
| 168 | ||
| 169 | 2x |
y <- y + lag |
| 170 | 2x |
xy <- list(x = x, y = y) |
| 171 | 2x |
xy |
| 172 |
} |
|
| 173 |
) |
|
| 174 | ||
| 175 |
#' @export |
|
| 176 |
#' @rdname signal_drift |
|
| 177 |
#' @aliases signal_drift,ANY,missing,ANY-method |
|
| 178 |
setMethod( |
|
| 179 |
f = "signal_drift", |
|
| 180 |
signature = c(x = "ANY", y = "missing", lag = "ANY"), |
|
| 181 |
definition = function(x, lag, subtract = FALSE) {
|
|
| 182 | 2x |
xy <- grDevices::xy.coords(x) |
| 183 | 2x |
zz <- grDevices::xy.coords(lag) |
| 184 | 2x |
lag <- if (subtract) -zz$y else zz$y |
| 185 | 2x |
methods::callGeneric(x = xy$x, y = xy$y, lag = lag) |
| 186 |
} |
|
| 187 |
) |
|
| 188 | ||
| 189 |
# Correct ====================================================================== |
|
| 190 |
#' @export |
|
| 191 |
#' @rdname signal_correct |
|
| 192 |
#' @aliases signal_correct,numeric,numeric-method |
|
| 193 |
setMethod( |
|
| 194 |
f = "signal_correct", |
|
| 195 |
signature = c(x = "numeric", y = "numeric"), |
|
| 196 |
definition = function(x, y, method = c("linear", "polynomial", "asls",
|
|
| 197 |
"rollingball", "rubberband", |
|
| 198 |
"SNIP", "4S"), ...) {
|
|
| 199 |
## Validation |
|
| 200 | ! |
method <- match.arg(method, several.ok = FALSE) |
| 201 | ||
| 202 |
## Get method |
|
| 203 | ! |
f <- switch( |
| 204 | ! |
method, |
| 205 | ! |
asls = baseline_asls, |
| 206 | ! |
linear = baseline_linear, |
| 207 | ! |
polynomial = baseline_polynomial, |
| 208 | ! |
rollingball = baseline_rollingball, |
| 209 | ! |
rubberband = baseline_rubberband, |
| 210 | ! |
SNIP = baseline_snip, |
| 211 | ! |
`4S` = baseline_peakfilling |
| 212 |
) |
|
| 213 | ||
| 214 |
## Baseline estimation |
|
| 215 | ! |
bsl <- f(x = x, y = y, ...) |
| 216 | ||
| 217 | ! |
xy <- list(x = x, y = y - bsl$y) |
| 218 | ! |
attr(xy, "method") <- method |
| 219 | ! |
xy |
| 220 |
} |
|
| 221 |
) |
|
| 222 | ||
| 223 |
#' @export |
|
| 224 |
#' @rdname signal_correct |
|
| 225 |
#' @aliases signal_correct,ANY,missing-method |
|
| 226 |
setMethod( |
|
| 227 |
f = "signal_correct", |
|
| 228 |
signature = c(x = "ANY", y = "missing"), |
|
| 229 |
definition = function(x, method = c("linear", "polynomial", "asls",
|
|
| 230 |
"rollingball", "rubberband", |
|
| 231 |
"SNIP", "4S"), ...) {
|
|
| 232 | ! |
xy <- grDevices::xy.coords(x) |
| 233 | ! |
methods::callGeneric(x = xy$x, y = xy$y, method = method, ...) |
| 234 |
} |
|
| 235 |
) |
| 1 |
# SCALING |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Area ========================================================================= |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname rescale_area |
|
| 8 |
#' @aliases rescale_area,numeric,numeric-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "rescale_area", |
|
| 11 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 12 |
definition = function(x, y, method = c("rectangle", "trapezoid"), ...) {
|
|
| 13 |
## Validation |
|
| 14 | 1x |
assert_length(y, length(x)) |
| 15 | 1x |
method <- match.arg(method, several.ok = FALSE) |
| 16 | ||
| 17 |
## Get method |
|
| 18 | 1x |
f <- switch( |
| 19 | 1x |
method, |
| 20 | 1x |
rectangle = integrate_rectangle, |
| 21 | 1x |
trapezoid = integrate_trapezoid |
| 22 |
) |
|
| 23 | ||
| 24 |
## Baseline estimation |
|
| 25 | 1x |
auc <- f(x = x, y = y, ...) |
| 26 | 1x |
y <- y / auc |
| 27 | ||
| 28 | 1x |
xy <- list(x = x, y = y) |
| 29 | 1x |
xy |
| 30 |
} |
|
| 31 |
) |
|
| 32 | ||
| 33 |
#' @export |
|
| 34 |
#' @rdname rescale_area |
|
| 35 |
#' @aliases rescale_area,ANY,missing-method |
|
| 36 |
setMethod( |
|
| 37 |
f = "rescale_area", |
|
| 38 |
signature = signature(x = "ANY", y = "missing"), |
|
| 39 |
definition = function(x, method = c("rectangle", "trapezoid"), ...) {
|
|
| 40 | 1x |
xy <- grDevices::xy.coords(x) |
| 41 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, method = method, ...) |
| 42 |
} |
|
| 43 |
) |
|
| 44 | ||
| 45 |
# Total ======================================================================== |
|
| 46 |
#' @export |
|
| 47 |
#' @rdname rescale_total |
|
| 48 |
#' @aliases rescale_total,numeric,numeric-method |
|
| 49 |
setMethod( |
|
| 50 |
f = "rescale_total", |
|
| 51 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 52 |
definition = function(x, y, total = 1) {
|
|
| 53 | 1x |
assert_length(y, length(x)) |
| 54 | ||
| 55 | 1x |
y <- (y * total) / sum(abs(y)) |
| 56 | 1x |
xy <- list(x = x, y = y) |
| 57 | 1x |
xy |
| 58 |
} |
|
| 59 |
) |
|
| 60 | ||
| 61 |
#' @export |
|
| 62 |
#' @rdname rescale_total |
|
| 63 |
#' @aliases rescale_total,ANY,missing-method |
|
| 64 |
setMethod( |
|
| 65 |
f = "rescale_total", |
|
| 66 |
signature = signature(x = "ANY", y = "missing"), |
|
| 67 |
definition = function(x, total = 1) {
|
|
| 68 | 1x |
xy <- grDevices::xy.coords(x) |
| 69 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, total = total) |
| 70 |
} |
|
| 71 |
) |
|
| 72 | ||
| 73 |
# Min-Max ====================================================================== |
|
| 74 |
#' @export |
|
| 75 |
#' @rdname rescale_range |
|
| 76 |
#' @aliases rescale_range,numeric,numeric-method |
|
| 77 |
setMethod( |
|
| 78 |
f = "rescale_range", |
|
| 79 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 80 |
definition = function(x, y, min = 0, max = 1) {
|
|
| 81 |
## Validation |
|
| 82 | 5x |
assert_length(y, length(x)) |
| 83 | 5x |
if (min > max) {
|
| 84 | 1x |
msg <- sprintf(tr_("%s (%g) must be lower than %s (%g)."),
|
| 85 | 1x |
sQuote("min"), min, sQuote("max"), max)
|
| 86 | 1x |
stop(msg, call. = FALSE) |
| 87 |
} |
|
| 88 | ||
| 89 | 4x |
y <- (y - min(y)) / (max(y) - min(y)) * (max - min) + min |
| 90 | 4x |
xy <- list(x = x, y = y) |
| 91 | 4x |
xy |
| 92 |
} |
|
| 93 |
) |
|
| 94 | ||
| 95 |
#' @export |
|
| 96 |
#' @rdname rescale_range |
|
| 97 |
#' @aliases rescale_range,ANY,missing-method |
|
| 98 |
setMethod( |
|
| 99 |
f = "rescale_range", |
|
| 100 |
signature = signature(x = "ANY", y = "missing"), |
|
| 101 |
definition = function(x, min = 0, max = 1) {
|
|
| 102 | 1x |
xy <- grDevices::xy.coords(x) |
| 103 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, min = min, max = max) |
| 104 |
} |
|
| 105 |
) |
|
| 106 | ||
| 107 |
#' @export |
|
| 108 |
#' @rdname rescale_range |
|
| 109 |
#' @aliases rescale_min,numeric,numeric-method |
|
| 110 |
setMethod( |
|
| 111 |
f = "rescale_min", |
|
| 112 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 113 |
definition = function(x, y, min = 0) {
|
|
| 114 | 2x |
rescale_range(x, y, min = min, max = max(y)) |
| 115 |
} |
|
| 116 |
) |
|
| 117 | ||
| 118 |
#' @export |
|
| 119 |
#' @rdname rescale_range |
|
| 120 |
#' @aliases rescale_min,ANY,missing-method |
|
| 121 |
setMethod( |
|
| 122 |
f = "rescale_min", |
|
| 123 |
signature = signature(x = "ANY", y = "missing"), |
|
| 124 |
definition = function(x, min = 0) {
|
|
| 125 | 2x |
xy <- grDevices::xy.coords(x) |
| 126 | 2x |
methods::callGeneric(x = xy$x, y = xy$y, min = min) |
| 127 |
} |
|
| 128 |
) |
|
| 129 | ||
| 130 |
#' @export |
|
| 131 |
#' @rdname rescale_range |
|
| 132 |
#' @aliases rescale_max,numeric,numeric-method |
|
| 133 |
setMethod( |
|
| 134 |
f = "rescale_max", |
|
| 135 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 136 |
definition = function(x, y, max = 1) {
|
|
| 137 | 2x |
rescale_range(x, y, min = min(y), max = max) |
| 138 |
} |
|
| 139 |
) |
|
| 140 | ||
| 141 |
#' @export |
|
| 142 |
#' @rdname rescale_range |
|
| 143 |
#' @aliases rescale_max,ANY,missing-method |
|
| 144 |
setMethod( |
|
| 145 |
f = "rescale_max", |
|
| 146 |
signature = signature(x = "ANY", y = "missing"), |
|
| 147 |
definition = function(x, max = 1) {
|
|
| 148 | 2x |
xy <- grDevices::xy.coords(x) |
| 149 | 2x |
methods::callGeneric(x = xy$x, y = xy$y, max = max) |
| 150 |
} |
|
| 151 |
) |
|
| 152 | ||
| 153 |
# Transform ==================================================================== |
|
| 154 |
#' @export |
|
| 155 |
#' @rdname rescale_transform |
|
| 156 |
#' @aliases rescale_transform,numeric,numeric-method |
|
| 157 |
setMethod( |
|
| 158 |
f = "rescale_transform", |
|
| 159 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 160 |
definition = function(x, y, f, ...) {
|
|
| 161 | 1x |
assert_length(y, length(x)) |
| 162 | ||
| 163 | 1x |
y <- f(y, ...) |
| 164 | 1x |
xy <- list(x = x, y = y) |
| 165 | 1x |
xy |
| 166 |
} |
|
| 167 |
) |
|
| 168 | ||
| 169 |
#' @export |
|
| 170 |
#' @rdname rescale_transform |
|
| 171 |
#' @aliases rescale_transform,ANY,missing-method |
|
| 172 |
setMethod( |
|
| 173 |
f = "rescale_transform", |
|
| 174 |
signature = signature(x = "ANY", y = "missing"), |
|
| 175 |
definition = function(x, f, ...) {
|
|
| 176 | 1x |
xy <- grDevices::xy.coords(x) |
| 177 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, f = f, ...) |
| 178 |
} |
|
| 179 |
) |
|
| 180 | ||
| 181 |
# SNV ========================================================================== |
|
| 182 |
#' @export |
|
| 183 |
#' @rdname rescale_snv |
|
| 184 |
#' @aliases rescale_snv,numeric,numeric-method |
|
| 185 |
setMethod( |
|
| 186 |
f = "rescale_snv", |
|
| 187 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 188 |
definition = function(x, y, ...) {
|
|
| 189 |
## Validation |
|
| 190 | 1x |
assert_length(y, length(x)) |
| 191 | ||
| 192 | 1x |
y <- (y - mean(y)) / stats::sd(y) |
| 193 | ||
| 194 | 1x |
xy <- list(x = x, y = y) |
| 195 | 1x |
xy |
| 196 |
} |
|
| 197 |
) |
|
| 198 | ||
| 199 |
#' @export |
|
| 200 |
#' @rdname rescale_snv |
|
| 201 |
#' @aliases rescale_snv,ANY,missing-method |
|
| 202 |
setMethod( |
|
| 203 |
f = "rescale_snv", |
|
| 204 |
signature = signature(x = "ANY", y = "missing"), |
|
| 205 |
definition = function(x, ...) {
|
|
| 206 | 1x |
xy <- grDevices::xy.coords(x) |
| 207 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, ...) |
| 208 |
} |
|
| 209 |
) |
| 1 |
# GENERIC METHODS |
|
| 2 | ||
| 3 |
# Baseline ===================================================================== |
|
| 4 |
#' Asymmetric Least Squares Smoothing |
|
| 5 |
#' |
|
| 6 |
#' Baseline estimation with asymmetric least squares smoothing. |
|
| 7 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 8 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 9 |
#' @param p A length-one [`numeric`] vector giving the asymmetry |
|
| 10 |
#' (\eqn{0.001 < p < 0.1} is a good choice for a signal with positive peaks).
|
|
| 11 |
#' @param lambda A length-one [`numeric`] vector giving the smoothing parameter. |
|
| 12 |
#' @param stop An [`integer`] giving the stopping rule (i.e. maximum number of |
|
| 13 |
#' iterations). |
|
| 14 |
#' @param ... Currently not used. |
|
| 15 |
#' @return |
|
| 16 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 17 |
#' @references |
|
| 18 |
#' Eilers, P. H. C. & Boelens, H. F. M. (2005). *Baseline Correction with |
|
| 19 |
#' Asymmetric Least Squares Smoothing*. |
|
| 20 |
#' @seealso [signal_correct()] |
|
| 21 |
#' @example inst/examples/ex-baseline-asls.R |
|
| 22 |
#' @author P. H. C. Eilers and H. F. M. Boelens (original Matlab code) |
|
| 23 |
#' @docType methods |
|
| 24 |
#' @family baseline estimation methods |
|
| 25 |
#' @aliases baseline_asls-method |
|
| 26 |
setGeneric( |
|
| 27 |
name = "baseline_asls", |
|
| 28 | 2x |
def = function(x, y, ...) standardGeneric("baseline_asls")
|
| 29 |
) |
|
| 30 | ||
| 31 |
#' Linear Baseline Estimation |
|
| 32 |
#' |
|
| 33 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 34 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 35 |
#' @param points A [`numeric`] vector specifying the data points to be used in |
|
| 36 |
#' the fitting process (in `x` unit). |
|
| 37 |
#' @param ... Currently not used. |
|
| 38 |
#' @return |
|
| 39 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 40 |
#' @seealso [signal_correct()] |
|
| 41 |
#' @example inst/examples/ex-baseline-linear.R |
|
| 42 |
#' @author N. Frerebeau |
|
| 43 |
#' @docType methods |
|
| 44 |
#' @family baseline estimation methods |
|
| 45 |
#' @aliases baseline_linear-method |
|
| 46 |
setGeneric( |
|
| 47 |
name = "baseline_linear", |
|
| 48 | 2x |
def = function(x, y, ...) standardGeneric("baseline_linear")
|
| 49 |
) |
|
| 50 | ||
| 51 |
#' Polynomial Baseline Estimation |
|
| 52 |
#' |
|
| 53 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 54 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 55 |
#' @param d An [`integer`] giving the degree of the polynomial. Must be less |
|
| 56 |
#' than the number of unique points. |
|
| 57 |
#' @param tolerance A [`numeric`] scalar giving the tolerance of difference |
|
| 58 |
#' between iterations. |
|
| 59 |
#' @param stop An [`integer`] giving the stopping rule (i.e. maximum number of |
|
| 60 |
#' iterations). |
|
| 61 |
#' @param ... Currently not used. |
|
| 62 |
#' @return |
|
| 63 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 64 |
#' @seealso [signal_correct()] |
|
| 65 |
#' @references |
|
| 66 |
#' Lieber, C. A. and Mahadevan-Jansen, A. (2003). Automated Method for |
|
| 67 |
#' Subtraction of Fluorescence from Biological Raman Spectra. *Applied |
|
| 68 |
#' Spectroscopy*, 57(11): 1363-67. \doi{10.1366/000370203322554518}.
|
|
| 69 |
#' @example inst/examples/ex-baseline-polynomial.R |
|
| 70 |
#' @author N. Frerebeau |
|
| 71 |
#' @docType methods |
|
| 72 |
#' @family baseline estimation methods |
|
| 73 |
#' @aliases baseline_polynomial-method |
|
| 74 |
setGeneric( |
|
| 75 |
name = "baseline_polynomial", |
|
| 76 | 2x |
def = function(x, y, ...) standardGeneric("baseline_polynomial")
|
| 77 |
) |
|
| 78 | ||
| 79 |
#' Rolling Ball Baseline Estimation |
|
| 80 |
#' |
|
| 81 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 82 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 83 |
#' @param m An odd [`integer`] giving the window size (i.e. the number of |
|
| 84 |
#' adjacent points to be used; see [`window_sliding()`]) for |
|
| 85 |
#' minimization/maximization. |
|
| 86 |
#' @param s An odd [`integer`] giving the window size (i.e. the number of |
|
| 87 |
#' adjacent points to be used; see [`window_sliding()`]) for smoothing. |
|
| 88 |
#' @param ... Currently not used. |
|
| 89 |
#' @note |
|
| 90 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 91 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 92 |
#' obtained. To prevent data loss, progressively wider/narrower windows are |
|
| 93 |
#' used at both ends of the data series. |
|
| 94 |
#' @return |
|
| 95 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 96 |
#' @seealso [signal_correct()] |
|
| 97 |
#' @references |
|
| 98 |
#' Kneen, M. A. and Annegarn, H. J. (1996). Algorithm for Fitting XRF, SEM and |
|
| 99 |
#' PIXE X-Ray Spectra Backgrounds. *Nuclear Instruments and Methods in Physics |
|
| 100 |
#' Research Section B: Beam Interactions with Materials and Atoms*, |
|
| 101 |
#' 109/110: 209-213. \doi{10.1016/0168-583X(95)00908-6}.
|
|
| 102 |
#' @example inst/examples/ex-baseline-rollingball.R |
|
| 103 |
#' @author N. Frerebeau |
|
| 104 |
#' @docType methods |
|
| 105 |
#' @family baseline estimation methods |
|
| 106 |
#' @aliases baseline_rollingball-method |
|
| 107 |
setGeneric( |
|
| 108 |
name = "baseline_rollingball", |
|
| 109 | 2x |
def = function(x, y, ...) standardGeneric("baseline_rollingball")
|
| 110 |
) |
|
| 111 | ||
| 112 |
#' Rubberband Baseline Estimation |
|
| 113 |
#' |
|
| 114 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 115 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 116 |
#' @param noise A length-one [`numeric`] vector giving the noise level. |
|
| 117 |
#' Only used if `method` is "`rubberband`". |
|
| 118 |
#' @param spline A [`logical`] scalar: should spline interpolation through the |
|
| 119 |
#' support points be used instead of linear interpolation? |
|
| 120 |
#' Only used if `method` is "`rubberband`". |
|
| 121 |
#' @param ... Extra arguments to be passed to [stats::smooth.spline()]. |
|
| 122 |
#' @details |
|
| 123 |
#' A convex envelope of the spectrum is determined and the |
|
| 124 |
#' baseline is estimated as the part of the convex envelope lying below the |
|
| 125 |
#' spectrum. Note that the rubber band does not enter the concave regions |
|
| 126 |
#' (if any) of the spectrum. |
|
| 127 |
#' @note |
|
| 128 |
#' `baseline_rubberband()` is slightly modified from C. Beleites' |
|
| 129 |
#' [hyperSpec::spc.rubberband()]. |
|
| 130 |
#' @return |
|
| 131 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 132 |
#' @seealso [signal_correct()] |
|
| 133 |
#' @example inst/examples/ex-baseline-rubberband.R |
|
| 134 |
#' @author N. Frerebeau |
|
| 135 |
#' @docType methods |
|
| 136 |
#' @family baseline estimation methods |
|
| 137 |
#' @aliases baseline_rubberband-method |
|
| 138 |
setGeneric( |
|
| 139 |
name = "baseline_rubberband", |
|
| 140 | 2x |
def = function(x, y, ...) standardGeneric("baseline_rubberband")
|
| 141 |
) |
|
| 142 | ||
| 143 |
#' SNIP Baseline Estimation |
|
| 144 |
#' |
|
| 145 |
#' Sensitive Nonlinear Iterative Peak clipping algorithm. |
|
| 146 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 147 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 148 |
#' @param LLS A [`logical`] scalar: should the LLS operator be applied on `x` |
|
| 149 |
#' before employing SNIP algorithm? Only used if `method` is "`SNIP`". |
|
| 150 |
#' @param decreasing A [`logical`] scalar: should a decreasing clipping window |
|
| 151 |
#' be used? |
|
| 152 |
#' @param n An [`integer`] value giving the number of iterations. |
|
| 153 |
#' @param ... Currently not used. |
|
| 154 |
#' @return |
|
| 155 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 156 |
#' @seealso [signal_correct()] |
|
| 157 |
#' @references |
|
| 158 |
#' Morháč, M., Kliman, J., Matoušek, V., Veselský, M. & Turzo, I. (1997). |
|
| 159 |
#' Background elimination methods for multidimensional gamma-ray spectra. |
|
| 160 |
#' *Nuclear Instruments and Methods in Physics Research Section A: |
|
| 161 |
#' Accelerators, Spectrometers, Detectors and Associated Equipment*, 401(1), |
|
| 162 |
#' p. 113-132. \doi{10.1016/S0168-9002(97)01023-1}
|
|
| 163 |
#' |
|
| 164 |
#' Morháč, M. & Matoušek, V. (2008). Peak Clipping Algorithms for Background |
|
| 165 |
#' Estimation in Spectroscopic Data. *Applied Spectroscopy*, 62(1), p. 91-106. |
|
| 166 |
#' \doi{10.1366/000370208783412762}
|
|
| 167 |
#' |
|
| 168 |
#' Ryan, C. G., Clayton, E., Griffin, W. L., Sie, S. H. & Cousens, D. R. |
|
| 169 |
#' (1988). SNIP, a statistics-sensitive background treatment for the |
|
| 170 |
#' quantitative analysis of PIXE spectra in geoscience applications. |
|
| 171 |
#' *Nuclear Instruments and Methods in Physics Research Section B: |
|
| 172 |
#' Beam Interactions with Materials and Atoms*, 34(3), p. 396-402. |
|
| 173 |
#' \doi{10.1016/0168-583X(88)90063-8}
|
|
| 174 |
#' @example inst/examples/ex-baseline-snip.R |
|
| 175 |
#' @author N. Frerebeau |
|
| 176 |
#' @docType methods |
|
| 177 |
#' @family baseline estimation methods |
|
| 178 |
#' @aliases baseline_snip-method |
|
| 179 |
setGeneric( |
|
| 180 |
name = "baseline_snip", |
|
| 181 | 6x |
def = function(x, y, ...) standardGeneric("baseline_snip")
|
| 182 |
) |
|
| 183 | ||
| 184 |
#' 4S Peak Filling |
|
| 185 |
#' |
|
| 186 |
#' Baseline estimation by iterative mean suppression. |
|
| 187 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 188 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 189 |
#' @param n An [`integer`] value giving the number of iterations. |
|
| 190 |
#' @param m An odd [`integer`] giving the half window size. |
|
| 191 |
#' @param by A length-one [`numeric`] vector giving the umber of buckets to |
|
| 192 |
#' divide `x` into. |
|
| 193 |
#' @param lambda An [`integer`] giving the smoothing parameter. The larger |
|
| 194 |
#' `lambda` is, the smoother the curve (see [smooth_whittaker()]). |
|
| 195 |
#' @param d An [`integer`] specifying the order of the penalty (see |
|
| 196 |
#' [smooth_whittaker()]). |
|
| 197 |
#' @param sparse A [`logical`] scalar: should sparse matrices be used for |
|
| 198 |
#' computation (see [smooth_whittaker()])? If `TRUE`, \pkg{Matrix} is required.
|
|
| 199 |
#' @param ... Currently not used. |
|
| 200 |
#' @return |
|
| 201 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 202 |
#' @references |
|
| 203 |
#' Liland, K. H. (2015). 4S Peak Filling - baseline estimation by iterative |
|
| 204 |
#' mean suppression. *MethodsX*, 2, 135-140. \doi{10.1016/j.mex.2015.02.009}.
|
|
| 205 |
#' @seealso [signal_correct()], [smooth_whittaker()] |
|
| 206 |
#' @example inst/examples/ex-baseline-peakfilling.R |
|
| 207 |
#' @author N. Frerebeau |
|
| 208 |
#' @docType methods |
|
| 209 |
#' @family baseline estimation methods |
|
| 210 |
#' @aliases baseline_peakfilling-method |
|
| 211 |
setGeneric( |
|
| 212 |
name = "baseline_peakfilling", |
|
| 213 | 2x |
def = function(x, y, ...) standardGeneric("baseline_peakfilling")
|
| 214 |
) |
|
| 215 | ||
| 216 |
# Integrate ==================================================================== |
|
| 217 |
#' Rectangle Rule |
|
| 218 |
#' |
|
| 219 |
#' Approximates the definite integral by using the rectangle rule. |
|
| 220 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 221 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 222 |
#' @param right A [`logical`] scalar: should the right rule be used instead of |
|
| 223 |
#' the left rule? |
|
| 224 |
#' @param ... Currently not used. |
|
| 225 |
#' @return |
|
| 226 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 227 |
#' @example inst/examples/ex-integrate.R |
|
| 228 |
#' @author N. Frerebeau |
|
| 229 |
#' @docType methods |
|
| 230 |
#' @family integration methods |
|
| 231 |
#' @aliases integrate_rectangle-method |
|
| 232 |
setGeneric( |
|
| 233 |
name = "integrate_rectangle", |
|
| 234 | 7x |
def = function(x, y, ...) standardGeneric("integrate_rectangle")
|
| 235 |
) |
|
| 236 | ||
| 237 |
#' Trapezoidal Rule |
|
| 238 |
#' |
|
| 239 |
#' Approximates the definite integral by using the trapezoidal rule. |
|
| 240 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 241 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 242 |
#' @param ... Currently not used. |
|
| 243 |
#' @return |
|
| 244 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 245 |
#' @example inst/examples/ex-integrate.R |
|
| 246 |
#' @author N. Frerebeau |
|
| 247 |
#' @docType methods |
|
| 248 |
#' @family integration methods |
|
| 249 |
#' @aliases integrate_trapezoid-method |
|
| 250 |
setGeneric( |
|
| 251 |
name = "integrate_trapezoid", |
|
| 252 | 2x |
def = function(x, y, ...) standardGeneric("integrate_trapezoid")
|
| 253 |
) |
|
| 254 | ||
| 255 |
# Peaks ======================================================================== |
|
| 256 |
#' Find Peaks |
|
| 257 |
#' |
|
| 258 |
#' Finds local maxima in sequential data. |
|
| 259 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 260 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 261 |
#' @param method A [`character`] string specifying the method to be used for |
|
| 262 |
#' background noise estimation (see below). |
|
| 263 |
#' @param SNR An [`integer`] giving the signal-to-noise-ratio for peak detection |
|
| 264 |
#' (see below). |
|
| 265 |
#' @param m An odd [`integer`] giving the window size (i.e. the number of |
|
| 266 |
#' adjacent points to be used). |
|
| 267 |
#' If `NULL`, 5% of the data points is used as the half window size. |
|
| 268 |
#' @param ... Extra parameters to be passed to internal methods. |
|
| 269 |
#' @details |
|
| 270 |
#' A local maximum has to be the highest one in the given window and has to be |
|
| 271 |
#' higher than \eqn{SNR \times noise}{SNR * noise} to be recognized as peak.
|
|
| 272 |
#' |
|
| 273 |
#' The following methods are available for noise estimation: |
|
| 274 |
#' \describe{
|
|
| 275 |
#' \item{`MAD`}{Median Absolute Deviation.}
|
|
| 276 |
#' } |
|
| 277 |
#' |
|
| 278 |
#' Note that to improve peak detection, it may be helpful to smooth the data |
|
| 279 |
#' and remove the baseline beforehand. |
|
| 280 |
#' @note |
|
| 281 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 282 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 283 |
#' obtained. To prevent data loss, progressively wider/narrower windows are |
|
| 284 |
#' used at both ends of the data series. |
|
| 285 |
#' @return |
|
| 286 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 287 |
#' @note |
|
| 288 |
#' Adapted from Stasia Grinberg's |
|
| 289 |
#' [`findPeaks`](https://github.com/stas-g/findPeaks) function. |
|
| 290 |
#' @example inst/examples/ex-peaks.R |
|
| 291 |
#' @author N. Frerebeau |
|
| 292 |
#' @docType methods |
|
| 293 |
#' @family peaks detection methods |
|
| 294 |
#' @aliases peaks_find-method |
|
| 295 |
setGeneric( |
|
| 296 |
name = "peaks_find", |
|
| 297 | ! |
def = function(x, y, ...) standardGeneric("peaks_find")
|
| 298 |
) |
|
| 299 | ||
| 300 |
#' Half-Width at Half-Maximum |
|
| 301 |
#' |
|
| 302 |
#' Estimates the Half-Width at Half-Maximum (FWHM) for a given peak. |
|
| 303 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 304 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 305 |
#' @param center A [`numeric`] value giving the peak position in `x` units. |
|
| 306 |
#' @param ... Currently not used. |
|
| 307 |
#' @return A [`numeric`] value. |
|
| 308 |
#' @details |
|
| 309 |
#' It tries to get the smallest possible estimate. |
|
| 310 |
#' @example inst/examples/ex-peaks.R |
|
| 311 |
#' @author N. Frerebeau |
|
| 312 |
#' @docType methods |
|
| 313 |
#' @family peaks detection methods |
|
| 314 |
#' @aliases peaks_fwhm-method |
|
| 315 |
setGeneric( |
|
| 316 |
name = "peaks_fwhm", |
|
| 317 | 2x |
def = function(x, y, ...) standardGeneric("peaks_fwhm")
|
| 318 |
) |
|
| 319 | ||
| 320 |
# Replace ====================================================================== |
|
| 321 |
#' Replace Values Below a Given Threshold |
|
| 322 |
#' |
|
| 323 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 324 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 325 |
#' @param threshold A [`numeric`] value or a [`function`] that takes a `numeric` |
|
| 326 |
#' vector as argument and returns a single `numeric` value. |
|
| 327 |
#' @param value A [`numeric`] value to replace values below `threshold`. |
|
| 328 |
#' @param ... Extra parameters to be passed to `threshold`. |
|
| 329 |
#' @return |
|
| 330 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 331 |
#' @author N. Frerebeau |
|
| 332 |
#' @docType methods |
|
| 333 |
#' @family replacement methods |
|
| 334 |
#' @aliases replace_threshold-method |
|
| 335 |
setGeneric( |
|
| 336 |
name = "replace_threshold", |
|
| 337 | 6x |
def = function(x, y, threshold, ...) standardGeneric("replace_threshold")
|
| 338 |
) |
|
| 339 | ||
| 340 |
#' Replace Negative Values |
|
| 341 |
#' |
|
| 342 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 343 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 344 |
#' @param value A [`numeric`] value to replace negative values. |
|
| 345 |
#' @param ... Extra parameters to be passed to `threshold`. |
|
| 346 |
#' @return |
|
| 347 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 348 |
#' @author N. Frerebeau |
|
| 349 |
#' @docType methods |
|
| 350 |
#' @family replacement methods |
|
| 351 |
#' @aliases replace_negative-method |
|
| 352 |
setGeneric( |
|
| 353 |
name = "replace_negative", |
|
| 354 | 2x |
def = function(x, y, ...) standardGeneric("replace_negative")
|
| 355 |
) |
|
| 356 | ||
| 357 |
# Resample ===================================================================== |
|
| 358 |
#' Bin |
|
| 359 |
#' |
|
| 360 |
#' Averages `x` values and applies a function to the corresponding `y` values. |
|
| 361 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 362 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 363 |
#' @param by An [`integer`] specifying the binning ratio (i.e. the number of |
|
| 364 |
#' points to be grouped together; see [`window_tumbling()`]). |
|
| 365 |
#' @param f A [`function`] that takes a `numeric` vector of intensities as |
|
| 366 |
#' argument and returns a single `numeric` vector. Used to estimate the local |
|
| 367 |
#' representative value in each bin (defaults to [sum()]; see examples). |
|
| 368 |
#' @param ... Extra parameters to be passed to `f`. |
|
| 369 |
#' @return |
|
| 370 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 371 |
#' @author N. Frerebeau |
|
| 372 |
#' @example inst/examples/ex-resample.R |
|
| 373 |
#' @docType methods |
|
| 374 |
#' @family resampling methods |
|
| 375 |
#' @aliases resample_bin-method |
|
| 376 |
setGeneric( |
|
| 377 |
name = "resample_bin", |
|
| 378 | 2x |
def = function(x, y, ...) standardGeneric("resample_bin")
|
| 379 |
) |
|
| 380 | ||
| 381 |
#' Downsample |
|
| 382 |
#' |
|
| 383 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 384 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 385 |
#' @param by An [`integer`] specifying the downsampling factor. |
|
| 386 |
#' @param ... Currently not used. |
|
| 387 |
#' @return |
|
| 388 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 389 |
#' @author N. Frerebeau |
|
| 390 |
#' @example inst/examples/ex-resample.R |
|
| 391 |
#' @docType methods |
|
| 392 |
#' @family resampling methods |
|
| 393 |
#' @aliases resample_down-method |
|
| 394 |
setGeneric( |
|
| 395 |
name = "resample_down", |
|
| 396 | 2x |
def = function(x, y, ...) standardGeneric("resample_down")
|
| 397 |
) |
|
| 398 | ||
| 399 |
# Upsample |
|
| 400 |
# |
|
| 401 |
# @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 402 |
# interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 403 |
# @param by An [`integer`] specifying the upsampling factor. |
|
| 404 |
# @param ... Currently not used. |
|
| 405 |
# @details |
|
| 406 |
# The signal is upsampled by inserting \eqn{n - 1} zeros between samples.
|
|
| 407 |
# @return |
|
| 408 |
# Returns a [`list`] with two components `x` and `y`. |
|
| 409 |
# @author N. Frerebeau |
|
| 410 |
# @example inst/examples/ex-resample.R |
|
| 411 |
# @docType methods |
|
| 412 |
# @family resampling methods |
|
| 413 |
# @aliases resample_interpolate-method |
|
| 414 |
# setGeneric( |
|
| 415 |
# name = "resample_up", |
|
| 416 |
# def = function(x, y, ...) standardGeneric("resample_up")
|
|
| 417 |
# ) |
|
| 418 | ||
| 419 |
#' Linearly Interpolate |
|
| 420 |
#' |
|
| 421 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 422 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 423 |
#' @param from A length-one [`numeric`] vector giving the starting value of the |
|
| 424 |
#' sequence where interpolation is to take place. |
|
| 425 |
#' @param to A length-one [`numeric`] vector giving the end value of the |
|
| 426 |
#' sequence where interpolation is to take place. |
|
| 427 |
#' @param by A length-one [`numeric`] vector specifying the increment of the |
|
| 428 |
#' sequence. |
|
| 429 |
#' @param ... Extra arguments to be passed to [stats::approx()]. |
|
| 430 |
#' @return |
|
| 431 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 432 |
#' @author N. Frerebeau |
|
| 433 |
#' @example inst/examples/ex-resample.R |
|
| 434 |
#' @docType methods |
|
| 435 |
#' @family resampling methods |
|
| 436 |
#' @aliases resample_interpolate-method |
|
| 437 |
setGeneric( |
|
| 438 |
name = "resample_interpolate", |
|
| 439 | ! |
def = function(x, y, ...) standardGeneric("resample_interpolate")
|
| 440 |
) |
|
| 441 | ||
| 442 |
# Rescale ====================================================================== |
|
| 443 |
#' Normalize intensities by AUC |
|
| 444 |
#' |
|
| 445 |
#' Rescales intensities so that the area under the curve (AUC) is equal to 1. |
|
| 446 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 447 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 448 |
#' @param method A [`character`] string specifying the method for integration. |
|
| 449 |
#' It must be one of "`rectangle`" or "`trapezoid`". |
|
| 450 |
#' Any unambiguous substring can be given. |
|
| 451 |
#' @param ... Currently not used. |
|
| 452 |
#' @return |
|
| 453 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 454 |
#' @example inst/examples/ex-rescale.R |
|
| 455 |
#' @author N. Frerebeau |
|
| 456 |
#' @docType methods |
|
| 457 |
#' @family normalization methods |
|
| 458 |
#' @aliases rescale_area-method |
|
| 459 |
setGeneric( |
|
| 460 |
name = "rescale_area", |
|
| 461 | 2x |
def = function(x, y, ...) standardGeneric("rescale_area")
|
| 462 |
) |
|
| 463 | ||
| 464 |
#' Rescale intensities to sum to a specified value |
|
| 465 |
#' |
|
| 466 |
#' Rescales intensities to sum to a specified value. |
|
| 467 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 468 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 469 |
#' @param total A legnth-one [`numeric`] vector specifying the output total. |
|
| 470 |
#' Defaults to 1, i.e. normalizes by total intensity. |
|
| 471 |
#' @param ... Currently not used. |
|
| 472 |
#' @return |
|
| 473 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 474 |
#' @example inst/examples/ex-rescale.R |
|
| 475 |
#' @author N. Frerebeau |
|
| 476 |
#' @docType methods |
|
| 477 |
#' @family normalization methods |
|
| 478 |
#' @aliases rescale_total-method |
|
| 479 |
setGeneric( |
|
| 480 |
name = "rescale_total", |
|
| 481 | 2x |
def = function(x, y, ...) standardGeneric("rescale_total")
|
| 482 |
) |
|
| 483 | ||
| 484 |
#' Rescales intensities to have specified minimum and maximum |
|
| 485 |
#' |
|
| 486 |
#' Rescales intensities to have specified minimum and maximum. |
|
| 487 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 488 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 489 |
#' @param min A legnth-one [`numeric`] vector specifying the output minimum. |
|
| 490 |
#' @param max A legnth-one [`numeric`] vector specifying the output maximum. |
|
| 491 |
#' @param ... Currently not used. |
|
| 492 |
#' @return |
|
| 493 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 494 |
#' @example inst/examples/ex-rescale.R |
|
| 495 |
#' @author N. Frerebeau |
|
| 496 |
#' @docType methods |
|
| 497 |
#' @family normalization methods |
|
| 498 |
#' @aliases rescale_range-method |
|
| 499 |
setGeneric( |
|
| 500 |
name = "rescale_range", |
|
| 501 | 6x |
def = function(x, y, ...) standardGeneric("rescale_range")
|
| 502 |
) |
|
| 503 | ||
| 504 |
#' @rdname rescale_range |
|
| 505 |
#' @aliases rescale_min-method |
|
| 506 |
setGeneric( |
|
| 507 |
name = "rescale_min", |
|
| 508 | 4x |
def = function(x, y, ...) standardGeneric("rescale_min")
|
| 509 |
) |
|
| 510 | ||
| 511 |
#' @rdname rescale_range |
|
| 512 |
#' @aliases rescale_max-method |
|
| 513 |
setGeneric( |
|
| 514 |
name = "rescale_max", |
|
| 515 | 4x |
def = function(x, y, ...) standardGeneric("rescale_max")
|
| 516 |
) |
|
| 517 | ||
| 518 |
#' Transform Intensities |
|
| 519 |
#' |
|
| 520 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 521 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 522 |
#' @param f A [`function`] that takes a `numeric` vector of intensities as |
|
| 523 |
#' argument and returns a `numeric` vector. |
|
| 524 |
#' @param ... Extra arguments to be passed to `f`. |
|
| 525 |
#' @details |
|
| 526 |
#' Transformation of intensities can be used to improve the identification of |
|
| 527 |
#' peaks with a low signal-to-noise ratio. |
|
| 528 |
#' @return |
|
| 529 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 530 |
#' @example inst/examples/ex-transform.R |
|
| 531 |
#' @author N. Frerebeau |
|
| 532 |
#' @docType methods |
|
| 533 |
#' @family normalization methods |
|
| 534 |
#' @aliases rescale_transform-method |
|
| 535 |
setGeneric( |
|
| 536 |
name = "rescale_transform", |
|
| 537 | 2x |
def = function(x, y, ...) standardGeneric("rescale_transform")
|
| 538 |
) |
|
| 539 | ||
| 540 |
#' Standard Normal Variate (SNV) Transformation |
|
| 541 |
#' |
|
| 542 |
#' Subtracts the mean and scales to unit variance. |
|
| 543 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 544 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 545 |
#' @param ... Currently not used. |
|
| 546 |
#' @return |
|
| 547 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 548 |
#' @references |
|
| 549 |
#' Barnes, R. J., Dhanoa, M. S. & Lister, S. J. (1989). Standard Normal Variate |
|
| 550 |
#' Transformation and De-Trending of Near-Infrared Diffuse Reflectance Spectra. |
|
| 551 |
#' *Applied Spectroscopy*, 43(5): 772-777. \doi{10.1366/0003702894202201}.
|
|
| 552 |
#' @example inst/examples/ex-normalize.R |
|
| 553 |
#' @author N. Frerebeau |
|
| 554 |
#' @docType methods |
|
| 555 |
#' @family normalization methods |
|
| 556 |
#' @aliases rescale_snv-method |
|
| 557 |
setGeneric( |
|
| 558 |
name = "rescale_snv", |
|
| 559 | 2x |
def = function(x, y, ...) standardGeneric("rescale_snv")
|
| 560 |
) |
|
| 561 | ||
| 562 |
# Signal ======================================================================= |
|
| 563 |
#' Baseline Correction |
|
| 564 |
#' |
|
| 565 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 566 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 567 |
#' @param method A [`character`] string specifying the method for baseline |
|
| 568 |
#' estimation (see details). Any unambiguous substring can be given. |
|
| 569 |
#' @param ... Extra arguments to be passed to `baseline_*()` (see details). |
|
| 570 |
#' @details |
|
| 571 |
#' Available methods for baseline estimation: |
|
| 572 |
#' \describe{
|
|
| 573 |
#' \item{`asls`}{Asymmetric Least Squares Smoothing (see [baseline_asls()]).}
|
|
| 574 |
#' \item{`linear`}{Linear baseline estimation (see [baseline_linear()]).}
|
|
| 575 |
#' \item{`polynomial`}{Polynomial baseline estimation (see
|
|
| 576 |
#' [baseline_polynomial()]).} |
|
| 577 |
#' \item{`rollingball`}{Rolling ball baseline estimation (see
|
|
| 578 |
#' [baseline_rollingball()]).} |
|
| 579 |
#' \item{`rubberband`}{Rubberband baseline estimation (see
|
|
| 580 |
#' [baseline_rubberband()]).} |
|
| 581 |
#' \item{`SNIP`}{Sensitive Nonlinear Iterative Peak clipping algorithm
|
|
| 582 |
#' (see [baseline_snip()]).} |
|
| 583 |
#' \item{`4S`}{4S Peak Filling (see [baseline_peakfilling()]).}
|
|
| 584 |
#' } |
|
| 585 |
#' @return |
|
| 586 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 587 |
#' @author N. Frerebeau |
|
| 588 |
#' @example inst/examples/ex-correct.R |
|
| 589 |
#' @docType methods |
|
| 590 |
#' @family signal processing methods |
|
| 591 |
#' @aliases signal_correct-method |
|
| 592 |
setGeneric( |
|
| 593 |
name = "signal_correct", |
|
| 594 | ! |
def = function(x, y, ...) standardGeneric("signal_correct")
|
| 595 |
) |
|
| 596 | ||
| 597 |
#' Bind |
|
| 598 |
#' |
|
| 599 |
#' Combines XY objects. |
|
| 600 |
#' @param ... Any object that can be interpreted in a suitable way |
|
| 601 |
#' (see [grDevices::xy.coords()]). |
|
| 602 |
#' @return |
|
| 603 |
#' Returns a [`matrix`] of intensities. |
|
| 604 |
#' @author N. Frerebeau |
|
| 605 |
#' @example inst/examples/ex-mean.R |
|
| 606 |
#' @docType methods |
|
| 607 |
#' @family signal processing methods |
|
| 608 |
#' @aliases signal_bind-method |
|
| 609 |
setGeneric( |
|
| 610 |
name = "signal_bind", |
|
| 611 | ! |
def = function(...) standardGeneric("signal_bind")
|
| 612 |
) |
|
| 613 | ||
| 614 |
#' Mean Intensities |
|
| 615 |
#' |
|
| 616 |
#' @param ... Any object that can be interpreted in a suitable way |
|
| 617 |
#' (see [grDevices::xy.coords()]). |
|
| 618 |
#' @return |
|
| 619 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 620 |
#' @author N. Frerebeau |
|
| 621 |
#' @example inst/examples/ex-mean.R |
|
| 622 |
#' @docType methods |
|
| 623 |
#' @family signal processing methods |
|
| 624 |
#' @aliases signal_mean-method |
|
| 625 |
setGeneric( |
|
| 626 |
name = "signal_mean", |
|
| 627 | ! |
def = function(...) standardGeneric("signal_mean")
|
| 628 |
) |
|
| 629 | ||
| 630 |
#' Subset |
|
| 631 |
#' |
|
| 632 |
#' @description |
|
| 633 |
#' * `signal_select()` allows to subset by values of `x`. |
|
| 634 |
#' * `signal_slice()` allows to subset by position along `x`. |
|
| 635 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 636 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 637 |
#' @param subset An [`integer`] vector giving either positive values to keep, |
|
| 638 |
#' or negative values to drop. The values provided must be either all |
|
| 639 |
#' positive or all negative (coerced to integer as by [as.integer()]). |
|
| 640 |
#' @param from,to A [`numeric`] value giving the first and last value (in `x` |
|
| 641 |
#' unit) to be selected. |
|
| 642 |
#' @param ... Currently not used. |
|
| 643 |
#' @return |
|
| 644 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 645 |
#' @author N. Frerebeau |
|
| 646 |
#' @example inst/examples/ex-subset.R |
|
| 647 |
#' @docType methods |
|
| 648 |
#' @family signal processing methods |
|
| 649 |
#' @name subset |
|
| 650 |
#' @rdname subset |
|
| 651 |
NULL |
|
| 652 | ||
| 653 |
#' @rdname subset |
|
| 654 |
#' @aliases signal_select-method |
|
| 655 |
setGeneric( |
|
| 656 |
name = "signal_select", |
|
| 657 | 4x |
def = function(x, y, ...) standardGeneric("signal_select")
|
| 658 |
) |
|
| 659 | ||
| 660 |
#' @rdname subset |
|
| 661 |
#' @aliases signal_slice-method |
|
| 662 |
setGeneric( |
|
| 663 |
name = "signal_slice", |
|
| 664 | 2x |
def = function(x, y, ...) standardGeneric("signal_slice")
|
| 665 |
) |
|
| 666 | ||
| 667 |
#' Shift the X Scale |
|
| 668 |
#' |
|
| 669 |
#' Shifts the `x` scale by a given value. |
|
| 670 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 671 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 672 |
#' @param lag A [`numeric`] vector specifying the offset. |
|
| 673 |
#' @param ... Currently not used. |
|
| 674 |
#' @return |
|
| 675 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 676 |
#' @author N. Frerebeau |
|
| 677 |
#' @example inst/examples/ex-shift.R |
|
| 678 |
#' @docType methods |
|
| 679 |
#' @family signal processing methods |
|
| 680 |
#' @aliases signal_shift-method |
|
| 681 |
setGeneric( |
|
| 682 |
name = "signal_shift", |
|
| 683 | 2x |
def = function(x, y, lag, ...) standardGeneric("signal_shift")
|
| 684 |
) |
|
| 685 | ||
| 686 |
#' Drift Intensities |
|
| 687 |
#' |
|
| 688 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 689 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 690 |
#' @param lag A [`numeric`] vector specifying the offset or any object that can |
|
| 691 |
#' be interpreted in a suitable way (see [grDevices::xy.coords()]) |
|
| 692 |
#' @param subtract A [`logical`] scalar: should `lag` be subtracted to `y`? |
|
| 693 |
#' @param ... Currently not used. |
|
| 694 |
#' @return |
|
| 695 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 696 |
#' @author N. Frerebeau |
|
| 697 |
#' @example inst/examples/ex-drift.R |
|
| 698 |
#' @docType methods |
|
| 699 |
#' @family signal processing methods |
|
| 700 |
#' @aliases signal_drift-method |
|
| 701 |
setGeneric( |
|
| 702 |
name = "signal_drift", |
|
| 703 | 4x |
def = function(x, y, lag, ...) standardGeneric("signal_drift")
|
| 704 |
) |
|
| 705 | ||
| 706 |
# Smooth ======================================================================= |
|
| 707 |
#' Rectangular Smoothing |
|
| 708 |
#' |
|
| 709 |
#' Unweighted sliding-average or rectangular Smoothing. |
|
| 710 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 711 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 712 |
#' @param m An odd [`integer`] giving the window size (i.e. the number of |
|
| 713 |
#' adjacent points to be used; see [`window_sliding()`]). |
|
| 714 |
#' @param ... Currently not used. |
|
| 715 |
#' @details |
|
| 716 |
#' It replaces each point in the signal with the average of \eqn{m} adjacent
|
|
| 717 |
#' points. |
|
| 718 |
#' @note |
|
| 719 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 720 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 721 |
#' obtained. To prevent data loss, progressively wider/narrower windows are |
|
| 722 |
#' used at both ends of the data series. |
|
| 723 |
#' @return |
|
| 724 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 725 |
#' @author N. Frerebeau |
|
| 726 |
#' @example inst/examples/ex-smooth.R |
|
| 727 |
#' @docType methods |
|
| 728 |
#' @family smoothing methods |
|
| 729 |
#' @aliases smooth_rectangular-method |
|
| 730 |
setGeneric( |
|
| 731 |
name = "smooth_rectangular", |
|
| 732 | ! |
def = function(x, y, ...) standardGeneric("smooth_rectangular")
|
| 733 |
) |
|
| 734 | ||
| 735 |
#' Triangular Smoothing |
|
| 736 |
#' |
|
| 737 |
#' Weighted sliding-average or triangular smoothing. |
|
| 738 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 739 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 740 |
#' @param m An odd [`integer`] giving the window size (i.e. the number of |
|
| 741 |
#' adjacent points to be used; see [`window_sliding()`]). |
|
| 742 |
#' @param ... Currently not used. |
|
| 743 |
#' @details |
|
| 744 |
#' It replaces each point in the signal with the weighted mean of \eqn{m}
|
|
| 745 |
#' adjacent points. |
|
| 746 |
#' @note |
|
| 747 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 748 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 749 |
#' obtained. To prevent data loss, progressively wider/narrower windows are |
|
| 750 |
#' used at both ends of the data series. |
|
| 751 |
#' @return |
|
| 752 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 753 |
#' @author N. Frerebeau |
|
| 754 |
#' @example inst/examples/ex-smooth.R |
|
| 755 |
#' @docType methods |
|
| 756 |
#' @family smoothing methods |
|
| 757 |
#' @aliases smooth_triangular-method |
|
| 758 |
setGeneric( |
|
| 759 |
name = "smooth_triangular", |
|
| 760 | ! |
def = function(x, y, ...) standardGeneric("smooth_triangular")
|
| 761 |
) |
|
| 762 | ||
| 763 |
#' Loess Smoothing |
|
| 764 |
#' |
|
| 765 |
#' Smoothes intensities by loess fitting. |
|
| 766 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 767 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 768 |
#' @param span An [`integer`] specifying the degree of smoothing (see |
|
| 769 |
#' [stats::loess()]). |
|
| 770 |
#' @param ... Extra arguments to be passed to [stats::loess()]. |
|
| 771 |
#' @return |
|
| 772 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 773 |
#' @author N. Frerebeau |
|
| 774 |
#' @example inst/examples/ex-smooth.R |
|
| 775 |
#' @docType methods |
|
| 776 |
#' @family smoothing methods |
|
| 777 |
#' @aliases smooth_loess-method |
|
| 778 |
setGeneric( |
|
| 779 |
name = "smooth_loess", |
|
| 780 | ! |
def = function(x, y, ...) standardGeneric("smooth_loess")
|
| 781 |
) |
|
| 782 | ||
| 783 |
#' Savitzky-Golay Filter |
|
| 784 |
#' |
|
| 785 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 786 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 787 |
#' @param m An odd [`integer`] giving the window size (i.e. the number of |
|
| 788 |
#' adjacent points to be used). |
|
| 789 |
#' @param p An [`integer`] giving the degree of the polynomial to be used. |
|
| 790 |
#' @param ... Currently not used. |
|
| 791 |
#' @details |
|
| 792 |
#' This method is based on the least-squares fitting of polynomials to |
|
| 793 |
#' segments of \eqn{m} adjacent points.
|
|
| 794 |
#' @note |
|
| 795 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 796 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 797 |
#' obtained. To prevent data loss, the original \eqn{(m - 1) / 2} points at
|
|
| 798 |
#' both ends of the data series are preserved. |
|
| 799 |
#' @return |
|
| 800 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 801 |
#' @references |
|
| 802 |
#' Gorry, P. A. (1990). General Least-Squares Smoothing and Differentiation by |
|
| 803 |
#' the Convolution (Savitzky-Golay) Method. *Analytical Chemistry*, 62(6), |
|
| 804 |
#' p. 570-573. \doi{10.1021/ac00205a007}.
|
|
| 805 |
#' |
|
| 806 |
#' Savitzky, A. & Golay, M. J. E. (1964). Smoothing and Differentiation of |
|
| 807 |
#' Data by Simplified Least Squares Procedures. *Analytical Chemistry*, |
|
| 808 |
#' 36(8), p. 1627-1639. \doi{10.1021/ac60214a047}.
|
|
| 809 |
#' @author N. Frerebeau |
|
| 810 |
#' @example inst/examples/ex-smooth.R |
|
| 811 |
#' @docType methods |
|
| 812 |
#' @family smoothing methods |
|
| 813 |
#' @aliases smooth_savitzky-method |
|
| 814 |
setGeneric( |
|
| 815 |
name = "smooth_savitzky", |
|
| 816 | ! |
def = function(x, y, ...) standardGeneric("smooth_savitzky")
|
| 817 |
) |
|
| 818 | ||
| 819 |
#' Whittaker Smoothing |
|
| 820 |
#' |
|
| 821 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 822 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 823 |
#' @param lambda An [`integer`] giving the smoothing parameter. The larger |
|
| 824 |
#' `lambda` is, the smoother the curve. |
|
| 825 |
#' @param d An [`integer`] specifying the order of the penalty. |
|
| 826 |
#' @param sparse A [`logical`] scalar: should sparse matrices be used for |
|
| 827 |
#' computation? If `TRUE`, \pkg{Matrix} is required.
|
|
| 828 |
#' @param ... Currently not used. |
|
| 829 |
#' @return |
|
| 830 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 831 |
#' @references |
|
| 832 |
#' Eilers, P. H. C. (2003). A Perfect Smoother. *Analytical Chemistry*, |
|
| 833 |
#' 75(14): 3631-36. \doi{10.1021/ac034173t}.
|
|
| 834 |
#' @author N. Frerebeau |
|
| 835 |
#' @example inst/examples/ex-smooth.R |
|
| 836 |
#' @docType methods |
|
| 837 |
#' @family smoothing methods |
|
| 838 |
#' @aliases smooth_whittaker-method |
|
| 839 |
setGeneric( |
|
| 840 |
name = "smooth_whittaker", |
|
| 841 | 1x |
def = function(x, y, ...) standardGeneric("smooth_whittaker")
|
| 842 |
) |
|
| 843 | ||
| 844 |
#' Penalized Likelihood Smoothing |
|
| 845 |
#' |
|
| 846 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 847 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 848 |
#' @param lambda An [`integer`] giving the smoothing parameter. The larger |
|
| 849 |
#' `lambda` is, the smoother the curve. |
|
| 850 |
#' @param d An [`integer`] specifying the order of the penalty. |
|
| 851 |
#' @param SE A [`logical`] scalar: should standard errors be returned? |
|
| 852 |
#' @param progress A [`logical`] scalar: should a progress bar be displayed? |
|
| 853 |
#' @param ... Currently not used. |
|
| 854 |
#' @return |
|
| 855 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 856 |
#' @note |
|
| 857 |
#' \pkg{Matrix} is required.
|
|
| 858 |
#' @references |
|
| 859 |
#' de Rooi, J. J., van der Pers, N. M., Hendrikx, R. W. A., Delhez, R., |
|
| 860 |
#' Böttger A. J. and Eilers, P. H. C. (2014). Smoothing of X-ray diffraction |
|
| 861 |
#' data and Ka2 elimination using penalized likelihood and the composite link |
|
| 862 |
#' model. *Journal of Applied Crystallography*, 47: 852-860. |
|
| 863 |
#' \doi{10.1107/S1600576714005809}
|
|
| 864 |
#' @author J. J. de Rooi *et al.* (original R code). |
|
| 865 |
#' @example inst/examples/ex-xrd-ka2.R |
|
| 866 |
#' @docType methods |
|
| 867 |
#' @family smoothing methods |
|
| 868 |
#' @aliases smooth_likelihood-method |
|
| 869 |
setGeneric( |
|
| 870 |
name = "smooth_likelihood", |
|
| 871 | ! |
def = function(x, y, ...) standardGeneric("smooth_likelihood")
|
| 872 |
) |
|
| 873 | ||
| 874 |
# Strip ======================================================================== |
|
| 875 |
#' Strip XRD ka2 |
|
| 876 |
#' |
|
| 877 |
#' @param x,y A [`numeric`] vector. If `y` is missing, an attempt is made to |
|
| 878 |
#' interpret `x` in a suitable way (see [grDevices::xy.coords()]). |
|
| 879 |
#' @param lambda An [`integer`] giving the smoothing parameter. The larger |
|
| 880 |
#' `lambda` is, the smoother the curve. |
|
| 881 |
#' @param wave A length-two [`numeric`] vector giving the characteristic |
|
| 882 |
#' wavelengths of the anode material (defaults to copper). |
|
| 883 |
#' @param tau A length-one [`numeric`] vector giving the ratio between |
|
| 884 |
#' \eqn{\alpha}1 and \eqn{\alpha}2 line intensities (defaults to 1/2).
|
|
| 885 |
#' @param nseg A length-one [`numeric`] vector specifying the number of equally |
|
| 886 |
#' sized segments for B-spline basis matrix computation. |
|
| 887 |
#' @param progress A [`logical`] scalar: should a progress bar be displayed? |
|
| 888 |
#' @param ... Currently not used. |
|
| 889 |
#' @return |
|
| 890 |
#' Returns a [`list`] with two components `x` and `y`. |
|
| 891 |
#' @note |
|
| 892 |
#' \pkg{Matrix} is required.
|
|
| 893 |
#' @author J. J. de Rooi *et al.* (original R code). |
|
| 894 |
#' @references |
|
| 895 |
#' de Rooi, J. J., van der Pers, N. M., Hendrikx, R. W. A., Delhez, R., |
|
| 896 |
#' Böttger A. J. and Eilers, P. H. C. (2014). Smoothing of X-ray diffraction |
|
| 897 |
#' data and Ka2 elimination using penalized likelihood and the composite link |
|
| 898 |
#' model. *Journal of Applied Crystallography*, 47: 852-860. |
|
| 899 |
#' \doi{10.1107/S1600576714005809}
|
|
| 900 |
#' @example inst/examples/ex-xrd-ka2.R |
|
| 901 |
#' @docType methods |
|
| 902 |
#' @family specialized tools |
|
| 903 |
#' @aliases ka2_strip_penalized-method |
|
| 904 |
setGeneric( |
|
| 905 |
name = "ka2_strip_penalized", |
|
| 906 | ! |
def = function(x, y, ...) standardGeneric("ka2_strip_penalized")
|
| 907 |
) |
|
| 908 | ||
| 909 |
# Windows ====================================================================== |
|
| 910 |
#' Sliding Windows |
|
| 911 |
#' |
|
| 912 |
#' @param n An [`integer`] giving the length of the data series (will be coerced |
|
| 913 |
#' with [`as.integer()`] and hence truncated toward zero). |
|
| 914 |
#' @param m An odd [`integer`] giving the window size, i.e. the number of |
|
| 915 |
#' adjacent points to be used (will be coerced with [`as.integer()`] and hence |
|
| 916 |
#' truncated toward zero). |
|
| 917 |
#' @param i A vector [`integer`] specifying the indices of the data points for |
|
| 918 |
#' which windows should be returned. If `NULL` (the default), windows are |
|
| 919 |
#' evaluated for each data point. |
|
| 920 |
#' @description |
|
| 921 |
#' There will be \eqn{(m - 1) / 2} points both at the beginning and at the end
|
|
| 922 |
#' of the data series for which a complete \eqn{m}-width window cannot be
|
|
| 923 |
#' obtained. To prevent data loss, progressively wider/narrower windows are |
|
| 924 |
#' evaluated at both ends of the data series. |
|
| 925 |
#' @param ... Currently not used. |
|
| 926 |
#' @return |
|
| 927 |
#' Returns a length-\eqn{n} [`list`] of [`integer`] vectors (indices of the
|
|
| 928 |
#' data points in each window). |
|
| 929 |
#' @author N. Frerebeau |
|
| 930 |
#' @example inst/examples/ex-windows.R |
|
| 931 |
#' @docType methods |
|
| 932 |
#' @family moving windows |
|
| 933 |
#' @aliases window_sliding-method |
|
| 934 |
setGeneric( |
|
| 935 |
name = "window_sliding", |
|
| 936 | 10x |
def = function(n, m, ...) standardGeneric("window_sliding")
|
| 937 |
) |
|
| 938 | ||
| 939 |
#' Tumbling Windows |
|
| 940 |
#' |
|
| 941 |
#' @param n An [`integer`] giving the length of the data series (will be coerced |
|
| 942 |
#' with [`as.integer()`] and hence truncated toward zero). |
|
| 943 |
#' @param m An [`integer`] giving the window size, i.e. the number of |
|
| 944 |
#' adjacent points to be used (will be coerced with [`as.integer()`] and hence |
|
| 945 |
#' truncated toward zero). |
|
| 946 |
#' @param drop A [`logical`] scalar: if `m` is not a multiple of `n`, should the |
|
| 947 |
#' last data points be removed so that all windows have the same length? |
|
| 948 |
#' @param ... Currently not used. |
|
| 949 |
#' @return |
|
| 950 |
#' Returns a [`list`] of [`integer`] vectors (indices of the data points in |
|
| 951 |
#' each window). |
|
| 952 |
#' @author N. Frerebeau |
|
| 953 |
#' @example inst/examples/ex-windows.R |
|
| 954 |
#' @docType methods |
|
| 955 |
#' @family moving windows |
|
| 956 |
#' @aliases window_tumbling-method |
|
| 957 |
setGeneric( |
|
| 958 |
name = "window_tumbling", |
|
| 959 | 4x |
def = function(n, m, ...) standardGeneric("window_tumbling")
|
| 960 |
) |
| 1 |
# RESAMPLE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Bin ========================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname resample_bin |
|
| 8 |
#' @aliases resample_bin,numeric,numeric-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "resample_bin", |
|
| 11 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 12 |
definition = function(x, y, by, f = mean, ...) {
|
|
| 13 | 1x |
assert_length(y, length(x)) |
| 14 | ||
| 15 | 1x |
k <- length(x) %% by |
| 16 | 1x |
if (k != 0) {
|
| 17 | 1x |
x <- utils::tail(x, -k) |
| 18 | 1x |
y <- utils::tail(y, -k) |
| 19 |
} |
|
| 20 | 1x |
i <- cut(seq_along(x), breaks = length(x) / by, labels = FALSE) |
| 21 | ||
| 22 | 1x |
mid <- tapply(X = x, INDEX = i, FUN = mean, simplify = FALSE) |
| 23 | 1x |
bin <- tapply(X = y, INDEX = i, FUN = f, ..., simplify = FALSE) |
| 24 | ||
| 25 | 1x |
xy <- list(x = unlist(mid), y = unlist(bin)) |
| 26 | 1x |
xy |
| 27 |
} |
|
| 28 |
) |
|
| 29 | ||
| 30 |
#' @export |
|
| 31 |
#' @rdname resample_bin |
|
| 32 |
#' @aliases resample_bin,ANY,missing-method |
|
| 33 |
setMethod( |
|
| 34 |
f = "resample_bin", |
|
| 35 |
signature = signature(x = "ANY", y = "missing"), |
|
| 36 |
definition = function(x, y, by, f = sum) {
|
|
| 37 | 1x |
xy <- grDevices::xy.coords(x) |
| 38 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, by = by, f = f) |
| 39 |
} |
|
| 40 |
) |
|
| 41 | ||
| 42 |
# Downsample =================================================================== |
|
| 43 |
#' @export |
|
| 44 |
#' @rdname resample_down |
|
| 45 |
#' @aliases resample_down,numeric,numeric-method |
|
| 46 |
setMethod( |
|
| 47 |
f = "resample_down", |
|
| 48 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 49 |
definition = function(x, y, by) {
|
|
| 50 | 1x |
assert_length(y, length(x)) |
| 51 | ||
| 52 | 1x |
i <- seq(from = 1, to = length(x), by = by) |
| 53 | 1x |
xy <- list(x = x[i], y = y[i]) |
| 54 | 1x |
xy |
| 55 |
} |
|
| 56 |
) |
|
| 57 | ||
| 58 |
#' @export |
|
| 59 |
#' @rdname resample_down |
|
| 60 |
#' @aliases resample_down,ANY,missing-method |
|
| 61 |
setMethod( |
|
| 62 |
f = "resample_down", |
|
| 63 |
signature = signature(x = "ANY", y = "missing"), |
|
| 64 |
definition = function(x, y, by) {
|
|
| 65 | 1x |
xy <- grDevices::xy.coords(x) |
| 66 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, by = by) |
| 67 |
} |
|
| 68 |
) |
|
| 69 | ||
| 70 |
# Interpolate ================================================================== |
|
| 71 |
#' @export |
|
| 72 |
#' @rdname resample_interpolate |
|
| 73 |
#' @aliases resample_interpolate,numeric,numeric-method |
|
| 74 |
setMethod( |
|
| 75 |
f = "resample_interpolate", |
|
| 76 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 77 |
definition = function(x, y, from, to, by, ...) {
|
|
| 78 | ! |
assert_length(y, length(x)) |
| 79 | ||
| 80 |
## New x scale |
|
| 81 | ! |
x_scale <- seq(from = from, to = to, by = by) |
| 82 | ||
| 83 |
## Interpolate |
|
| 84 | ! |
new_data <- stats::approx(x = x, y = y, xout = x_scale, ...) |
| 85 | ||
| 86 | ! |
xy <- list(x = new_data$x, y = new_data$y) |
| 87 | ! |
xy |
| 88 |
} |
|
| 89 |
) |
|
| 90 | ||
| 91 |
#' @export |
|
| 92 |
#' @rdname resample_interpolate |
|
| 93 |
#' @aliases resample_interpolate,ANY,missing-method |
|
| 94 |
setMethod( |
|
| 95 |
f = "resample_interpolate", |
|
| 96 |
signature = signature(x = "ANY", y = "missing"), |
|
| 97 |
definition = function(x, y, from, to, by, ...) {
|
|
| 98 | ! |
xy <- grDevices::xy.coords(x) |
| 99 | ! |
methods::callGeneric(x = xy$x, y = xy$y, from = from, to = to, by = by, ...) |
| 100 |
} |
|
| 101 |
) |
| 1 |
# SLIDING WINDOWS |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname window_sliding |
|
| 7 |
#' @aliases window_sliding,integer,integer-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "window_sliding", |
|
| 10 |
signature = signature(n = "integer", m = "integer"), |
|
| 11 |
definition = function(n, m, i = NULL) {
|
|
| 12 |
## Validation |
|
| 13 | 5x |
assert_odd(m) |
| 14 | ||
| 15 |
## Index |
|
| 16 | 5x |
k <- (m - 1) / 2 |
| 17 | 5x |
x <- if (is.null(i)) seq_len(n) else i |
| 18 | ||
| 19 | 5x |
left <- x - k |
| 20 | 5x |
left <- ifelse(left > 0, left, 1) |
| 21 | ||
| 22 | 5x |
right <- x + k |
| 23 | 5x |
right <- ifelse(right < n, right, n) |
| 24 | ||
| 25 | 5x |
mapply( |
| 26 | 5x |
FUN = seq, |
| 27 | 5x |
from = left, |
| 28 | 5x |
to = right, |
| 29 | 5x |
MoreArgs = list(by = 1), |
| 30 | 5x |
SIMPLIFY = FALSE |
| 31 |
) |
|
| 32 |
} |
|
| 33 |
) |
|
| 34 | ||
| 35 |
#' @export |
|
| 36 |
#' @rdname window_sliding |
|
| 37 |
#' @aliases window_sliding,numeric,numeric-method |
|
| 38 |
setMethod( |
|
| 39 |
f = "window_sliding", |
|
| 40 |
signature = signature(n = "numeric", m = "numeric"), |
|
| 41 |
definition = function(n, m, i = NULL) {
|
|
| 42 | 5x |
n <- as.integer(n) |
| 43 | 5x |
m <- as.integer(m) |
| 44 | 5x |
methods::callGeneric(n, m, i = i) |
| 45 |
} |
|
| 46 |
) |
|
| 47 | ||
| 48 |
#' @export |
|
| 49 |
#' @rdname window_tumbling |
|
| 50 |
#' @aliases window_tumbling,integer,integer-method |
|
| 51 |
setMethod( |
|
| 52 |
f = "window_tumbling", |
|
| 53 |
signature = signature(n = "integer", m = "integer"), |
|
| 54 |
definition = function(n, m, drop = FALSE) {
|
|
| 55 | 2x |
k <- n %% m |
| 56 | 2x |
if (drop && k != 0) {
|
| 57 | 1x |
n <- n - k |
| 58 |
} |
|
| 59 | ||
| 60 | 2x |
x <- seq_len(n) |
| 61 | 2x |
i <- cut(x, breaks = n / m, labels = FALSE) |
| 62 | 2x |
split(x, f = i) |
| 63 |
} |
|
| 64 |
) |
|
| 65 | ||
| 66 |
#' @export |
|
| 67 |
#' @rdname window_tumbling |
|
| 68 |
#' @aliases window_tumbling,numeric,numeric-method |
|
| 69 |
setMethod( |
|
| 70 |
f = "window_tumbling", |
|
| 71 |
signature = signature(n = "numeric", m = "numeric"), |
|
| 72 |
definition = function(n, m, drop = FALSE) {
|
|
| 73 | 2x |
n <- as.integer(n) |
| 74 | 2x |
m <- as.integer(m) |
| 75 | 2x |
methods::callGeneric(n, m, drop = drop) |
| 76 |
} |
|
| 77 |
) |
| 1 |
# SIGNAL INTEGRATION |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
#' @export |
|
| 6 |
#' @rdname integrate_rectangle |
|
| 7 |
#' @aliases integrate_rectangle,numeric,numeric-method |
|
| 8 |
setMethod( |
|
| 9 |
f = "integrate_rectangle", |
|
| 10 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 11 |
definition = function(x, y, right = FALSE) {
|
|
| 12 | 4x |
assert_length(y, length(x)) |
| 13 | ||
| 14 | 4x |
h <- if (right) utils::tail(y, -1) else utils::head(y, -1) |
| 15 | 4x |
sum(h * diff(x)) |
| 16 |
} |
|
| 17 |
) |
|
| 18 | ||
| 19 |
#' @export |
|
| 20 |
#' @rdname integrate_rectangle |
|
| 21 |
#' @aliases integrate_rectangle,ANY,missing-method |
|
| 22 |
setMethod( |
|
| 23 |
f = "integrate_rectangle", |
|
| 24 |
signature = signature(x = "ANY", y = "missing"), |
|
| 25 |
definition = function(x, right = FALSE) {
|
|
| 26 | 3x |
xy <- grDevices::xy.coords(x) |
| 27 | 3x |
methods::callGeneric(x = xy$x, y = xy$y, right = right) |
| 28 |
} |
|
| 29 |
) |
|
| 30 | ||
| 31 |
#' @export |
|
| 32 |
#' @rdname integrate_trapezoid |
|
| 33 |
#' @aliases integrate_trapezoid,numeric,numeric-method |
|
| 34 |
setMethod( |
|
| 35 |
f = "integrate_trapezoid", |
|
| 36 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 37 |
definition = function(x, y) {
|
|
| 38 | 1x |
assert_length(y, length(x)) |
| 39 | ||
| 40 | 1x |
sum((utils::head(y, -1) + utils::tail(y, -1)) * diff(x) / 2) |
| 41 |
} |
|
| 42 |
) |
|
| 43 | ||
| 44 |
#' @export |
|
| 45 |
#' @rdname integrate_trapezoid |
|
| 46 |
#' @aliases integrate_trapezoid,ANY,missing-method |
|
| 47 |
setMethod( |
|
| 48 |
f = "integrate_trapezoid", |
|
| 49 |
signature = signature(x = "ANY", y = "missing"), |
|
| 50 |
definition = function(x) {
|
|
| 51 | 1x |
xy <- grDevices::xy.coords(x) |
| 52 | 1x |
methods::callGeneric(x = xy$x, y = xy$y) |
| 53 |
} |
|
| 54 |
) |
| 1 |
# REPLACE |
|
| 2 |
#' @include AllGenerics.R |
|
| 3 |
NULL |
|
| 4 | ||
| 5 |
# Threshold ==================================================================== |
|
| 6 |
#' @export |
|
| 7 |
#' @rdname replace_threshold |
|
| 8 |
#' @aliases replace_threshold,numeric,numeric,function-method |
|
| 9 |
setMethod( |
|
| 10 |
f = "replace_threshold", |
|
| 11 |
signature = signature(x = "numeric", y = "numeric", threshold = "function"), |
|
| 12 |
definition = function(x, y, threshold, value = 0, ...) {
|
|
| 13 | 1x |
assert_length(y, length(x)) |
| 14 | ||
| 15 | 1x |
threshold <- threshold(y, ...) |
| 16 | 1x |
methods::callGeneric(x, y, threshold = threshold, value = value) |
| 17 |
} |
|
| 18 |
) |
|
| 19 | ||
| 20 |
#' @export |
|
| 21 |
#' @rdname replace_threshold |
|
| 22 |
#' @aliases replace_threshold,ANY,missing,function-method |
|
| 23 |
setMethod( |
|
| 24 |
f = "replace_threshold", |
|
| 25 |
signature = signature(x = "ANY", y = "missing", threshold = "function"), |
|
| 26 |
definition = function(x, threshold, value = 0, ...) {
|
|
| 27 | 1x |
xy <- grDevices::xy.coords(x) |
| 28 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, threshold = threshold, |
| 29 | 1x |
value = value) |
| 30 |
} |
|
| 31 |
) |
|
| 32 | ||
| 33 |
#' @export |
|
| 34 |
#' @rdname replace_threshold |
|
| 35 |
#' @aliases replace_threshold,numeric,numeric,numeric-method |
|
| 36 |
setMethod( |
|
| 37 |
f = "replace_threshold", |
|
| 38 |
signature = signature(x = "numeric", y = "numeric", threshold = "numeric"), |
|
| 39 |
definition = function(x, y, threshold, value = 0, ...) {
|
|
| 40 | 3x |
assert_length(y, length(x)) |
| 41 | ||
| 42 | 3x |
i <- y < threshold |
| 43 | 3x |
y[i] <- value |
| 44 | ||
| 45 | 3x |
xy <- list(x = x, y = y) |
| 46 | 3x |
xy |
| 47 |
} |
|
| 48 |
) |
|
| 49 | ||
| 50 |
#' @export |
|
| 51 |
#' @rdname replace_threshold |
|
| 52 |
#' @aliases replace_threshold,ANY,missing,numeric-method |
|
| 53 |
setMethod( |
|
| 54 |
f = "replace_threshold", |
|
| 55 |
signature = signature(x = "ANY", y = "missing", threshold = "numeric"), |
|
| 56 |
definition = function(x, threshold, value = 0, ...) {
|
|
| 57 | 1x |
xy <- grDevices::xy.coords(x) |
| 58 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, threshold = threshold, |
| 59 | 1x |
value = value) |
| 60 |
} |
|
| 61 |
) |
|
| 62 | ||
| 63 |
# Negative ===================================================================== |
|
| 64 |
#' @export |
|
| 65 |
#' @rdname replace_negative |
|
| 66 |
#' @aliases replace_negative,numeric,numeric-method |
|
| 67 |
setMethod( |
|
| 68 |
f = "replace_negative", |
|
| 69 |
signature = signature(x = "numeric", y = "numeric"), |
|
| 70 |
definition = function(x, y, value = 0) {
|
|
| 71 | 1x |
replace_threshold(x, y, threshold = 0, value = value) |
| 72 |
} |
|
| 73 |
) |
|
| 74 | ||
| 75 |
#' @export |
|
| 76 |
#' @rdname replace_negative |
|
| 77 |
#' @aliases replace_negative,ANY,missing-method |
|
| 78 |
setMethod( |
|
| 79 |
f = "replace_negative", |
|
| 80 |
signature = signature(x = "ANY", y = "missing"), |
|
| 81 |
definition = function(x, value = 0) {
|
|
| 82 | 1x |
xy <- grDevices::xy.coords(x) |
| 83 | 1x |
methods::callGeneric(x = xy$x, y = xy$y, value = value) |
| 84 |
} |
|
| 85 |
) |