sort - trier r




Comment trier un dataframe par colonne(s)? (10)

Je veux trier un data.frame par plusieurs colonnes. Par exemple, avec le data.frame ci-dessous je voudrais trier par colonne z (décroissant) puis par colonne b (ascendant):

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
dd
    b x y z
1  Hi A 8 1
2 Med D 3 1
3  Hi A 9 1
4 Low C 9 2

Vos choix

  • order depuis la base
  • arrange partir de dplyr
  • setorder et setorderv de data.table
  • arrange partir de plyr
  • sort partir de taRifx
  • orderBy par doBy
  • sortData de Deducer

La plupart du temps, vous devez utiliser les solutions dplyr ou data.table , à moins d'avoir des dépendances sans aucune importance, auquel cas utilisez base::order .

J'ai récemment ajouté sort.data.frame à un paquet CRAN, le rendant compatible avec la classe comme discuté ici: Meilleure façon de créer une cohérence générique / méthode pour sort.data.frame?

Par conséquent, étant donné le fichier data.frame dd, vous pouvez trier comme suit:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(taRifx)
sort(dd, f= ~ -z + b )

Si vous êtes l'un des auteurs originaux de cette fonction, contactez-moi. Discussion sur le domaine public est ici: http://chat..com/transcript/message/1094290#1094290

Vous pouvez également utiliser la fonction « arrange() plyr comme l'a souligné Hadley dans le fil plyr :

library(plyr)
arrange(dd,desc(z),b)

Benchmarks: Notez que j'ai chargé chaque paquet dans une nouvelle session R car il y avait beaucoup de conflits. En particulier, le chargement du paquet doBy provoque le retour du sort "Les objets suivants sont masqués depuis 'x (position 17)': b, x, y, z", et le chargement du paquet sort.data.frame écrase sort.data.frame de Kevin Wright ou le paquet taRifx.

#Load each time
dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(microbenchmark)

# Reload R between benchmarks
microbenchmark(dd[with(dd, order(-z, b)), ] ,
    dd[order(-dd$z, dd$b),],
    times=1000
)

Les temps médians:

dd[with(dd, order(-z, b)), ] 778

dd[order(-dd$z, dd$b),] 788

library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)

Temps médian: 1 567

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)

Temps médian: 862

library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)

Temps médian: 1 694

Notez que doBy prend beaucoup de temps pour charger le paquet.

library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)

Impossible de charger Deducer. Nécessite une console JGR.

esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}

microbenchmark(esort(dd, -z, b),times=1000)

Ne semble pas être compatible avec Microbenchmark en raison de l'attachement / détachement.

m <- microbenchmark(
  arrange(dd,desc(z),b),
  sort(dd, f= ~-z+b ),
  dd[with(dd, order(-z, b)), ] ,
  dd[order(-dd$z, dd$b),],
  times=1000
  )

uq <- function(x) { fivenum(x)[4]}  
lq <- function(x) { fivenum(x)[2]}

y_min <- 0 # min(by(m$time,m$expr,lq))
y_max <- max(by(m$time,m$expr,uq)) * 1.05

p <- ggplot(m,aes(x=expr,y=time)) + coord_cartesian(ylim = c( y_min , y_max )) 
p + stat_summary(fun.y=median,fun.ymin = lq, fun.ymax = uq, aes(fill=expr))

(les lignes s'étendent du quartile inférieur au quartile supérieur, le point est la médiane)

Compte tenu de ces résultats et en pesant la simplicité contre la vitesse, je devrais donner le signe de la tête pour arrange dans le paquet de plyr . Il a une syntaxe simple et est presque aussi rapide que les commandes de base R avec leurs machinations compliquées. Typiquement brillant travail Hadley Wickham. Mon seul reproche est que ça casse la nomenclature R standard où les objets de tri sont appelés par sort(object) , mais je comprends pourquoi Hadley l'a fait de cette façon en raison des problèmes discutés dans la question ci-dessus.


Alternativement, en utilisant le paquet Deducer

library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))

En réponse à un commentaire ajouté dans le PO pour savoir comment trier par programme:

Utilisation de dplyr et data.table

library(dplyr)
library(data.table)

dplyr

Utilisez simplement arrange_ , qui est la version d'évaluation standard pour arrange .

df1 <- tbl_df(iris)
#using strings or formula
arrange_(df1, c('Petal.Length', 'Petal.Width'))
arrange_(df1, ~Petal.Length, ~Petal.Width)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.4         3.9          1.3         0.4  setosa
7           5.5         3.5          1.3         0.2  setosa
8           4.4         3.0          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...


#Or using a variable
sortBy <- c('Petal.Length', 'Petal.Width')
arrange_(df1, .dots = sortBy)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.5         3.5          1.3         0.2  setosa
7           4.4         3.0          1.3         0.2  setosa
8           4.4         3.2          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...

#Doing the same operation except sorting Petal.Length in descending order
sortByDesc <- c('desc(Petal.Length)', 'Petal.Width')
arrange_(df1, .dots = sortByDesc)

plus d'infos ici: https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

Il est préférable d'utiliser la formule car elle capture également l'environnement pour évaluer une expression dans

data.table

dt1 <- data.table(iris) #not really required, as you can work directly on your data.frame
sortBy <- c('Petal.Length', 'Petal.Width')
sortType <- c(-1, 1)
setorderv(dt1, sortBy, sortType)
dt1
     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  1:          7.7         2.6          6.9         2.3 virginica
  2:          7.7         2.8          6.7         2.0 virginica
  3:          7.7         3.8          6.7         2.2 virginica
  4:          7.6         3.0          6.6         2.1 virginica
  5:          7.9         3.8          6.4         2.0 virginica
 ---                                                            
