r data.table

Evaluar tanto el nombre de la columna como el valor objetivo dentro de la expresión `j` dentro de` data.table`



(1)

Aquí hay una posible alternativa.

target <- "vs" value <- 1 dt <- as.data.table(head(mtcars))

En términos de código, no es necesariamente más simple, pero podemos configurar una llamada no evaluada cl definida fuera del alcance de dt que se evaluará dentro del entorno de la tabla de datos.

cl <- substitute( x == y, list(x = as.name(target), y = value) )

Puede ser necesario substitute() para expresiones más largas. Pero en este caso, call() acortaría el código y crearía el mismo resultado de cl . Y entonces cl también podría ser

cl <- call("==", as.name(target), value)

Ahora podemos evaluar cl dentro de dt . En su ejemplo, esto parece funcionar bien.

dt[, NEWCOL := sum(eval(cl)), by = am][] # mpg cyl disp hp drat wt qsec vs am gear carb NEWCOL # 1: 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 1 # 2: 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 1 # 3: 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 1 # 4: 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 2 # 5: 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 2 # 6: 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 2

Después de pensar en esto por un minuto, no estoy seguro de que value necesario sustituir el valor y, por lo tanto, lo siguiente también funciona. Pero como señala David, el primer enfoque es más eficiente en el tiempo.

dt[, eval(as.name(target)) == value] # [1] FALSE FALSE TRUE TRUE FALSE TRUE

Considerar

target <- "vs" value <- 1 library(data.table) dt <- as.data.table(head(mtcars))

Así que estoy tratando de pasar el nombre de la columna y un valor como variables en la expresión j dentro del entorno data.table , algo que sería equivalente a

dt[, vs == 1] # [1] FALSE FALSE TRUE TRUE FALSE TRUE

Si solo el valor es la variable, funciona bien

dt[, vs == value] # [1] FALSE FALSE TRUE TRUE FALSE TRUE

También podemos llamar a la columna dentro del alcance data.table cuando es una variable

dt[, target, with = FALSE] # vs # 1: 0 # 2: 0 # 3: 1 # 4: 1 # 5: 0 # 6: 1

Pero no puedo entender cómo combinar los dos de una manera simple

Nota: Soy consciente de que simplemente puedo hacer:

dt[[target]] == value # [1] FALSE FALSE TRUE TRUE FALSE TRUE

Pero lo necesito dentro del alcance de la tabla de datos para poder modificar otras columnas por referencia, algo así como

dt[, NEWCOL := sum(vs == 1), by = am]

Así que aquí están mis intentos cuando tanto el nombre de la columna como el valor son variables

dt[, target == value, with = FALSE] # Null data.table (0 rows and 0 cols) dt[, target == value] # [1] FALSE dt[, (target) == value] # [1] FALSE dt[, .(target == value)] # V1 # 1: FALSE dt[, eval(target) == value] # [1] FALSE dt[target %in% value] ## Empty data.table (0 rows) of 11 cols: mpg,cyl,disp,hp,drat,wt...

Finalmente se me ocurrió

dt[, .SD[[target]] == value] # [1] FALSE FALSE TRUE TRUE FALSE TRUE

pero es muy ineficiente, aquí hay un punto de referencia simple

set.seed(123) n <- 1e6 dt <- data.table(vs = sample(1L:30L, n, replace = TRUE), am = seq_len(n)) system.time(dt[, NEWCOL := sum(.SD[[target]] == value), by = am]) # user system elapsed # 13.00 0.02 13.12 system.time(dt[, NEWCOL2 := sum(vs == value), by = am]) # user system elapsed # 0.82 0.00 0.83

Pregunta : ¿Hay alguna forma mejor de hacer esto que me estoy perdiendo aquí? Algo más idiomático o mucho más eficiente.

Editar

Originalmente estaba buscando algo idiomático, así que pensé que la solución simple de @GGrothendieck usando get era la correcta, pero sorprendentemente todas las versiones de @Richard están superando incluso la versión que no evalúa el nombre de la columna.

set.seed(123) n <- 1e7 dt <- data.table(vs = sample(1L:30L, n, replace = TRUE), am = seq_len(n)) cl <- substitute( x == y, list(x = as.name(target), y = value) ) cl2 <- call("==", as.name(target), value) system.time(dt[, NEWCOL := sum(vs == value), by = am]) # user system elapsed # 0.83 0.00 0.82 system.time(dt[, NEWCOL1 := sum(.SD[[target]] == value), by = am]) # user system elapsed # 8.97 0.00 8.97 system.time(dt[, NEWCOL2 := sum(get(target) == value), by = am]) # user system elapsed # 2.35 0.00 2.37 system.time(dt[, NEWCOL3 := sum(eval(cl)), by = am]) # user system elapsed # 0.69 0.02 0.71 system.time(dt[, NEWCOL4 := sum(eval(cl2)), by = am]) # user system elapsed # 0.76 0.00 0.77 system.time(dt[, NEWCOL5 := sum(eval(as.name(target)) == value), by = am]) # user system elapsed # 0.78 0.00 0.78