¿Cómo escribir una función que llama a una función que llama data.table?
(2)
Creo que podrías estar atándote en nudos. Esto funciona:
library(data.table)
foo <- function(data, by){
by <- by
data[, .N, by=by]
}
DT <- data.table(mtcars)
foo(DT, ''gear'')
plotfoo <- function(data, by){
foo(data, by)
}
plotfoo(DT, ''gear'')
Y ese método soporta pasar valores de caracteres:
> gg <- ''gear''
> plotfoo <- function(data, by){
+ foo(data, by)
+ }
> plotfoo(DT, gg)
gear N
1: 4 12
2: 3 15
3: 5 5
El paquete data.table
tiene una sintaxis especial que requiere que uno use expresiones como los argumentos i
y j
.
Esto tiene algunas implicaciones sobre cómo se escriben las funciones que aceptan y pasan argumentos a las tablas de datos, como se explica muy bien en la sección 1.16 de las Preguntas frecuentes .
Pero no puedo averiguar cómo tomar este nivel adicional.
Aquí hay un ejemplo. Digamos que quiero escribir una función de envoltorio foo()
que hace un resumen específico de mis datos, y luego un segundo plotfoo()
que llama a foo()
y traza el resultado:
library(data.table)
foo <- function(data, by){
by <- substitute(by)
data[, .N, by=list(eval(by))]
}
DT <- data.table(mtcars)
foo(DT, gear)
OK, esto funciona, porque obtengo mis resultados tabulados:
by N
1: 4 12
2: 3 15
3: 5 5
Ahora, trato de hacer lo mismo cuando escribo plotfoo()
pero fallo miserablemente:
plotfoo <- function(data, by){
by <- substitute(by)
foo(data, eval(by))
}
plotfoo(DT, gear)
Pero esta vez me sale un mensaje de error:
Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
OK, entonces la eval()
está causando un problema. Vamos a quitarlo:
plotfoo <- function(data, by){
by <- substitute(by)
foo(data, by)
}
plotfoo(DT, gear)
Oh no, me sale un nuevo mensaje de error:
Error in `[.data.table`(data, , .N, by = list(eval(by))) :
column or expression 1 of ''by'' or ''keyby'' is type symbol. Do not quote column names. Useage: DT[,sum(colC),by=list(colA,month(colB))]
Y aquí es donde me quedo estancado.
Pregunta: ¿Cómo escribir una función que llame a una función que llame data.table?
Esto funcionará:
plotfoo <- function(data, by) {
by <- substitute(by)
do.call(foo, list(quote(data), by))
}
plotfoo(DT, gear)
# by N
# 1: 4 12
# 2: 3 15
# 3: 5 5
Explicación:
El problema es que su llamada a foo()
en plotfoo()
parece a uno de los siguientes:
foo(data, eval(by))
foo(data, by)
Cuando foo
procesa esas llamadas, substitute
debidamente al segundo argumento formal ( by
) obteniendo como valor de s los símbolos eval(by)
o by
. Pero usted quiere que el valor de sea gear
, como en la llamada foo(data, gear)
.
do.call()
resuelve este problema evaluando los elementos de su segundo argumento antes de construir la llamada que luego evalúa. Como resultado, cuando lo pasa, lo evalúa a su valor (el gear
del símbolo) antes de construir una llamada que se vea (esencialmente) así:
foo(data, gear)