Domanda

Sto cercando di prendere una serie molto ampia di record con più indici, calcolare una statistica aggregata su gruppi determinati da un sottoinsieme degli indici e quindi inserirlo in ogni riga nella tabella. Il problema qui è che queste sono tavoli molto grandi: oltre 10 m di file ciascuna.

Il codice per la riproduzione dei dati è di seguito.

L'idea di base è che ci siano una serie di indici, diciamo IX1, IX2, IX3, ..., IXK. In generale, ne sto scegliendo solo un paio, dicono IX1 e IX2. Quindi, calcolo un'aggregazione di tutte le righe con valori corrispondenti IX1 e IX2 (su tutte le combinazioni che compaiono), per una colonna chiamata val. Per mantenerlo semplice, mi concentrerò su una somma.

Ho provato i seguenti metodi

  1. Tramite matrici sparse: converti i valori in un elenco di coordinate, IE (IX1, IX2, VAL), quindi crea una SparseMatrix - questo riassume tutto, e quindi ho bisogno di convertire solo la rappresentazione della matrice sparsa nell'elenco delle coordinate. Velocità: bene, ma sta facendo più del necessario e non si generalizza a dimensioni più elevate (ad es. IX1, IX2, IX3) o più funzioni generali di una somma.

  2. Uso di lapply e split: Creando un nuovo indice unico per tutti (IX1, IX2, ...) N-tuple, posso quindi usare Split and Applicat. La cosa negativa qui è che l'indice unico è convertito da split In un fattore e questa conversione richiede terribilmente tempo. Provare system({zz <- as.factor(1:10^7)}).

  3. Ora sto provando data.table, tramite un comando come sumDT <- DT[,sum(val),by = c("ix1","ix2")]. Tuttavia, non vedo ancora come posso unire sumDT insieme a DT, oltre a qualcosa di simile DT2 <- merge(DT, sumDT, by = c("ix1","ix2"))

Esiste un metodo più veloce per questo data. merge operazione che ho descritto?

Ho anche provato bigsplit dal bigtabulate pacchetto e alcuni altri metodi. Tutto ciò che si converte in un fattore è praticamente fuori - per quanto posso dire, che il processo di conversione è molto lento.


Codice per generare dati. Naturalmente, è meglio provare un più piccolo N vedere che qualcosa funziona, ma non tutti i metodi si ridimensionano molto bene per 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)
È stato utile?

Soluzione

Bene, è possibile scoprire che fare l'iscrizione non è così male finché il tuo keyS sono impostati correttamente.

Imposta nuovamente il problema:

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"))

Ora puoi calcolare le tue statistiche di riepilogo

info <- DT[, list(summary=sum(val)), by=key(DT)]

E unisci le colonne "il modo di dati. merge

m1 <- DT[info]            ## the data.table way
m2 <- merge(DT, info)     ## if you're just used to merge
identical(m1, m2)
[1] TRUE

Se uno di questi modi di fondere è troppo lento, puoi provare un modo complicato per costruire info A costo della memoria:

info2 <- DT[, list(summary=rep(sum(val), length(val))), by=key(DT)]
m3 <- transform(DT, summary=info2$summary)
identical(m1, m3)
[1] TRUE

Ora vediamo i tempi:

#######################################################################
## 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

Oppure puoi saltare l'intermedio info Costruzione di tabelle se i seguenti non sembrano troppo imperscrutabile per i tuoi gusti:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top