tutorial style formatstyle extension data r data.table

style - crear una expresión de una función para data.table a eval



render table shiny (2)

Esto no se siente ideal, pero es lo mejor que he podido encontrar. Lo lanzaré para ver si ayuda a obtener mejores respuestas ...

vars <- c("x", "y") res <- do.call(data.table, (lapply(vars, function(X) dat[,eval(myfun(X)),]))) setnames(res, names(res), paste0(vars, "_out")) ## Check the results head(res, 3) # x_out y_out # 1: 0 0 # 2: 0 0 # 3: 0 0

La parte que no me gusta es que lapply() creará una copia de la salida, en forma de lista, y luego data.table() tendrá (por lo que tengo entendido) que copiar esos datos en una ubicación separada, que es peor que si hubiera usado la construcción list() dentro de [.data.frame() .

Dados los data.table la data.table :

dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10)

Me gustaría una función que crea una expresión entre dos filas similares dado su nombre "raíz", por ejemplo, x_one - x_two .

myfun <- function(name) { one <- paste0(name, ''_one'') two <- paste0(name, ''_two'') parse(text=paste(one, ''-'', two)) }

Ahora, usar solo un nombre de raíz funciona como se espera y da como resultado un vector.

dat[, eval(myfun(''x'')),] [1] 0 0 0 0 0 0 0 0 0 0

Sin embargo, al intentar asignar a esa salida un nombre usando la técnica de list falla:

dat[, list(x_out = eval(myfun(''x''))),] Error in eval(expr, envir, enclos) : object ''x_one'' not found

Puedo "resolver" esto agregando un with(dat, ...) pero eso parece poco data.table-ish

dat[, list(x_out = with(dat, eval(myfun(''x''))), y_out = with(dat, eval(myfun(''y'')))),] x_out y_out 1: 0 0 2: 0 0 3: 0 0 4: 0 0 5: 0 0 6: 0 0 7: 0 0 8: 0 0 9: 0 0 10: 0 0

¿Cuál es la forma correcta de generar y evaluar estas expresiones si quiero una salida como la que tengo arriba?

En caso de que ayude, la salida de sessionInfo() está abajo. Recuerdo haber podido hacer esto, o algo parecido, pero ha pasado un tiempo y data.table se actualiza desde ...