146:          5.4         3.9          1.3         0.4    setosa
147:          5.8         4.0          1.2         0.2    setosa
148:          5.0         3.2          1.2         0.2    setosa
149:          4.3         3.0          1.1         0.1    setosa
150:          4.6         3.6          1.0         0.2    setosa

Il y a beaucoup d'excellentes réponses ici, mais dplyr donne la seule syntaxe que je peux rapidement et facilement mémoriser (et que j'utilise maintenant très souvent):

library(dplyr)
# sort mtcars by mpg, ascending... use desc(mpg) for descending
arrange(mtcars, mpg)
# sort mtcars first by mpg, then by cyl, then by wt)
arrange(mtcars , mpg, cyl, wt)

Pour le problème de l'OP:

arrange(dd, desc(z),  b)

    b x y z
1 Low C 9 2
2 Med D 3 1
3  Hi A 8 1
4  Hi A 9 1

La réponse de Dirk est bonne mais si vous avez besoin que le tri soit persistant, vous voudrez appliquer le tri sur le nom de cette trame de données. En utilisant l'exemple de code:

dd <- dd[with(dd, order(-z, b)), ] 

La réponse de Dirk est géniale. Il met également en évidence une différence clé dans la syntaxe utilisée pour indexer data.frame s et data.table s:

## The data.frame way
dd[with(dd, order(-z, b)), ]

## The data.table way: (7 fewer characters, but that's not the important bit)
dd[order(-z, b)]

La différence entre les deux appels est faible, mais cela peut avoir des conséquences importantes. Surtout si vous écrivez du code de production et / ou êtes préoccupé par l'exactitude de vos recherches, il est préférable d'éviter la répétition inutile de noms de variables. data.table vous aide à le faire.

Voici un exemple de la façon dont la répétition des noms de variables peut vous causer des ennuis:

Changeons le contexte de la réponse de Dirk, et disons que cela fait partie d'un plus grand projet où il y a beaucoup de noms d'objets et ils sont longs et significatifs; au lieu de dd ça s'appelle le rapport quarterlyreport . Il devient :

quarterlyreport[with(quarterlyreport,order(-z,b)),]

OK bien. Aucun problème avec cela. Ensuite, votre patron vous demande d'inclure le rapport du dernier trimestre dans le rapport. Vous passez par votre code, en ajoutant un objet dernier rapport lastquarterlyreport à divers endroits et en quelque sorte (comment diable?) Vous vous retrouvez avec ceci:

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

Ce n'est pas ce que vous vouliez dire, mais vous ne l'avez pas repéré parce que vous l'avez fait rapidement et qu'il est niché sur une page de code similaire. Le code ne tombe pas (pas d'avertissement et pas d'erreur) car R pense que c'est ce que vous vouliez dire. Vous espérez que celui qui lit votre rapport le remarque, mais peut-être pas. Si vous travaillez beaucoup avec les langages de programmation alors cette situation peut être familière. C'était une "faute de frappe" que vous direz. Je vais corriger la "faute de frappe" que vous allez dire à votre patron.

Dans data.table nous sommes préoccupés par de petits détails comme celui-ci. Nous avons donc fait quelque chose de simple pour éviter de taper des noms de variables deux fois. Quelque chose de très simple. i évalué dans le cadre de dd , automatiquement. Vous n'avez pas besoin with() du tout.

Au lieu de

dd[with(dd, order(-z, b)), ]

c'est juste

dd[order(-z, b)]

Et au lieu de

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

c'est juste

quarterlyreport[order(-z,b)]

C'est une très petite différence, mais ça pourrait sauver votre cou un jour. Lorsque vous évaluez les différentes réponses à cette question, envisagez de compter les répétitions de noms de variables parmi vos critères de décision. Certaines réponses ont quelques répétitions, d'autres n'en ont pas.


Par souci d'exhaustivité: vous pouvez également utiliser la fonction sortByCol() du package BBmisc :

library(BBmisc)
sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE))
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

Comparaison de performance:

library(microbenchmark)
microbenchmark(sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE)), times = 100000)
median 202.878

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=100000)
median 148.758

microbenchmark(dd[with(dd, order(-z, b)), ], times = 100000)
median 115.872

Si SQL vous vient naturellement, sqldf gère ORDER BY comme Codd destiné.


Tout comme les trieuses de cartes mécaniques d'antan, d'abord trier par la clé la moins significative, puis la suivante la plus significative, etc. Aucune bibliothèque requise, fonctionne avec un nombre quelconque de touches et toute combinaison de touches ascendantes et descendantes.

 dd <- dd[order(dd$b, decreasing = FALSE),]

Nous sommes maintenant prêts à faire la clé la plus significative. Le tri est stable et tous les liens dans la clé la plus significative ont déjà été résolus.

dd <- dd[order(dd$z, decreasing = TRUE),]

Ce n'est peut-être pas le plus rapide, mais il est certainement simple et fiable


Vous pouvez utiliser la fonction order() directement sans avoir recours à des outils complémentaires - voir cette réponse plus simple qui utilise une astuce tout en haut du code exemple:

R> dd[with(dd, order(-z, b)), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

Modifier quelques 2+ ans plus tard: Il a juste été demandé comment faire cela par index de colonne. La réponse est simplement de passer la (les) colonne (s) de tri désirée (s) à la fonction order() :

R> dd[ order(-dd[,4], dd[,1]), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1
R> 

plutôt que d'utiliser le nom de la colonne (et with() pour un accès plus facile / plus direct).







r-faq