web-dev-qa-db-fra.com

Pourquoi rbindlist est-il "meilleur" que rbind?

Je suis en train de parcourir la documentation de data.table et a également remarqué, d'après certaines conversations tenues ici-même sur SO) que rbindlist est censé être meilleur que rbind.

J'aimerais savoir pourquoi rbindlist est-il meilleur que rbind et dans quels scénarios rbindlist excelle vraiment par-dessus rbind?

Y a-t-il un avantage en termes d'utilisation de la mémoire?

127
Chinmay Patil

rbindlist est une version optimisée de do.call(rbind, list(...)), connue pour être lente lors de l'utilisation de rbind.data.frame


Où est-ce vraiment Excel

Quelques questions qui montrent où rbindlist brille

Fusion vectorisée rapide de la liste de data.frames par ligne

Problème lors de la conversion d'une longue liste de data.frames (~ 1 million) en single data.frame en utilisant do.call et ldply

Ceux-ci ont des repères qui montrent à quelle vitesse il peut être.


rbind.data.frame est lent, pour une raison

rbind.data.frame Effectue beaucoup de vérifications et correspondra par nom. (c'est-à-dire que rbind.data.frame tiendra compte du fait que les colonnes peuvent être dans des ordres différents et correspondre par nom), rbindlist ne fait pas ce type de vérification et joint par position.

par exemple

do.call(rbind, list(data.frame(a = 1:2, b = 2:3), data.frame(b = 1:2, a = 2:3)))
##    a b
## 1  1 2
## 2  2 3
## 3  2 1
## 4  3 2

rbindlist(list(data.frame(a = 1:5, b = 2:6), data.frame(b = 1:5, a = 2:6)))
##     a b
##  1: 1 2
##  2: 2 3
##  3: 1 2
##  4: 2 3

Quelques autres limitations de rbindlist

Il avait l'habitude de lutter contre factors, à cause d'un bogue corrigé depuis:

rbindlist deux data.tables où l'un a un facteur et l'autre a un type de caractère pour une colonne ( Bogue n ° 265 )

Il a des problèmes avec les noms de colonnes en double

voir Message d'avertissement: dans rbindlist (allargs): NA introduites par la contrainte: bogue possible dans data.table? ( Bug # 2384 )


rbind.data.frame rownames peut être frustrant

rbindlist peut gérer listsdata.frames et data.tables, et renverra un fichier data.table sans noms de domaine

vous pouvez entrer dans une confusion de noms de noms en utilisant do.call(rbind, list(...)) voir

Comment éviter de renommer des lignes lors de l'utilisation de rbind dans do.call?


Efficacité de la mémoire

En termes de mémoire, rbindlist est implémenté dans C, la mémoire est donc efficace, elle utilise setattr pour définir les attributs par référence.

rbind.data.frame Est implémenté dans R, il assigne beaucoup et utilise attr<- (Et class<- Et rownames<-, Ce qui va tous ( en interne) créer des copies du fichier data.frame créé.

147
mnel

Par v1.9.2, rbindlist a beaucoup évolué, implémentant de nombreuses fonctionnalités, notamment:

  • Choisir le SEXPTYPE le plus élevé de colonnes lors de la liaison - implémenté dans v1.9.2 fermeture FR # 2456 et Bug # 4981 .
  • Manipulation correcte des colonnes factor - d'abord implémentée dans v1.8.10 fermant Bug # 2650 et étendu à la reliure ordonnée avec soin dans v1.9.2 également, en fermant FR # 4856 et Bug # 5019 .

De plus, dans v1.9.2, rbind.data.table a également obtenu un argument fill, qui permet de lier en remplissant les colonnes manquantes, implémenté dans R.

Maintenant dans v1.9.3, il y a encore plus d'améliorations sur ces fonctionnalités existantes:

  • rbindlist gagne un argument use.names, qui est par défaut FALSE pour la compatibilité avec les versions antérieures.
  • rbindlist gagne également un argument fill, qui par défaut est également FALSE pour la compatibilité avec les versions antérieures.
  • Ces fonctionnalités sont toutes implémentées en C et écrites avec soin pour ne pas compromettre la vitesse tout en ajoutant des fonctionnalités.
  • Étant donné que rbindlist peut désormais correspondre aux noms et remplir les colonnes manquantes, rbind.data.table appelle simplement rbindlist maintenant. La seule différence est que use.names=TRUE par défaut pour rbind.data.table, par compatibilité avec les versions antérieures.

rbind.data.frame ralentit un peu, principalement en raison des copies (ce que @mnel indique également) qui pourraient être évitées (en passant en C). Je pense que ce n'est pas la seule raison. L'implémentation pour vérifier/faire correspondre les noms de colonne dans rbind.data.frame pourrait également devenir plus lente lorsqu'il y a beaucoup de colonnes par data.frame et qu'il y a beaucoup de telles data.frames à lier (comme indiqué dans le repère ci-dessous).

Cependant, rbindlist manque (certaines fonctionnalités) (comme la vérification des niveaux de facteurs ou les noms correspondants) a très peu de poids (ou aucun) pour que cela soit plus rapide que rbind.data.frame. C'est parce qu'ils ont été soigneusement mis en œuvre en C, optimisés pour la vitesse et la mémoire.

Voici un point de repère qui met en évidence l'efficacité de la liaison lors de la mise en correspondance par noms de colonnes, en utilisant également rbindlist '_ use.names de v1.9.3. Le jeu de données est constitué de 10000 données.frames de taille 10 * 500.

NB: ce repère a été mis à jour pour inclure une comparaison avec dplyr '_ bind_rows

library(data.table) # 1.11.5, 2018-06-02 00:09:06 UTC
library(dplyr) # 0.7.5.9000, 2018-06-12 01:41:40 UTC
set.seed(1L)
names = paste0("V", 1:500)
cols = 500L
foo <- function() {
    data = as.data.frame(setDT(lapply(1:cols, function(x) sample(10))))
    setnames(data, sample(names))
}
n = 10e3L
ll = vector("list", n)
for (i in 1:n) {
    .Call("Csetlistelt", ll, i, foo())
}

system.time(ans1 <- rbindlist(ll))
#  user  system elapsed 
# 1.226   0.070   1.296 

system.time(ans2 <- rbindlist(ll, use.names=TRUE))
#  user  system elapsed 
# 2.635   0.129   2.772 

system.time(ans3 <- do.call("rbind", ll))
#   user  system elapsed 
# 36.932   1.628  38.594 

system.time(ans4 <- bind_rows(ll))
#   user  system elapsed 
# 48.754   0.384  49.224 

identical(ans2, setDT(ans3)) 
# [1] TRUE
identical(ans2, setDT(ans4))
# [1] TRUE

La liaison des colonnes en tant que telle sans vérification des noms ne prenait que 1,3, tandis que la vérification des noms de colonnes et la liaison appropriée ne prenaient que 1,5 seconde de plus. Comparé à la solution de base, il est 14 fois plus rapide et 18 fois plus rapide que la version de dplyr.

41
Arun