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

  1. 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.

  2. Uso de lapply y split: 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 en split en un factor, y esta conversión lleva mucho tiempo. Probar system({zz <- as.factor(1:10^7)}).

  3. Ahora estoy intentando data.table, a través de un comando como sumDT <- DT[,sum(val),by = c("ix1","ix2")]. Sin embargo, todavía no veo cómo puedo fusionar sumDT con DT, aparte de algo como DT2 <- 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)
¿Fue útil?

Solución

Bueno, es posible que encuentres que hacer la fusión no es tan malo siempre que tu keys 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top