Forma correcta/más rápida para remodelar un data.table
(4)
Esta característica ahora se implementa en data.table (a partir de la versión 1.8.11), como se puede ver en la respuesta anterior de Zach.
Acabo de ver este gran fragmento de código de Arun aquí en SO . Así que supongo que hay una solución de datos. Aplicado a este problema:
library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=1e6),
y=c("A","B"),
v=sample(1:100,12))
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
# edit (mnel) to avoid setNames which creates a copy
# when calling `names<-` inside the function
out[, as.list(setattr(SUM, ''names'', y)), by=list(x)]
})
x A B
1: 1 26499966 28166677
2: 2 26499978 28166673
3: 3 26500056 28166650
Esto da los mismos resultados que el enfoque de DWin:
tapply(DT$v,list(DT$x, DT$y), FUN=sum)
A B
1 26499966 28166677
2 26499978 28166673
3 26500056 28166650
Además, es rápido:
system.time({
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out[, as.list(setattr(SUM, ''names'', y)), by=list(x)]})
## user system elapsed
## 0.64 0.05 0.70
system.time(tapply(DT$v,list(DT$x, DT$y), FUN=sum))
## user system elapsed
## 7.23 0.16 7.39
ACTUALIZAR
Para que esta solución también funcione para conjuntos de datos no balanceados (es decir, algunas combinaciones no existen), primero debe ingresarlas en la tabla de datos:
library(data.table)
set.seed(1234)
DT <- data.table(x=c(rep(c(1,2,3),each=4),3,4), y=c("A","B"), v=sample(1:100,14))
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
setkey(out, x, y)
intDT <- expand.grid(unique(out[,x]), unique(out[,y]))
setnames(intDT, c("x", "y"))
out <- out[intDT]
out[, as.list(setattr(SUM, ''names'', y)), by=list(x)]
Resumen
Combinando los comentarios con lo anterior, esta es la solución de 1 línea:
DT[, sum(v), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
setNames(as.list(V1), paste(y)), by = x]
También es fácil modificar esto para tener más que solo la suma, por ejemplo:
DT[, list(sum(v), mean(v)), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
setNames(as.list(c(V1, V2)), c(paste0(y,".sum"), paste0(y,".mean"))), by = x]
# x A.sum B.sum A.mean B.mean
#1: 1 72 123 36.00000 61.5
#2: 2 84 119 42.00000 59.5
#3: 3 187 96 62.33333 48.0
#4: 4 NA 81 NA 81.0
Tengo una tabla de datos en R:
library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))
DT
x y v
[1,] 1 A 12
[2,] 1 B 62
[3,] 1 A 60
[4,] 1 B 61
[5,] 2 A 83
[6,] 2 B 97
[7,] 2 A 1
[8,] 2 B 22
[9,] 3 A 99
[10,] 3 B 47
[11,] 3 A 63
[12,] 3 B 49
Puedo sumar fácilmente la variable v por los grupos en data.table:
out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out
x y SUM
[1,] 1 A 72
[2,] 1 B 123
[3,] 2 A 84
[4,] 2 B 119
[5,] 3 A 162
[6,] 3 B 96
Sin embargo, me gustaría tener los grupos (y) como columnas, en lugar de filas. Puedo lograr esto usando la reshape
:
out <- reshape(out,direction=''wide'',idvar=''x'', timevar=''y'')
out
x SUM.A SUM.B
[1,] 1 72 123
[2,] 2 84 119
[3,] 3 162 96
¿Hay una manera más eficiente de remodelar los datos después de agregarlos? ¿Hay alguna manera de combinar estas operaciones en un solo paso, utilizando las operaciones data.table?
El paquete data.table
implementa funciones de melt/dcast
más melt/dcast
(en C). También tiene características adicionales al permitir derretir y moldear columnas múltiples . Por favor, vea la nueva remodelación eficiente usando data.tables en Github.
Las funciones de fusión / difusión para data.table han estado disponibles desde v1.9.0 y las características incluyen:
No es necesario cargar el paquete
reshape2
antes de la conversión. Pero si desea cargarlo para otras operaciones, cárguelo antes de cargardata.table
.dcast
es también un S3 genérico. No más dedcast.data.table()
. Solo usadcast()
.melt
:es capaz de fundirse en columnas de tipo ''lista''.
gana
variable.factor
yvalue.factor
que por defecto sonTRUE
yFALSE
respectivamente para la compatibilidad conreshape2
. Esto permite controlar directamente el tipo de salida devariable
columnas devariable
yvalue
(como factores o no).melt.data.table
na.rm = TRUE
está optimizado internamente para eliminar NA directamente durante la fusión y, por lo tanto, es mucho más eficiente.NUEVO:
melt
puede aceptar una lista parameasure.vars
y las columnas especificadas en cada elemento de la lista se combinarán juntas. Esto se facilita aún más mediante el uso depatterns()
. Ver viñeta o?melt
.
dcast
:acepta múltiples
fun.aggregate
y multiplevalue.var
. Ver viñeta o?dcast
.use la función
rowid()
directamente en la fórmula para generar una columna id, que a veces se requiere para identificar las filas de manera única. Ver? Dcast.
Puntos de referencia antiguos:
-
melt
: 10 millones de filas y 5 columnas, 61.3 segundos reducido a 1.2 segundos. -
dcast
: 1 millón de filas y 4 columnas, 192 segundos reducidos a 3.6 segundos.
-
Recordatorio de Colonia (diciembre de 2013) presentación diapositiva 32: ¿Por qué no enviar una dcast
extracción reshape2
para reshape2
?
Los objetos Data.table heredan de ''data.frame'' para que pueda usar tapply:
> tapply(DT$v,list(DT$x, DT$y), FUN=sum)
AA BB
a 72 123
b 84 119
c 162 96
Puede usar dcast
de la biblioteca reshape2
. Aquí está el código
# DUMMY DATA
library(data.table)
mydf = data.table(
x = rep(1:3, each = 4),
y = rep(c(''A'', ''B''), times = 2),
v = rpois(12, 30)
)
# USE RESHAPE2
library(reshape2)
dcast(mydf, x ~ y, fun = sum, value_var = "v")
NOTA: la solución de tapply
sería mucho más rápida.