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

Resuelto frankc asked hace 54 años • 6 respuestas

¿Cómo se hace referencia a las variables en a data.tablesi los nombres de las variables se almacenan en un vector de caracteres? Por ejemplo, esto funciona para 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 esperaba que lo hiciera).

frankc avatar Jan 01 '70 08:01 frankc
Aceptado

Dos formas de seleccionar variables mediante programación:

  1. with = FALSE:

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

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

Para obtener una descripción más detallada de la notación 'punto punto' ( ..), consulte Nuevas funciones en 1.10.2 (actualmente no se describe en el texto de ayuda).

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

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

Este último se conoce como columna plonk , porque reemplaza todo el vector de columna por referencia. Si iestuviera presente un subconjunto, se subasignaría por referencia. Los pares (colname)son una abreviatura introducida en la versión v1.9.4 en CRAN en octubre de 2014. Aquí está la noticia :

El uso with = FALSEde with :=ahora está obsoleto en todos los casos, dado que :=durante algún tiempo se ha preferido envolver el LHS de con paréntesis.

colVar = "col1"
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

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

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

Y para responder más preguntas en el comentario, aquí hay una forma (como siempre, hay muchas formas):

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

O puede que le resulte más fácil leer, escribir y depurar solo en evalun archivo paste, similar a construir una declaración 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 con frecuencia, 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.table1.8.2 optimiza automáticamente jla eficiencia, puede ser preferible utilizar este evalmétodo. El get()in jimpide algunas optimizaciones, por ejemplo.

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

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66
Matt Dowle avatar Sep 12 '2012 16:09 Matt Dowle

*En realidad, esta no es una respuesta, pero no tengo suficiente credibilidad 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 funcionar. No tengo ni idea de su rendimiento. ¿Alguna sugerencia de mejora? ¿Es seguro asumir que una nueva columna sin nombre siempre recibirá 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))

Observe que puedo hacer referencia a él muy bien en sum() pero parece que no puedo lograr que se asigne 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.

efh0888 avatar Jun 30 '2015 16:06 efh0888

Recupere varias columnas de data.table mediante una variable o función:

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]

que todos rinden

   that whatever
1:    1        1
2:    2        2

El camino me parece .SDcolsel más elegante.

C.K avatar May 05 '2020 08:05 C.K

Con la versión de desarrollo 1.14.3,tabla de datosha obtenido una nueva interfaz para programación en data.table , consulte el elemento 10 en Nuevas funciones . Utiliza el nuevo env =parámetro.

library(data.table) # development version 1.14.3 used
dt <- data.table(col1 = 1:3)
colname <- "col1"

dt[, cn := cn + 3L, env = list(cn = colname)][]
    col1
   <int>
1:     4
2:     5
3:     6
Uwe avatar Dec 09 '2021 08:12 Uwe

Para varias columnas y una función aplicada a los valores de las columnas.

Al actualizar los valores de una función, el RHS debe ser un objeto de lista, por lo que usar un bucle .SDcon lapplyserá suficiente.

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
#         a           b          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)
#         a           b          c1 
# "numeric"   "numeric" "character" 
Sathish avatar Apr 17 '2017 04:04 Sathish