hoverinfo - Dividir argumentos `...` y distribuirlos a mĂșltiples funciones
r plotly axis format (3)
Usando la siguiente función foo()
como un simple ejemplo, me gustaría distribuir los valores dados en ...
dos funciones diferentes, si es posible.
foo <- function(x, y, ...) {
list(sum = sum(x, ...), grep = grep("abc", y, ...))
}
En el siguiente ejemplo, me gustaría que na.rm
pase a sum()
y que el value
pase a grep()
. Pero recibo un error por un argumento no utilizado en grep()
.
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, na.rm = TRUE, value = TRUE)
# Error in grep("abc", y, ...) : unused argument (na.rm = TRUE)
Parece que los argumentos fueron enviados a grep()
primero. ¿Es eso correcto? Creo que R vería y evaluaría sum()
primero, y devolvería un error para ese caso.
Además, al tratar de dividir los argumentos en ...
, me encontré con problemas. sum()
argumentos formales de sum()
son NULL
porque es un .Primitive
, y por lo tanto no puedo usar
names(formals(sum)) %in% names(list(...))
Tampoco quiero suponer que los argumentos sobrantes de
names(formals(grep)) %in% names(list(...))
se pasan automáticamente a sum()
.
¿Cómo puedo distribuir de manera segura y eficiente ...
argumentos a múltiples funciones para que no se realicen evaluaciones innecesarias?
A largo plazo, me gustaría poder aplicar esto a funciones con una larga lista de ...
argumentos, similares a los de download.file()
y scan()
.
¿Por qué
grep
error beforesum
?Vea que la
sum
es mucho más complaciente con sus argumentos:X <- c(1:5, NA, 6:10) sum(X, na.rm = TRUE, value = TRUE) ## [1] 56
No falla porque no le importan otros argumentos nombrados, por lo que el
value = TRUE
simplifica aTRUE
que suma 1. Por cierto:sum(X, na.rm = TRUE) ## [1] 55
¿Cómo dividir
...
a diferentes funciones?Un método (que es muy propenso a errores) es buscar los argumentos para las funciones objetivo. Por ejemplo:
foo <- function(x, y, ...){ argnames <- names(list(...)) sumargs <- intersect(argnames, names(as.list(args(sum)))) grepargs <- intersect(argnames, names(as.list(args(grep)))) list(sum = do.call(sum, c(list(x), list(...)[sumargs])), grep = do.call(grep, c(list("abc", y), list(...)[grepargs]))) }
Esto es propenso a error siempre que los argumentos que usa una función no sean informados correctamente por
args
, como los objetos S3. Como ejemplo:names(as.list(args(plot))) ## [1] "x" "y" "..." "" names(as.list(args(plot.default))) ## [1] "x" "y" "type" "xlim" "ylim" ## [6] "log" "main" "sub" "xlab" "ylab" ## [11] "ann" "axes" "frame.plot" "panel.first" "panel.last" ## [16] "asp" "..." ""
En este caso, puede sustituir la función S3 apropiada. Debido a esto, no tengo una solución generalizada para esto (aunque no sé si existe o no).
Solo puede pasar el ...
argumento a otra función, si esa otra función incluye todos los argumentos con nombre a los que pasa ...
o si tiene un ...
argumento en sí mismo. Entonces, para sum
, esto no es problema ( args(sum)
devuelve la function (..., na.rm = FALSE)
). Por otro lado, grep
no tiene na.rm
ni ...
como argumento.
args(grep)
# function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
# fixed = FALSE, useBytes = FALSE, invert = FALSE)
Esto no incluye ...
y tampoco incluye un argumento con nombre na.rm
Una solución simple es simplemente definir su propia función mygrep
siguiente manera:
mygrep <- function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
fixed = FALSE, useBytes = FALSE, invert = FALSE, ...)
grep(pattern, x, ignore.case, perl, value, fixed, useBytes, invert)
Entonces parece funcionar:
foo <- function(x, y, ...){
list(sum = sum(x, ...), grep = mygrep("abc", y, ...))
}
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, na.rm = TRUE, value = TRUE)
# $sum
# [1] 56
#
# $grep
# [1] "xyzabcxyz"
Listas separadas Si realmente desea pasar diferentes conjuntos de parámetros a diferentes funciones, probablemente sea más limpio especificar listas separadas:
foo <- function(x, y, sum = list(), grep = list()) {
list(sum = do.call("sum", c(x, sum)), grep = do.call("grep", c("abc", y, grep)))
}
# test
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, sum = list(na.rm = TRUE), grep = list(value = TRUE))
## $sum
## [1] 55
##
## $grep
## [1] "xyzabcxyz"
Lista híbrida / ... Una alternativa es que podríamos usar ... para uno de estos y luego especificar el otro como una lista, particularmente en el caso de que uno de ellos se use con frecuencia y el otro se use con poca frecuencia. El que se usa con frecuencia se pasará por ... y se usará con poca frecuencia a través de una lista. p.ej
foo <- function(x, y, sum = list(), ...) {
list(sum = do.call("sum", c(x, sum)), grep = grep("abc", y, ...))
}
foo(X, Y, sum = list(na.rm = TRUE), value = TRUE)
Aquí hay un par de ejemplos del enfoque híbrido de la R misma:
i) La función mapply
toma ese enfoque usando ambos ...
y una lista MoreArgs
:
> args(mapply)
function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
NULL
ii) nls
también toma este enfoque usando tanto ...
como la lista de control
:
> args(nls)
function (formula, data = parent.frame(), start, control = nls.control(),
algorithm = c("default", "plinear", "port"), trace = FALSE,
subset, weights, na.action, model = FALSE, lower = -Inf,
upper = Inf, ...)
NULL