R: tabulaciones e inserciones con datos.
-
28-10-2019 - |
Pregunta
Estoy tratando de tomar un conjunto muy grande de registros con múltiples índices, calcular una estadística agregada en grupos determinados por un subconjunto de los índices y luego insertelo en cada fila de la tabla. El problema aquí es que estas son tablas muy grandes: más de 10 m filas cada una.
El código para reproducir los datos está a continuación.
La idea básica es que hay un conjunto de índices, digamos IX1, IX2, IX3, ..., IXK. En general, estoy eligiendo solo un par de ellos, digamos IX1 e IX2. Luego, calculo una agregación de todas las filas con valores de IX1 e Ix2 coincidentes (en todas las combinaciones que aparecen), para una columna llamada val
. Para mantenerlo simple, me centraré en una suma.
He probado los siguientes métodos
A través de matrices dispersas: convierta los valores en una lista de coordenadas, es decir (IX1, IX2, Val), luego cree una Sparsematrix, esto resume bien todo, y luego solo necesito volver a convertir de la representación de la matriz dispersa a la lista de coordenadas. Velocidad: Bien, pero está haciendo más de lo necesario y no se generaliza a dimensiones más altas (por ejemplo, IX1, IX2, IX3) o más funciones generales que una suma.
Uso de
lapply
ysplit
: Al crear un nuevo índice que sea único para todas (ix1, ix2, ...) n-tuples, puedo usar Split y aplicar. Lo malo aquí es que el índice único se convierte ensplit
en un factor, y esta conversión lleva mucho tiempo. Probarsystem({zz <- as.factor(1:10^7)})
.Ahora estoy intentando
data.table
, a través de un comando comosumDT <- DT[,sum(val),by = c("ix1","ix2")]
. Sin embargo, todavía no veo cómo puedo fusionarsumDT
conDT
, aparte de algo comoDT2 <- merge(DT, sumDT, by = c("ix1","ix2"))
¿Existe un método más rápido para este datos de datos que a través del a través del merge
Operación que he descrito?
También lo he intentado bigsplit
desde el bigtabulate
paquete y algunos otros métodos. Cualquier cosa que se convierta en un factor está más o menos fuera, por lo que puedo decir, ese proceso de conversión es muy lento.
Código para generar datos. Naturalmente, es mejor probar un N
Para ver que algo funciona, pero no todos los métodos escalan muy bien para N
>> 1000.
N <- 10^7
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DF <- data.frame(ix1 = ix1, ix2 = ix2, ix3 = ix3, val = val)
DF <- DF[order(DF[,1],DF[,2],DF[,3]),]
DT <- as.data.table(DF)
Solución
Bueno, es posible que encuentres que hacer la fusión no es tan malo siempre que tu key
s están correctamente establecidos.
Vamos a configurar el problema nuevamente:
N <- 10^6 ## not 10^7 because RAM is tight right now
set.seed(2011)
ix1 <- 1 + floor(rexp(N, 0.01))
ix2 <- 1 + floor(rexp(N, 0.01))
ix3 <- 1 + floor(rexp(N, 0.01))
val <- runif(N)
DT <- data.table(ix1=ix1, ix2=ix2, ix3=ix3, val=val, key=c("ix1", "ix2"))
Ahora puede calcular sus estadísticas de resumen
info <- DT[, list(summary=sum(val)), by=key(DT)]
Y fusionar las columnas "la forma data.table", o solo con merge
m1 <- DT[info] ## the data.table way
m2 <- merge(DT, info) ## if you're just used to merge
identical(m1, m2)
[1] TRUE
Si alguna de esas formas de fusionar es demasiado lento, puede probar una forma difícil de construir info
A costa de la memoria:
info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)]
m3 <- transform(DT, summary=info2$summary)
identical(m1, m3)
[1] TRUE
Ahora veamos el momento:
#######################################################################
## Using data.table[ ... ] or merge
system.time(info <- DT[, list(summary=sum(val)), by=key(DT)])
user system elapsed
0.203 0.024 0.232
system.time(DT[info])
user system elapsed
0.217 0.078 0.296
system.time(merge(DT, info))
user system elapsed
0.981 0.202 1.185
########################################################################
## Now the two parts of the last version done separately:
system.time(info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)])
user system elapsed
0.574 0.040 0.616
system.time(transform(DT, summary=info2$summary))
user system elapsed
0.173 0.093 0.267
O puedes omitir el intermedio info
Construcción de la mesa si lo siguiente no parece demasiado inescrutable para sus gustos:
system.time(m5 <- DT[ DT[, list(summary=sum(val)), by=key(DT)] ])
user system elapsed
0.424 0.101 0.525
identical(m5, m1)
# [1] TRUE