Comment trier une trame de données par plusieurs colonnes

1317

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(croissant):

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
Christopher DuBois
la source

Réponses:

1626

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

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 environ 2 ans et plus plus tard: il a juste été demandé comment procéder par index de colonne. La réponse est de simplement passer la ou les colonnes de tri souhaitées à la order()fonction:

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

Dirk Eddelbuettel
la source
@Dirk Eddelbuettel existe-t-il une méthode tout aussi simple pour les matrices?
Jota
14
Devrait fonctionner de la même manière, mais vous ne pouvez pas l'utiliser with. Essayez M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))de créer une matrice M, puis utilisez-la M[order(M[,"a"],-M[,"b"]),]pour la classer sur deux colonnes.
Dirk Eddelbuettel
4
Assez simple :, dd[ order(-dd[,4], dd[,1]), ]mais ne peut pas être utilisé withpour le sous-ensemble basé sur le nom.
Dirk Eddelbuettel
18
J'ai une erreur "argument non valide à l'opérateur unaire" lors de l'exécution du deuxième exemple.
Nailgun
21
L'erreur "argument non valide à l'opérateur unaire" se produit lorsque vous utilisez moins avec une colonne de caractères. Résolvez-le en enveloppant la colonne xtfrm, par exemple dd[ order(-xtfrm(dd[,4]), dd[,1]), ].
Richie Cotton
477

Vos choix

  • order de base
  • arrange de dplyr
  • setorderet setordervdedata.table
  • arrange de plyr
  • sort de taRifx
  • orderBy de doBy
  • sortData de Deducer

La plupart du temps, vous devez utiliser les solutions dplyror data.table, sauf s'il est important de ne pas avoir de dépendances, auquel cas utilisez-les base::order.


J'ai récemment ajouté sort.data.frame à un package CRAN, ce qui le rend compatible avec les classes comme discuté ici: Le meilleur moyen de créer une cohérence générique / méthode pour sort.data.frame?

Par conséquent, étant donné le 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, veuillez me contacter. La discussion sur le domaine public est ici: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Vous pouvez également utiliser la arrange()fonction de plyrcomme Hadley l'a souligné dans le fil ci-dessus:

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 package doBy provoque sortle retour "Les objets suivants sont masqués de" x (position 17) ": b, x, y, z", et le chargement du package Deducer remplace sort.data.frameKevin Wright ou le package 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
)

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: 1567

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: 1694

Notez que doBy prend un peu de temps pour charger le package.

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

Impossible de charger le déducteur. 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 la référence microbienne 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))

tracé de microbenchmark

