Seleccionar / asignar a data.table cuando los nombres de las variables se almacenan en un vector de caracteres

¿Cómo se refiere a las variables en una data.table si los nombres de las variables se almacenan en un vector de caracteres? Por ejemplo, esto funciona para un data.frame :

 df <- data.frame(col1 = 1:3) colname <- "col1" df[colname] <- 4:6 df # col1 # 1 4 # 2 5 # 3 6 

¿Cómo puedo realizar esta misma operación para una tabla de datos, ya sea con o sin := notación? Lo obvio de dt[ , list(colname)] no funciona (ni lo esperaba).

Dos formas de seleccionar las variables programáticamente:

  1. with = FALSE :

     DT = data.table(col1 = 1:3) colname = "col1" DT[, colname, with = FALSE] # col1 # 1: 1 # 2: 2 # 3: 3 
  2. ‘punto punto’ ( .. ) prefijo:

     DT[, ..colname] # col1 # 1: 1 # 2: 2 # 3: 3 

Para una descripción más detallada de la notación ‘dot dot’ ( .. ), consulte Nuevas características en 1.10.2 (actualmente no se describe en el texto de ayuda).

Para asignar variable (s), ajuste el LHS de := entre paréntesis:

 DT[, (colname) := 4:6] # col1 # 1: 4 # 2: 5 # 3: 6 

Esta última se conoce como columna plonk , porque reemplaza el vector completo de la columna por referencia. Si un subconjunto i estaba presente, se subasignaría por referencia. Los parens alrededor (colname) es una taquigrafía introducida en la versión v1.9.4 en CRAN Oct 2014. Aquí está la noticia :

Usar with = FALSE con := ahora está obsoleto en todos los casos, dado que se prefirió envolver el LHS de := con paréntesis durante algún tiempo.

 colVar = "col1" DT[, colVar := 1, with = FALSE] # deprecated, still works silently DT[, (colVar) := 1] # please change to this DT[, c("col1", "col2") := 1] # no change DT[, 2:4 := 1] # no change DT[, c("col1","col2") := list(sum(a), mean(b)] # no change DT[, `:=`(...), by = ...] # no change 

Ver también la sección de Detalles en ?`:=` :

 DT[i, (colnamevector) := value] # [...] The parens are enough to stop the LHS being a symbol 

Y para responder más preguntas en comentarios, aquí hay una forma (como de costumbre, hay muchas maneras):

 DT[, colname := cumsum(get(colname)), with = FALSE] # col1 # 1: 4 # 2: 9 # 3: 15 

o bien, es posible que le resulte más fácil leer, escribir y depurar solo para eval y paste , similar a la construcción de una statement SQL dinámica para enviar a un servidor:

 expr = paste0("DT[,",colname,":=cumsum(",colname,")]") expr # [1] "DT[,col1:=cumsum(col1)]" eval(parse(text=expr)) # col1 # 1: 4 # 2: 13 # 3: 28 

Si haces eso mucho, puedes definir una función auxiliar EVAL :

 EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2)) EVAL("DT[,",colname,":=cumsum(",colname,")]") # col1 # 1: 4 # 2: 17 # 3: 45 

Ahora que data.table 1.8.2 optimiza automáticamente j para la eficiencia, puede ser preferible usar el método eval . El get() en j evita algunas optimizaciones, por ejemplo.

O bien, hay un set() . Una forma baja, funcional y funcional de := , que estaría bien aquí. Ver ?set .

 set(DT, j = colname, value = cumsum(DT[[colname]])) DT # col1 # 1: 4 # 2: 21 # 3: 66 

* Esta no es una respuesta realmente, pero no tengo suficiente credibilidad en la calle para publicar comentarios: /

De todos modos, para cualquiera que esté buscando crear una nueva columna en una tabla de datos con un nombre almacenado en una variable, tengo lo siguiente para trabajar. No tengo idea de su rendimiento. ¿Alguna sugerencia para mejorar? ¿Es seguro asumir que una nueva columna sin nombre siempre tendrá el nombre V1?

 colname < - as.name("users") # Google Analytics query is run with chosen metric and resulting data is assigned to DT DT2 <- DT[, sum(eval(colname, .SD)), by = country] setnames(DT2, "V1", as.character(colname)) 

Tenga en cuenta que puedo hacer referencia muy bien en la sum () pero parece que no puedo asignarlo en el mismo paso. Por cierto, la razón por la que necesito hacer esto es que colname se basará en la entrada del usuario en una aplicación Shiny.

Para columnas múltiples y una función aplicada en valores de columna.

Al actualizar los valores de una función, el RHS debe ser un objeto de lista, por lo que usar un bucle en .SD con lapply hará el truco.

El siguiente ejemplo convierte columnas enteras en columnas numéricas

 a1 < - data.table(a=1:5, b=6:10, c1=letters[1:5]) sapply(a1, class) # show classes of columns # ab c1 # "integer" "integer" "character" # column name character vector nm <- c("a", "b") # Convert columns a and b to numeric type a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ] sapply(a1, class) # ab c1 # "numeric" "numeric" "character"