Pregunta

Tengo una tabil de datos DT con una columna nombrada RF y muchas columnas con un subrayado _en eso. Quiero recorrer todas esas columnas con un subrayado y restar el RF columna de ella. Sin embargo, estoy atascado. Parece que todo en el RHS del:= operador en un data.table no funciona con variables dinámicas.

Aquí está mi DT y la salida deseada (codificado):

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30)
#Desired output
DT[ , S_1 := S_1 - RF]
DT[ , S_2 := S_2 - RF]
DT
      RF S_1 S_2
 [1,]  1  10  20
 [2,]  2  10  20
 [3,]  3  10  20
...

Sin embargo, quiero que esto sea más flexible, es decir, recorrer cada columna con "_" en su nombre y restar RF:

#1. try: Does not work; Interestingly, the i on the LHS of := is interpreted as the column i, but on the RHS of
#:= it is interpreted as 2 and 3, respectively
for (i in grep("_", names(DT))){
  DT[ , i:= i - 1, with=FALSE]
}
DT
          RF  S_1 S_2
 [1,]  1   1   2
 [2,]  2   1   2
 [3,]  3   1   2
...

#2. try: Work with parse and eval
for (i in grep("_", names(DT), value=TRUE)){
  DT[ , eval(parse(text=i)):= eval(parse(text=i)) - RF]
}
#Error in eval(expr, envir, enclos) : object 'S_1' not found

Cualquier sugerencia de cómo hacerlo sería genial.

EDITAR: Tan pronto como publiqué la pregunta, pensé para mí mismo: ¿Por qué estás trabajando con el := Operador en primer lugar, y efectivamente, me di cuenta de que no tengo que hacerlo. Esto funciona y no necesita un bucle:

DT[, grep("_", names(DT)), with=FALSE] - DT[, RF]

Lo siento por eso. Sin embargo, dejo la pregunta abierta porque todavía estoy interesado en por qué mi enfoque con el := El operador no funciona. Entonces, tal vez alguien pueda ayudarme allí.

¿Fue útil?

Solución

Estabas en el camino correcto con tu segundo intento. Aquí hay un enfoque que usa substitute para construir la expresión que se pasa como el 'j' argumentar DT[ , j ].

for (i in grep("_", names(DT), value=TRUE)){
    e <- substitute(X := X - RF, list(X = as.symbol(i)))
    DT[ , eval(e)]
}
DT
#     RF S_1 S_2
# [1,]  1  10  20
# [2,]  2  10  20
# [3,]  3  10  20
# [4,]  4  10  20
# [5,]  5  10  20

También podría usar una expresión de LHS en lugar de un símbolo:

for (i in grep("_", names(DT), value=TRUE))
    DT[, (i) := get(i)-RF]

Otros consejos

Una solución que desafortunadamente descubrí después de publicar la pregunta es la siguiente:

DT[, .SD, .SDcols = patterns('_')] - DT[, RF]

Esto también funciona en un entorno más complicado en el que hay columnas adicionales que desea mantener, pero con un esfuerzo adicional:

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30,
                 addCol = rnorm(10)) #Column that should not be subtracted by RF, but still kept in DT

DT <- cbind(DT[, .SD, .SDcols = patterns("_")] - DT[, RF], addCol = DT[, addCol])

Actualizado para usar set()+..; set es poderoso (ver ediciones para un intento anterior).

varnames <- grep("_", names(DT), value=TRUE)
set(DT, j = varnames, value = DT[, ..varnames] - DT[, RF])
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top