Question

Je suis en train de prendre un très grand nombre de dossiers avec de multiples indices, calculer un agrégat statistique sur les groupes déterminés par un sous-ensemble des indices, puis insérez-la dans chaque ligne de la table.Le problème ici est que ce sont de très grandes tables - plus de 10 millions de lignes chacune.

Code pour reproduire les données ci-dessous.

L'idée de base est qu'il existe un ensemble d'indices, de dire ix1, ix2, ix3, ..., ixK.Généralement, je choisis seulement un couple d'entre eux, dire ix1 et ix2.Ensuite, je calcule une agrégation de toutes les lignes avec une correspondance ix1 et ix2 valeurs (plus de toutes les combinaisons qui apparaissent), pour une colonne appelée val.Pour faire simple, je vais mettre l'accent sur une somme.

J'ai essayé les méthodes suivantes

  1. Via des matrices creuses:convertir les valeurs de coordonnées de la liste, c'est à dire(ix1, ix2, val), puis créer un sparseMatrix - cela résume bien tout, et puis j'ai besoin de convertir en arrière à partir de la matrice creuse de la représentation de l'coordonner la liste.Vitesse:bon, mais il fait plus que ce qui est nécessaire et il ne faut pas généraliser à des dimensions supérieures (par ex.ix1, ix2, ix3) ou plus généralement de fonctions que par une somme.

  2. L'utilisation de lapply et split:par la création d'un nouvel indice, qui est unique pour tous (ix1, ix2, ...) n-tuples, je peux alors utiliser split et à appliquer.La mauvaise, c'est ici que l'index unique est converti par split un facteur, et cette conversion est terriblement chronophage.Essayez system({zz <- as.factor(1:10^7)}).

  3. Je vais maintenant essayer data.table, via une commande comme sumDT <- DT[,sum(val),by = c("ix1","ix2")].Cependant, je n'ai pas encore de voir comment je peux les fusionner sumDT avec DT, autrement que par quelque chose comme DT2 <- merge(DT, sumDT, by = c("ix1","ix2"))

Est-il une méthode plus rapide pour ce type de données.jointure de table que par le biais de la merge l'opération que je viens de décrire?

[J'ai aussi essayé bigsplit à partir de la bigtabulate paquet, et quelques autres méthodes.Tout ce qui le convertit à un facteur est quasiment hors - aussi loin que je peux dire, que la conversion est un processus très lent.]


Code pour générer les données.Naturellement, il est préférable d'essayer un petit N pour voir que quelque chose fonctionne, mais pas toutes les méthodes évoluent très bien pour 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)
Était-ce utile?

La solution

Ainsi, il est possible que vous trouverez que la fusion n'est pas si mal aussi longtemps que votre keys ne sont pas correctement définis.

Permet l'installation de nouveau le problème:

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

Maintenant, vous pouvez calculer votre résumé statistiques

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

Et fusionner les colonnes de "la qualité des données.table façon", ou tout simplement avec 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 l'un de ces modes de fusion est trop lent, vous pouvez essayer une délicate façon de construire info le coût de la mémoire:

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

Maintenant, nous allons voir le calendrier:

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

Ou vous pouvez sauter les intermédiaires info tableau bâtiment si la suite ne semble pas trop impénétrable à vos goûts:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top