(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 de la simplicité de pesée par rapport à la vitesse, je devrais donner le feu vert à arrangel' plyremballage . Il a une syntaxe simple et pourtant est presque aussi rapide que les commandes de base R avec leurs machinations alambiquées. Travail Hadley Wickham typiquement brillant. Mon seul reproche, c'est qu'il brise la nomenclature R standard où les objets de tri sont appelés sort(object), mais je comprends pourquoi Hadley l'a fait de cette façon en raison des problèmes discutés dans la question liée ci-dessus.

Ari B. Friedman
la source
3
La fonction de référence microbienne ggplot2 ci-dessus est maintenant disponible en tant que taRifx::autoplot.microbenchmark.
Ari B. Friedman
@ AriB.Friedman Quels sont les intervalles de l'axe y / quelle est l'échelle?
naught101
@ naught101 L'axe y commence à 0. L'échelle doit être en microsecondes.
Ari B. Friedman
2
@AME regarde comment best trié dans l'échantillon. La valeur par défaut est le tri par ordre croissant, vous ne devez donc pas l'envelopper desc. Croissant à la fois: arrange(dd,z,b). Descendant dans les deux: arrange(dd,desc(z),desc(b)).
Ari B. Friedman du
2
Selon ?arrange: "# REMARQUE: les fonctions plyr ne conservent PAS les noms de lignes". Cela rend l'excellente arrange()fonction sous-optimale si l'on veut la conserver row.names.
landroni
149

La réponse de Dirk est excellente. Il met également en évidence une différence clé dans la syntaxe utilisée pour l'indexation de data.frames et data.tables:

## 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 elle peut avoir des conséquences importantes. Surtout si vous écrivez du code de production et / ou que vous vous préoccupez de l'exactitude de vos recherches, il est préférable d'éviter la répétition inutile des 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 problèmes:

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 quarterlyreport. Il devient :

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

OK bien. Aucun problème avec ça. Ensuite, votre patron vous demande d'inclure le rapport du dernier trimestre dans le rapport. Vous passez par votre code, en ajoutant un objet 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 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 voit, mais peut-être pas. Si vous travaillez beaucoup avec les langages de programmation, cette situation peut être familière. C'était une "faute de frappe" direz-vous. Je vais corriger la "faute de frappe" que vous direz à votre patron.

En data.tablenous sommes préoccupés par les petits détails comme celui - ci. Nous avons donc fait quelque chose de simple pour éviter de taper deux fois les noms de variables. Quelque chose de très simple. iest évalué dans le cadre de dddéjà, automatiquement. Tu n'as 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 cela pourrait peut-être vous sauver la nuque un jour. Lorsque vous soupesez les différentes réponses à cette question, envisagez de compter les répétitions de noms de variables comme l'un de vos critères de décision. Certaines réponses ont plusieurs répétitions, d'autres n'en ont pas.

Matt Dowle
la source
9
+1 C'est un excellent point, et j'obtiens un détail de la syntaxe de R qui m'a souvent irrité. J'utilise parfois subset()juste pour éviter d'avoir à faire référence à plusieurs reprises au même objet au cours d'un même appel.
Josh O'Brien
2
@ naught101 La FAQ 1.9 de data.table répond-elle à cela?
Matt Dowle
5
Je suppose que vous pouvez également ajouter la nouvelle setorderfonction ici, car ce fil est l'endroit où nous envoyons tous les ordertypes de dupes.
David Arenburg
125

Il y a beaucoup d'excellentes réponses ici, mais dplyr donne la seule syntaxe dont je me souvienne rapidement et facilement (et maintenant j'utilise 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 du PO:

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
Ben
la source
2
La réponse acceptée ne fonctionne pas lorsque mes colonnes sont ou tapez facteur (ou quelque chose comme ça) et je veux trier de manière décroissante pour cette colonne de facteur suivie d'une colonne entière de façon ascendante. Mais cela fonctionne très bien! Je vous remercie!
Saheel Godhane
10
Pourquoi "seulement"? Je trouve que data.table est dd[order(-z, b)]assez facile à utiliser et à mémoriser.
Matt Dowle
2
D'accord, il n'y a pas grand-chose entre ces deux méthodes, et data.tablec'est une énorme contribution à Rbien d'autres égards également. Je suppose que pour moi, il se pourrait que le fait d'avoir un ensemble de parenthèses de moins (ou un type de parenthèses de moins) dans ce cas réduit la charge cognitive d'une quantité à peine perceptible.
Ben
7
Pour moi, cela revient au fait que arrange()c'est complètement déclaratif, dd[order(-z, b)]non.
Mullefa
83

Le package R data.tablefournit à la fois un ordre rapide et efficace en mémoire de data.tables avec une syntaxe simple (une partie de laquelle Matt a très bien souligné dans sa réponse ). Il y a eu pas mal d'améliorations et aussi une nouvelle fonction setorder()depuis lors. De v1.9.5+, setorder()fonctionne également avec data.frames .

Tout d'abord, nous allons créer un ensemble de données suffisamment grand et comparer les différentes méthodes mentionnées dans les autres réponses, puis répertorier les fonctionnalités de data.table .

Les données:

require(plyr)
require(doBy)
require(data.table)
require(dplyr)
require(taRifx)

set.seed(45L)
dat = data.frame(b = as.factor(sample(c("Hi", "Med", "Low"), 1e8, TRUE)),
                 x = sample(c("A", "D", "C"), 1e8, TRUE),
                 y = sample(100, 1e8, TRUE),
                 z = sample(5, 1e8, TRUE), 
                 stringsAsFactors = FALSE)

Repères:

Les temporisations signalées proviennent de l'exécution system.time(...)de ces fonctions indiquées ci-dessous. Les horaires sont tabulés ci-dessous (dans l'ordre du plus lent au plus rapide).