R version 2.15.1 (2012-06-22) Platform: x86_64-pc-linux-gnu (64-bit) locale: [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=C LC_NAME=C [9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C attached base packages: [1] graphics grDevices utils datasets stats grid methods base other attached packages: [1] Cairo_1.5-1 zoo_1.7-7 stringr_0.6.1 doMC_1.2.5 multicore_0.1-7 iterators_1.0.6 foreach_1.4.0 [8] data.table_1.8.2 circular_0.4-3 boot_1.3-5 ggplot2_0.9.1 reshape2_1.2.1 plyr_1.7.1 loaded via a namespace (and not attached): [1] codetools_0.2-8 colorspace_1.1-1 dichromat_1.2-4 digest_0.5.2 labeling_0.1 lattice_0.20-6 [7] MASS_7.3-20 memoise_0.1 munsell_0.3 proto_0.3-9.2 RColorBrewer_1.0-5 scales_0.2.1 [13] tools_2.15.1


Una solución es poner la list(...) dentro de la salida de la función.

as.quoted a usar como. as.quoted , robando la forma en que implementa @hadley .() En el paquete plyr .

library(data.table) library(plyr) dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10) myfun <- function(name) { one <- paste0(name, ''_one'') two <- paste0(name, ''_two'') out <- paste0(name,''_out'') as.quoted(paste(''list('',out, ''='',one, ''-'', two,'')''))[[1]] } dat[, eval(myfun(''x'')),] # x_out # 1: 0 # 2: 0 # 3: 0 # 4: 0 # 5: 0 # 6: 0 # 7: 0 # 8: 0 # 9: 0 #10: 0

Para hacer dos columnas a la vez puedes ajustar tu llamada.

myfun <- function(name) { one <- paste0(name, ''_one'') two <- paste0(name, ''_two'') out <- paste0(name,''_out'') calls <- paste(paste(out, ''='', one, ''-'',two), collapse = '','') as.quoted(paste(''list('', calls,'')''))[[1]] } dat[, eval(myfun(c(''x'',''y''))),] # x_out y_out # 1: 0 0 # 2: 0 0 # 3: 0 0 # 4: 0 0 # 5: 0 0 # 6: 0 0 # 7: 0 0 # 8: 0 0 # 9: 0 0 # 0: 0 0

En cuanto a la razón .....

en esta solución, toda la llamada a '' list(..) se evalúa dentro de parent.frame como data.table.

El código relevante dentro de [.data.table es

if (missing(j)) stop("logical error, j missing") jsub = substitute(j) if (is.null(jsub)) return(NULL) jsubl = as.list.default(jsub) if (identical(jsubl[[1L]],quote(eval))) { jsub = eval(jsubl[[2L]],parent.frame()) if (is.expression(jsub)) jsub = jsub[[1L]] }

si (en tu caso)

j = list(xout = eval(myfun(''x''))) ##then jsub <- substitute(j)

es

# list(xout = eval(myfun("x")))

y

as.list.default(jsub) ## [[1]] ## list ## ## $xout ## eval(myfun("x"))

así que jsubl[[1L]] es la list , jsubl[[2L]] es eval(myfun("x"))

por data.table tanto, data.table no ha encontrado una llamada a eval y no la tratará de manera adecuada.

Esto funcionará, forzando la segunda evaluación dentro de los datos correctos.

# using OP myfun dat[,list(xout =eval(myfun(''x''), dat))]

De la misma manera

eval(parse(text = ''x_one''),dat) # [1] 1 2 3 4 5 6 7 8 9 10

Trabaja pero

eval(eval(parse(text = ''x_one'')), dat)

No

Editar 10/4/13

Aunque probablemente sea más seguro (pero más lento) usar .SD como entorno, ya que será más robusto para i o by ejemplo.

dat[,list(xout =eval(myfun(''x''), .SD))]

Editar desde Mateo:

+10 a arriba. Yo no podría haberlo explicado mejor. Yendo un paso más allá, lo que a veces hago es construir la consulta completa de data.table y luego eval eso. Puede ser un poco más robusto de esa manera, a veces. Pienso en ello como SQL; es decir, a menudo construimos una declaración dinámica de SQL que se envía al servidor SQL para ser ejecutada. Cuando también está depurando, a veces también es más fácil mirar la consulta construida y ejecutarla en el indicador del navegador. Pero, a veces, una consulta de este tipo sería muy larga, por lo que pasar eval a i , j o by puede ser más eficiente al no volver a calcular los otros componentes. Como de costumbre, hay muchas maneras de pelar al gato.

Las razones sutiles para considerar evaluar la consulta completa incluyen:

  1. Una razón por la que la agrupación es rápida es que inspecciona primero la expresión j . Si es una list , quita los nombres, pero los recuerda. A continuación, eval una lista sin nombre para cada grupo y luego restablece los nombres una vez, al final del resultado final. Una razón por la que otros métodos pueden ser lentos es la recreación del mismo vector de nombre de columna para cada grupo, una y otra vez. Sin embargo, cuanto más complejo se define j (por ejemplo, si la expresión no comienza precisamente con la list ), más difícil será codificar internamente la lógica de inspección. Hay muchas pruebas en esta área; por ejemplo, en combinación con eval , e informes de verbosidad si la omisión del nombre no funciona. Sin embargo, construir una consulta "simple" (la consulta completa) y eval puede ser más rápido y más sólido por esta razón.

  2. Con v1.8.2 ahora hay optimización de j : options(datatable.optimize=Inf) . Esto inspecciona j y lo modifica para optimizar la mean y el lapply(.SD,...) , hasta ahora. Esto hace que los órdenes de magnitud sean diferentes y significa que hay menos para que el usuario los sepa (por ejemplo, algunos de los puntos de wiki ya han desaparecido). Podríamos hacer más de esto; por ejemplo, DT[a==10] podría optimizarse a DT[J(10)] automáticamente si la key(DT)[1]=="a" [Actualización de septiembre de 2014: ahora implementada en v1.9.3]. Pero, nuevamente, las optimizaciones internas son más difíciles de codificar internamente si en lugar de DT[,mean(a),by=b] es DT[,list(x=eval(expr)),by=b] donde expr contenía una llamada para mean , por ejemplo. Por lo tanto, eval la consulta completa puede funcionar mejor con datatable.optimize . Activar verbosidad en los informes lo que está haciendo y la optimización se puede desactivar si es necesario; Por ejemplo, para probar la diferencia de velocidad que hace.

Según los comentarios, FR # 2183 se ha agregado: "Cambiar j = list (xout = eval (...)) eval a eval dentro del alcance de DT". Gracias por destacar. Ese es el tipo de complejo que quiero decir donde la eval está anidada en la expresión. Sin embargo, si j comienza con eval , eso es mucho más simple y ya está codificado (como se muestra arriba) y probado, y debería optimizarse correctamente.

Si hay data.table se data.table de esto, entonces es: use DT[...,verbose=TRUE] u options(datatable.verbose=TRUE) para verificar data.table aún funciona de manera eficiente cuando se usa para consultas dinámicas que involucran eval .