R:Les tableaux et les insertions de données.table
-
28-10-2019 - |
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
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.
L'utilisation de
lapply
etsplit
: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 parsplit
un facteur, et cette conversion est terriblement chronophage.Essayezsystem({zz <- as.factor(1:10^7)})
.Je vais maintenant essayer
data.table
, via une commande commesumDT <- DT[,sum(val),by = c("ix1","ix2")]
.Cependant, je n'ai pas encore de voir comment je peux les fusionnersumDT
avecDT
, autrement que par quelque chose commeDT2 <- 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)
La solution
Ainsi, il est possible que vous trouverez que la fusion n'est pas si mal aussi longtemps que votre key
s 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