Evaluando el nombre de la columna y el valor objective dentro de la expresión `j` dentro de` data.table`

Considerar

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

Así que estoy tratando de pasar tanto el nombre de la columna como 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 scope 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 imaginar cómo combinar los dos de una manera simple

Nota: Soy muy consciente de que puedo simplemente hacer:

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

Pero lo necesito dentro del scope 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 bashs 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 falta 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 era get , pero sorprendentemente todas las versiones de @Richard están superando incluso a la versión que no hace ninguna evaluación del 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 

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 scope de dt que se va a evaluar dentro del entorno de la tabla de datos.

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

substitute() podría ser necesario para expresiones más largas. Pero en este caso, call() acortaría el código y crearía el mismo resultado cl . Y así 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 haya que 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