orderBy( ~ -z + b, data = dat)     ## doBy
plyr::arrange(dat, desc(z), b)     ## plyr
arrange(dat, desc(z), b)           ## dplyr
sort(dat, f = ~ -z + b)            ## taRifx
dat[with(dat, order(-z, b)), ]     ## base R

# convert to data.table, by reference
setDT(dat)

dat[order(-z, b)]                  ## data.table, base R like syntax
setorder(dat, -z, b)               ## data.table, using setorder()
                                   ## setorder() now also works with data.frames 

# R-session memory usage (BEFORE) = ~2GB (size of 'dat')
# ------------------------------------------------------------
# Package      function    Time (s)  Peak memory   Memory used
# ------------------------------------------------------------
# doBy          orderBy      409.7        6.7 GB        4.7 GB
# taRifx           sort      400.8        6.7 GB        4.7 GB
# plyr          arrange      318.8        5.6 GB        3.6 GB 
# base R          order      299.0        5.6 GB        3.6 GB
# dplyr         arrange       62.7        4.2 GB        2.2 GB
# ------------------------------------------------------------
# data.table      order        6.2        4.2 GB        2.2 GB
# data.table   setorder        4.5        2.4 GB        0.4 GB
# ------------------------------------------------------------
  • data.tableLa DT[order(...)]syntaxe était ~ 10x plus rapide que la plus rapide des autres méthodes ( dplyr), tout en consommant la même quantité de mémoire que dplyr.

  • data.table« s setorder()était ~ 14x plus rapide que le plus rapide des autres méthodes ( dplyr), tout en prenant simplement 0.4GB de mémoire supplémentaire . datest maintenant dans l'ordre dont nous avons besoin (car il est mis à jour par référence).

fonctionnalités de data.table:

La vitesse:

  • La commande de data.table est extrêmement rapide car elle implémente la commande radix .

  • La syntaxe DT[order(...)]est optimisée en interne pour utiliser également la commande rapide de data.table . Vous pouvez continuer à utiliser la syntaxe de base R familière mais accélérer le processus (et utiliser moins de mémoire).

Mémoire:

  • La plupart du temps, nous n'avons pas besoin du data.frame ou du data.table d'origine après la réorganisation. Autrement dit, nous attribuons généralement le résultat au même objet, par exemple:

    DF <- DF[order(...)]

    Le problème est que cela nécessite au moins deux fois (2x) la mémoire de l'objet d'origine. Pour être efficace en mémoire , data.table fournit donc également une fonction setorder().

    setorder()réorganise les tableaux de données by reference ( sur place ), sans faire de copies supplémentaires. Il utilise uniquement une mémoire supplémentaire égale à la taille d'une colonne.

Autres caractéristiques:

  1. Il soutient integer, logical, numeric, characteret même bit64::integer64types.

    Notez que factor, Date, POSIXctetc .. classes sont tous integer/ numerictypes en dessous avec des attributs supplémentaires et sont donc pris en charge.

  2. Dans la base R, nous ne pouvons pas utiliser -sur un vecteur de caractères pour trier par cette colonne dans l'ordre décroissant. Au lieu de cela, nous devons utiliser -xtfrm(.).

    Cependant, dans data.table , nous pouvons simplement faire, par exemple, dat[order(-x)]ou setorder(dat, -x).

Arun
la source
Merci pour cette réponse très instructive sur data.table. Cependant, je ne comprends pas ce qu'est la "mémoire de pointe" et comment vous l'avez calculée. Pourriez-vous expliquer s'il vous plaît? Merci !
Julien Navarre
J'ai utilisé Instruments -> allocations et signalé la taille "Tous les tas et toutes les machines virtuelles d'allocation".
Arun
2
@Arun le lien Instruments dans votre commentaire est mort. Vous souhaitez publier une mise à jour?
MichaelChirico
@MichaelChirico Voici un lien vers des informations sur les instruments fabriqués par Apple: developer.apple.com/library/content/documentation/…
n1k31t4
73

Avec cette fonction (très utile) de Kevin Wright , publiée dans la section des astuces du wiki R, cela est facilement réalisable.

