Background
I have a fun function that compares vectors within a list using all.equal. As I'm making use of the all.equal I would like to pass relevant all.equal arguments via ellipsis. Without the need to pass anything to all.equal the function works as desired.
Function goals
- The function aims to provide modified version of
all.equalcall working on any number of vectors - Each of the vectors can have any number of elements; however, if all vectors are not of equal lengths the function will return false.
- The function should be able to take advantage of arguments available in
all.equal. For instance vectorsc(1.1, 2),c(1, 2)andc(1.3, 2)will be considered equal iftoleranceargument with the right value is provided. The question is concerned with getting this to work.
Example
Comparing 1,000 vectors, each consisting of three integers.
compare_multiple_vectors(x = lapply(
X = vector(mode = "list", length = 1e3),
FUN = function(...) {
c(1, 2, 3)
}
))
# [1] TRUE
Problem / desired results
all.equal called with tolerance = 1 on the following list of vectors will return expected TRUE
all.equal(c(1,2), c(1,1), tolerance = 1)
# [1] TRUE
The tolerance = 1 argument fails to filter down to inside Reduce.
compare_multiple_vectors(x = list(c(1,2), c(1,1)), tolerance = 1)
# [1] FALSE
The desired result should be TRUE.
Code
#' @title Compare Values of Multiple Vectors
#'
#' @description The function compares values across multiple vectors using
#' \code{\link[base]{all.equal}}.
#'
#' @param x Alist of vectors to compare
#' @param ... as in \code{\link[base]{all.equal}}
#'
#' @return A logical
#'
#' @export
#'
#' @importFrom checkmate assert_atomic_vector
#'
#' @examples
#' # Returns TRUE
#' compare_multiple_vectors(c(1,1,1), c(1,1,1))
#' # Returns FALSE
#' compare_multiple_vectors(c(1,1,1), c(1,1,1), c(1,2,1))
#' # Returns FALSE
#' compare_multiple_vectors(c(1,2,3), c(3,2,1))
compare_multiple_vectors <- function(x, ...) {
# Check if all elements of x are atomic vectors
Vectorize(FUN = checkmate::assert_atomic_vector,
vectorize.args = "x")(x)
# Compare list elements
Reduce(
f = function(a, b, ...) {
if (isTRUE(all.equal(target = a, current = b, ...))) {
a
} else {
FALSE
}
},
x = x
) -> res_red
# Return results
if (isFALSE(res_red)) {
return(FALSE)
} else {
return(TRUE)
}
}
Notes
I'm interested in making use of ellipsis and leaving the initial call the way it is with
compare_multiple_vectors(x = list_of_vectors_to_compare, ... # all.equal arguments )
Reducedoesn't have a...argument, butMapdoes. Would something like thisMap(function(a, b, ...) all.equal(a, b, ...), list(c(1,2)), list(c(1,1)), tolerance = 1)do it? Note that the list is now two lists. - Rui BarradasMap(all.equal, list(c(1,2)), list(c(1,1)), tolerance = 1). Apparently,Maprecognizes the ellipses argument ofall.equal. - Rui Barradas