From 68f6bb93ae90419df37974be52e0b4868f03f333 Mon Sep 17 00:00:00 2001 From: vagudets Date: Wed, 28 May 2025 12:08:11 +0200 Subject: [PATCH 1/6] Reorder 'data' if named list to match the order of the arguments in 'fun' --- R/Apply.R | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/R/Apply.R b/R/Apply.R index 080af96..f3bc59c 100644 --- a/R/Apply.R +++ b/R/Apply.R @@ -133,6 +133,12 @@ Apply <- function(data, target_dims = NULL, fun, ..., if (!is.list(data)) { data <- list(data) } + if (!is.null(names(data)) && !is.null(formalArgs(fun))) { + unnamed_elements <- which(names(data) == "") + common_args <- formalArgs(fun)[which(formalArgs(fun) %in% names(data))] + common_args <- match(common_args, names(data)) + data <- data[c(common_args, unnamed_elements)] + } #if (any(!sapply(data, is.numeric))) { # stop("Parameter 'data' must be one or a list of numeric objects.") #} @@ -142,6 +148,7 @@ Apply <- function(data, target_dims = NULL, fun, ..., guessed_any_dimnames <- FALSE for (i in 1 : length(data)) { if (length(data[[i]]) < 1) { + browser() stop("Arrays in 'data' must be of length > 0.") } if (is.null(dim(data[[i]]))) { @@ -613,6 +620,7 @@ Apply <- function(data, target_dims = NULL, fun, ..., for (i in seq_along(extra_info)) { assign(names(extra_info)[i], extra_info[[i]], envir = fun_env) } + environment(fun) <- fun_env splatted_f <- splat(fun) -- GitLab From 8935e9f9d98473fe64b47a1f7eb2842d1683ba7b Mon Sep 17 00:00:00 2001 From: vagudets Date: Wed, 28 May 2025 12:08:50 +0200 Subject: [PATCH 2/6] Remove browser() call --- R/Apply.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/Apply.R b/R/Apply.R index f3bc59c..29ebe7c 100644 --- a/R/Apply.R +++ b/R/Apply.R @@ -148,7 +148,6 @@ Apply <- function(data, target_dims = NULL, fun, ..., guessed_any_dimnames <- FALSE for (i in 1 : length(data)) { if (length(data[[i]]) < 1) { - browser() stop("Arrays in 'data' must be of length > 0.") } if (is.null(dim(data[[i]]))) { -- GitLab From deed6ead05b350130b909b2964c5790274344c76 Mon Sep 17 00:00:00 2001 From: vagudets Date: Wed, 28 May 2025 12:12:23 +0200 Subject: [PATCH 3/6] Unignore unit tests --- .Rbuildignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Rbuildignore b/.Rbuildignore index bdb1d43..a17ed7f 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -5,4 +5,4 @@ .pdf ./.nc # unit tests should be ignored when building the package for CRAN -^tests$ +tests$ -- GitLab From 5aa8ae47f6baa063cfd4806f26af2fdf8682f91d Mon Sep 17 00:00:00 2001 From: vagudets Date: Fri, 30 May 2025 16:59:17 +0200 Subject: [PATCH 4/6] add example --- R/Apply.R | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/R/Apply.R b/R/Apply.R index 29ebe7c..3e1795f 100644 --- a/R/Apply.R +++ b/R/Apply.R @@ -30,10 +30,13 @@ #'right-most positions) and resulting dimensions (at the left-most positions). #' #'@param data One or a list of vectors, matrices or arrays. They must be in the -#' same order as expected by the function provided in the parameter 'fun'. The -#' dimensions do not necessarily have to be ordered. If the 'target_dims' -#' require a different order than the provided, \code{Apply} will automatically -#' reorder the dimensions as needed. +#' same order as expected by the function provided in the parameter 'fun', or +#' named like the corresponding parameter in 'fun'. If 'fun' has a '...' +#' parameter and one or more vectors in 'data' need to be passed onto it, they +#' should be left unnamed. The dimensions of the elements in 'data' do not +#' necessarily have to be ordered. If the 'target_dims' require a different +#' order than the provided, \code{Apply} will automatically reorder the +#' dimensions as needed. #'@param target_dims One or a list of vectors (or NULLs) containing the #' dimensions to be input into fun for each of the objects in the data. If a #' single vector of target dimensions is specified and multiple inputs are @@ -97,7 +100,8 @@ #' of 2 will result in 8 pieces for each core, and so on. The special value #' 'greatest' will split the input data into as many pieces as possible. #' The larger the split factor, the smaller the amount of data that will be -#' processed at once and the finer the granules to be distributed across cores,#' but the larger the overhead for granule startup, etc. +#' processed at once and the finer the granules to be distributed across cores, +#' but the larger the overhead for granule startup, etc. #' #'@details #'When using a single object as input, Apply is almost identical to the apply @@ -118,6 +122,19 @@ #' (sum(y > z) / (length(y)))) * 100 #' } #' test <- Apply(data, target = list(3, 3, NULL), test_fun) +#' +#' # Function using dots: +#' # When defining the function, '...' should be the last of the arguments +#' # that will be passed onto 'data'. +#' test_fun <- function(x, ...) { +#' sum(...) / x +#' } +#' # The elements that correspond to '...' should be unnamed: +#' data <- list(x = array(rnorm(50), c(5, 10)), +#' array(rnorm(500), c(5, 10)), +#' array(rnorm(50), c(5, 10))) +#' test <- Apply(data, target = list(1, 1, 1), test_fun) +#' #'@importFrom foreach registerDoSEQ #'@importFrom doParallel registerDoParallel #'@importFrom plyr splat llply @@ -134,10 +151,22 @@ Apply <- function(data, target_dims = NULL, fun, ..., data <- list(data) } if (!is.null(names(data)) && !is.null(formalArgs(fun))) { + fun_args <- formalArgs(fun) + if (!all(names(data)[which(names(data) != "")] %in% fun_args)) { + stop("Some names in 'data' are not found as arguments in 'fun'. ", + "Please rename the elements to match the 'fun' argument names, ", + "or provide an unnamed list in the expected order.") + } unnamed_elements <- which(names(data) == "") - common_args <- formalArgs(fun)[which(formalArgs(fun) %in% names(data))] + common_args <- fun_args[which(fun_args %in% names(data))] common_args <- match(common_args, names(data)) data <- data[c(common_args, unnamed_elements)] + if (length(unnamed_elements) > 0) { + warning("Some elements in list 'data' are named, while others are ", + "unnamed. The unnamed elements have been placed at the end of ", + "the list. Please check carefully that the order is the same ", + "as expected by 'fun', or provide list names for safety.") + } } #if (any(!sapply(data, is.numeric))) { # stop("Parameter 'data' must be one or a list of numeric objects.") -- GitLab From 956b199ffdaf058b2f0d50bf56c93ba3259a1a47 Mon Sep 17 00:00:00 2001 From: vagudets Date: Wed, 4 Jun 2025 11:36:19 +0200 Subject: [PATCH 5/6] Fix examples in documentation --- R/Apply.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/Apply.R b/R/Apply.R index 810f322..54841d3 100644 --- a/R/Apply.R +++ b/R/Apply.R @@ -121,7 +121,7 @@ #' ((sum(x > z) / (length(x))) / #' (sum(y > z) / (length(y)))) * 100 #' } -#' test <- Apply(data, target = list(3, 3, NULL), test_fun) +#' test <- Apply(data, target_dims = list(3, 3, NULL), test_fun) #' #' # Function using dots: #' # When defining the function, '...' should be the last of the arguments @@ -133,7 +133,7 @@ #' data <- list(x = array(rnorm(50), c(5, 10)), #' array(rnorm(500), c(5, 10)), #' array(rnorm(50), c(5, 10))) -#' test <- Apply(data, target = list(1, 1, 1), test_fun) +#' test <- Apply(data, target_dims = list(1, 1, 1), test_fun) #' #'@importFrom foreach registerDoSEQ #'@importFrom doParallel registerDoParallel -- GitLab From 141822a0f624abe0df7eb42122dfec23568f37f2 Mon Sep 17 00:00:00 2001 From: vagudets Date: Mon, 16 Jun 2025 11:45:39 +0200 Subject: [PATCH 6/6] Update documentation --- DESCRIPTION | 2 +- man/Apply.Rd | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 09bc59c..6b94aa5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -34,4 +34,4 @@ License: GPL-3 URL: https://earth.bsc.es/gitlab/ces/multiApply BugReports: https://earth.bsc.es/gitlab/ces/multiApply/-/issues Encoding: UTF-8 -RoxygenNote: 7.2.0 +RoxygenNote: 7.3.1 diff --git a/man/Apply.Rd b/man/Apply.Rd index 0cac5af..9e8ccd6 100644 --- a/man/Apply.Rd +++ b/man/Apply.Rd @@ -20,10 +20,13 @@ Apply( } \arguments{ \item{data}{One or a list of vectors, matrices or arrays. They must be in the -same order as expected by the function provided in the parameter 'fun'. The -dimensions do not necessarily have to be ordered. If the 'target_dims' -require a different order than the provided, \code{Apply} will automatically -reorder the dimensions as needed.} +same order as expected by the function provided in the parameter 'fun', or +named like the corresponding parameter in 'fun'. If 'fun' has a '...' +parameter and one or more vectors in 'data' need to be passed onto it, they +should be left unnamed. The dimensions of the elements in 'data' do not +necessarily have to be ordered. If the 'target_dims' require a different +order than the provided, \code{Apply} will automatically reorder the +dimensions as needed.} \item{target_dims}{One or a list of vectors (or NULLs) containing the dimensions to be input into fun for each of the objects in the data. If a @@ -97,7 +100,8 @@ pieces for each of the cores (as specified in \code{ncores}). A split factor of 2 will result in 8 pieces for each core, and so on. The special value 'greatest' will split the input data into as many pieces as possible. The larger the split factor, the smaller the amount of data that will be -processed at once and the finer the granules to be distributed across cores,#' but the larger the overhead for granule startup, etc.} +processed at once and the finer the granules to be distributed across cores, +but the larger the overhead for granule startup, etc.} } \value{ List of arrays or matrices or vectors resulting from applying 'fun' to @@ -148,7 +152,20 @@ test_fun <- function(x, y, z) { ((sum(x > z) / (length(x))) / (sum(y > z) / (length(y)))) * 100 } -test <- Apply(data, target = list(3, 3, NULL), test_fun) +test <- Apply(data, target_dims = list(3, 3, NULL), test_fun) + +# Function using dots: +# When defining the function, '...' should be the last of the arguments +# that will be passed onto 'data'. +test_fun <- function(x, ...) { + sum(...) / x +} +# The elements that correspond to '...' should be unnamed: +data <- list(x = array(rnorm(50), c(5, 10)), + array(rnorm(500), c(5, 10)), + array(rnorm(50), c(5, 10))) +test <- Apply(data, target_dims = list(1, 1, 1), test_fun) + } \references{ Wickham, H (2011), The Split-Apply-Combine Strategy for Data Analysis, -- GitLab