sort(dd,by = ~ -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
Christopher DuBois
la source
2
Voir ma réponse pour l'analyse comparative de l'algorithme utilisé dans cette fonction.
Ari B. Friedman
39

ou vous pouvez utiliser le package doBy

library(doBy)
dd <- orderBy(~-z+b, data=dd)
George Dontas
la source
39

Supposons que vous en ayez un data.frame Aet que vous souhaitiez le trier à l'aide d'une colonne appelée xordre décroissant. Appelez le triédata.frame newdata

newdata <- A[order(-A$x),]

Si vous voulez un ordre croissant, remplacez-le "-"par rien. Vous pouvez avoir quelque chose comme

newdata <- A[order(-A$x, A$y, -A$z),]

xet zsont des colonnes data.frame A. Cela signifie trier data.frame Apar ordre xdécroissant, ycroissant et zdécroissant.

Khayelihle
la source
32

si SQL vous vient naturellement, le sqldfpackage se gère ORDER BYcomme Codd le souhaitait.

malecki
la source
7
MJM, merci d'avoir signalé ce paquet. C'est incroyablement flexible et parce que la moitié de mon travail est déjà fait en tirant des bases de données SQL, il est plus facile que d'apprendre une grande partie de la syntaxe moins qu'intuitive de R.
Brandon Bertelsen
31

Alternativement, en utilisant le package Deducer

library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))
Ian Fellows
la source
19

En réponse à un commentaire ajouté dans l'OP pour savoir comment trier par programme:

Utilisation de dplyretdata.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'informations 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

table de données

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
info_seekeR
la source
18

J'ai pris connaissance orderde l'exemple suivant qui m'a ensuite longtemps confondu:

set.seed(1234)

ID        = 1:10
Age       = round(rnorm(10, 50, 1))
diag      = c("Depression", "Bipolar")
Diagnosis = sample(diag, 10, replace=TRUE)

data = data.frame(ID, Age, Diagnosis)

databyAge = data[order(Age),]
databyAge

La seule raison pour laquelle cet exemple fonctionne est parce que le ordertri est effectué par vector Age, et non par la colonne nommée Agedans le data frame data.

Pour voir cela, créez un bloc de données identique en utilisant read.tabledes noms de colonne légèrement différents et sans utiliser aucun des vecteurs ci-dessus:

