web-dev-qa-db-fra.com

Trier rapidement un tableau de données par ordre croissant / décroissant

J'ai un data.table avec environ 3 millions de lignes et 40 colonnes. Je voudrais trier ce tableau par ordre décroissant au sein de groupes comme le code simulé sql suivant:

sort by ascending Year, ascending MemberID, descending Month 

Existe-t-il un moyen équivalent dans data.table de le faire? Jusqu'à présent, je dois le décomposer en 2 étapes:

setkey(X, Year, MemberID)

C'est très rapide et ne prend que quelques secondes.

X <- X[,.SD[order(-Month)],by=list(Year, MemberID)]

Cette étape prend beaucoup plus de temps (5 minutes).

Mise à jour: Quelqu'un a fait un commentaire pour faire X <- X[sort(Year, MemberID, -Month)] et supprimé plus tard. Cette approche semble être beaucoup plus rapide:

user  system elapsed 
5.560  11.242  66.236 

Mon approche: setkey () puis order (-Month)

   user  system elapsed 
816.144   9.648 848.798 

Ma question est maintenant: si je veux résumer par année, MemberId et mois après le tri (année, MemberID, mois), data.table reconnaît-il l'ordre de tri?

Mise à jour 2: pour répondre à Matthew Dowle:

Après avoir défini setkey avec Year, MemberID et Month, j'ai toujours plusieurs enregistrements par groupe. Je voudrais résumer pour chacun des groupes. Ce que je voulais dire était: si j'utilise X [commande (année, ID membre, mois)], la sommation utilise-t-elle la fonctionnalité de recherche binaire de data.table:

monthly.X <- X[, lapply(.SD[], sum), by = list(Year, MemberID, Month)]

Mise à jour 3: Matthew D a proposé plusieurs approches. Le temps d'exécution de la première approche est plus rapide que l'approche order ():

   user  system elapsed 
  7.910   7.750  53.916 

Matthew: ce qui m'a surpris, c'est de convertir le signe du mois prend la plupart du temps. Sans cela, setkey est extrêmement rapide.

50
AdamNYC

Mise à jour du 5 juin 2014:

La version de développement actuelle de data.table v1.9.3 a deux nouvelles fonctions implémentées, à savoir: setorder et setorderv, qui fait exactement ce dont vous avez besoin. Ces fonctions réorganisent le data.table par référence avec la possibilité de choisir un ordre croissant ou décroissant sur chaque colonne pour classer par. Consultez ?setorder Pour plus d'informations.

De plus, DT[order(.)] est également optimisé par défaut pour utiliser l'ordre rapide interne de data.table au lieu de base:::order. Contrairement à setorder, cela fera une copie entière des données, et est donc moins efficace en mémoire, mais sera toujours plus rapide que de fonctionner en utilisant l'ordre de base.

Repères:

Voici une illustration des différences de vitesse en utilisant setorder, l'ordre rapide interne de data.table et avec base:::order:

require(data.table) ## 1.9.3
set.seed(1L)
DT <- data.table(Year     = sample(1950:2000, 3e6, TRUE), 
                 memberID = sample(paste0("V", 1:1e4), 3e6, TRUE), 
                 month    = sample(12, 3e6, TRUE))

## using base:::order
system.time(ans1 <- DT[base:::order(Year, memberID, -month)])
#   user  system elapsed 
# 76.909   0.262  81.266 

## optimised to use data.table's fast order
system.time(ans2 <- DT[order(Year, memberID, -month)])
#   user  system elapsed 
#  0.985   0.030   1.027

## reorders by reference
system.time(setorder(DT, Year, memberID, -month))
#   user  system elapsed 
#  0.585   0.013   0.600 

## or alternatively
## setorderv(DT, c("Year", "memberID", "month"), c(1,1,-1))

## are they equal?
identical(ans2, DT)    # [1] TRUE
identical(ans1, ans2)  # [1] TRUE

Sur ces données, les benchmarks indiquent que l'ordre de data.table est environ ~ 79x plus rapide que base:::order Et setorder est ~ 135x plus rapide que base:::order Ici.

data.table Trie/commande toujours en C-locale. Si vous devez commander dans un autre pays, vous devez alors utiliser DT[base:::order(.)].

Toutes ces nouvelles optimisations et fonctions constituent ensemble FR # 2405 . le support bit64 :: integer64 a également été ajouté .


REMARQUE: Veuillez vous référer à l'historique/révisions pour une réponse et des mises à jour antérieures.

73
Matt Dowle

Le commentaire était le mien, donc je posterai la réponse. Je l'ai supprimé car je n'ai pas pu tester s'il était équivalent à ce que vous aviez déjà. Heureux d'entendre que c'est plus rapide.

X <- X[order(Year, MemberID, -Month)]

Le résumé ne devrait pas dépendre de l'ordre de vos lignes.

13
Matthew Plourde