r data.table

Usar nombres dinámicos de columnas en `data.table`



(1)

Quiero calcular la media de cada una de varias columnas en una tabla de datos, agrupada por otra columna. Mi pregunta es similar a otras dos preguntas sobre SO ( one y two ) pero no pude aplicarlas a mi problema.

Aquí hay un ejemplo:

library(data.table) dtb <- fread(input = "condition,var1,var2,var3 one,100,1000,10000 one,101,1001,10001 one,102,1002,10002 two,103,1003,10003 two,104,1004,10004 two,105,1005,10005 three,106,1006,10006 three,107,1007,10007 three,108,1008,10008 four,109,1009,10009 four,110,1010,10010") dtb # condition var1 var2 var3 # 1: one 100 1000 10000 # 2: one 101 1001 10001 # 3: one 102 1002 10002 # 4: two 103 1003 10003 # 5: two 104 1004 10004 # 6: two 105 1005 10005 # 7: three 106 1006 10006 # 8: three 107 1007 10007 # 9: three 108 1008 10008 # 10: four 109 1009 10009 # 11: four 110 1010 10010

El cálculo de cada media individual es fácil; por ejemplo, para "var1": dtb[ , mean(var1), by = condition] . Pero esto rápidamente se vuelve engorroso si hay muchas variables y necesita escribirlas todas. Por lo tanto, dtb[, list(mean(var1), mean(var2), mean(var3)), by = condition] es indeseable. Necesito que los nombres de las columnas sean dinámicos y deseo terminar con algo como esto:

condition var1 var2 var3 1: one 101.0 1001.0 10001.0 2: two 104.0 1004.0 10004.0 3: three 107.0 1007.0 10007.0 4: four 109.5 1009.5 10009.5


debe usar .SDcols (especialmente si tiene demasiadas columnas y necesita que una operación en particular se realice solo en un subconjunto de las columnas (aparte de las columnas de las variables de agrupación).

dtb[, lapply(.SD, mean), by=condition, .SDcols=2:4] # condition var1 var2 var3 # 1: one 101.0 1001.0 10001.0 # 2: two 104.0 1004.0 10004.0 # 3: three 107.0 1007.0 10007.0 # 4: four 109.5 1009.5 10009.5

También podría obtener todos los nombres de columnas que desea tomar como primeros en una variable y luego pasarlos a .SDcols esta manera:

keys <- setdiff(names(dtb), "condition") # keys = var1, var2, var3 dtb[, lapply(.SD, mean), by=condition, .SDcols=keys]

Editar: Como señaló correctamente Matthew Dowle, dado que requiere que se calcule en todas las demás columnas después de agruparlas por condition , puede hacer:

dtb[, lapply(.SD, mean), by=condition]

Edición de David: (que fue rechazada): Lea más sobre .SD desde esta publicación . Encuentro que esto es relevante aquí. Gracias @David.

Edición 2: data.table que tiene una data.table con 1000 filas y 301 columnas (una columna para agrupar y 300 columnas numéricas):

require(data.table) set.seed(45) dt <- data.table(grp = sample(letters[1:15], 1000, replace=T)) m <- matrix(rnorm(300*1000), ncol=300) dt <- cbind(dt, m) setkey(dt, "grp")

y usted quería encontrar la media de las columnas, digamos, 251: 300 solo,

  • puede calcular la media de todas las columnas y luego subconjuntar estas columnas (lo que no es muy eficiente ya que calculará en la totalidad de los datos).

    dt.out <- dt[, lapply(.SD, mean), by=grp] dim(dt.out) # 15 * 301, not efficient.

  • usted puede filtrar el data.table primero a solo estas columnas y luego calcular la media (que de nuevo no es necesariamente la mejor solución ya que tiene que crear un subconjunto adicional de datos.table cada vez que desee operaciones en ciertas columnas).

    dt.sub <- dt[, c(1, 251:300), with=FALSE] setkey(dt.sub, "grp") dt.out <- dt.sub[, lapply(.SD, mean), by=grp]

  • puede especificar cada una de las columnas una por una como lo haría normalmente (pero esto es deseable para data.tables más pequeños)

    # if you just need one or few columns dt.out <- dt[, list(m.v251 = mean(V251)), by = grp]

Entonces, ¿cuál es la mejor solución? La respuesta es .Sdcols .

Como dice la documentación, para un data.table x , .SDcols especifica las columnas que se incluyen en .SD .

Esto básicamente filtra implícitamente las columnas que se pasarán a .SD en lugar de crear un subconjunto (como lo hicimos antes), ¡solo que es MUY eficiente y RÁPIDO!

¿Cómo podemos hacer esto?

  • Al especificar los números de columna:

    dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = 251:300] dim(dt.out) # 15 * 51 (what we expect)

  • O alternativamente al especificar el ID de la columna:

    ids <- paste0("V", 251:300) # get column ids dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = ids] dim(dt.out) # 15 * 51 (what we expect)

Acepta nombres de columnas y números como argumentos. En ambos casos, .SD se proporcionará solo con estas columnas que hemos especificado.

Espero que esto ayude.