my.data <- read.table(text = '

  id age  diagnosis
   1  49 Depression
   2  50 Depression
   3  51 Depression
   4  48 Depression
   5  50 Depression
   6  51    Bipolar
   7  49    Bipolar
   8  49    Bipolar
   9  49    Bipolar
  10  49 Depression

', header = TRUE)

La structure de ligne ci-dessus orderne fonctionne plus car il n'y a pas de vecteur nommé age:

databyage = my.data[order(age),]

La ligne suivante fonctionne car ordertrie sur la colonne agedans my.data.

databyage = my.data[order(my.data$age),]

J'ai pensé que cela valait la peine d'être publié étant donné à quel point j'étais confus par cet exemple pendant si longtemps. Si ce message n'est pas jugé approprié pour le fil, je peux le supprimer.

EDIT: 13 mai 2014

Vous trouverez ci-dessous un moyen généralisé de trier un bloc de données par chaque colonne sans spécifier de noms de colonne. Le code ci-dessous montre comment trier de gauche à droite ou de droite à gauche. Cela fonctionne si chaque colonne est numérique. Je n'ai pas essayé avec une colonne de caractères ajoutée.

J'ai trouvé le do.callcode il y a un mois ou deux dans un ancien message sur un site différent, mais seulement après une recherche approfondie et difficile. Je ne suis pas sûr de pouvoir déplacer ce poste maintenant. Le thread actuel est le premier hit pour commander un data.framein R. J'ai donc pensé que ma version développée de ce do.callcode d' origine pourrait être utile.

set.seed(1234)

v1  <- c(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1)
v2  <- c(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1)
v3  <- c(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1)
v4  <- c(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1)

df.1 <- data.frame(v1, v2, v3, v4) 
df.1

rdf.1 <- df.1[sample(nrow(df.1), nrow(df.1), replace = FALSE),]
rdf.1

order.rdf.1 <- rdf.1[do.call(order, as.list(rdf.1)),]
order.rdf.1

order.rdf.2 <- rdf.1[do.call(order, rev(as.list(rdf.1))),]
order.rdf.2

rdf.3 <- data.frame(rdf.1$v2, rdf.1$v4, rdf.1$v3, rdf.1$v1) 
rdf.3

order.rdf.3 <- rdf.1[do.call(order, as.list(rdf.3)),]
order.rdf.3
Mark Miller
la source
4
Cette syntaxe fonctionne si vous stockez vos données dans un data.table, au lieu d'un data.frame: require(data.table); my.dt <- data.table(my.data); my.dt[order(age)]cela fonctionne parce que les noms des colonnes sont mis à disposition entre crochets [].
Frank
Je ne pense pas que le downvote soit nécessaire ici, mais je ne pense pas non plus que cela ajoute beaucoup à la question , en particulier compte tenu de l'ensemble de réponses existant, dont certaines captent déjà l'exigence avec data.frames à utiliser withou $.
A5C1D2H2I1M1N2O1R2T1
1
upvote pour do.callcela simplifie le tri d'un bloc de données multicolonnes. Tout simplement do.call(sort, mydf.obj)et une belle sorte de cascade sera obtenue.
AdamO
17

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

dd <- dd[with(dd, order(-z, b)), ] 
Andrew
la source
13

L'arrangement () dans dplyer est mon option préférée. Utilisez l'opérateur de tuyau et passez de l'aspect le moins important au plus important

dd1 <- dd %>%
    arrange(z) %>%
    arrange(desc(x))
Kaden Killpack
la source
7

Par souci d'exhaustivité, car peu de choses ont été dites sur le tri par numéro de colonne ... On peut certainement affirmer que ce n'est souvent pas souhaitable (car l'ordre des colonnes pourrait changer, ouvrant la voie à des erreurs), mais dans certaines situations spécifiques (lorsque, par exemple, vous avez besoin d'un travail rapide et qu'il n'y a pas un tel risque que les colonnes changent d'ordres), cela peut être la chose la plus raisonnable à faire, en particulier lorsque vous traitez un grand nombre de colonnes.

Dans ce cas, do.call()vient à la rescousse:

ind <- do.call(what = "order", args = iris[,c(5,1,2,3)])
iris[ind, ]

##        Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
##    14           4.3         3.0          1.1         0.1     setosa
##    9            4.4         2.9          1.4         0.2     setosa
##    39           4.4         3.0          1.3         0.2     setosa
##    43           4.4         3.2          1.3         0.2     setosa
##    42           4.5         2.3          1.3         0.3     setosa
##    4            4.6         3.1          1.5         0.2     setosa
##    48           4.6         3.2          1.4         0.2     setosa
##    7            4.6         3.4          1.4         0.3     setosa
##    (...)
Dominic Comtois
la source
6

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

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 des performances:

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
Lars Kotthoff
la source
4
étrange d'ajouter une comparaison de performances lorsque votre méthode est la plus lente ... de toute façon douteuse de la valeur d'utiliser un benchmark sur une ligne à 4data.frame
MichaelChirico
5

Tout comme les trieurs mécaniques de cartes d'il y a longtemps, triez d'abord par la clé la moins significative, puis par la plus importante suivante, etc. Aucune bibliothèque requise, fonctionne avec n'importe quel nombre de clés et n'importe quelle combinaison de clés ascendantes et descendantes.

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

Maintenant, nous sommes prêts à faire la clé la plus importante. 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 c'est certainement simple et fiable

Meule
la source
4

Une autre alternative, en utilisant le rgrpackage:

> library(rgr)
> gx.sort.df(dd, ~ -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
Stéphane Laurent
la source
4

Je luttais avec les solutions ci-dessus lorsque je voulais automatiser mon processus de commande pour n colonnes, dont les noms de colonnes pouvaient être différents à chaque fois. J'ai trouvé une fonction super utile dans le psychpackage pour le faire de manière simple:

dfOrder(myDf, columnIndices)

columnIndicessont les indices d'une ou plusieurs colonnes, dans l'ordre dans lequel vous souhaitez les trier. Plus d'informations ici:

Fonction dfOrder du package 'psych'

AHegde
la source