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
with
. EssayezM <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))
de créer une matriceM
, puis utilisez-laM[order(M[,"a"],-M[,"b"]),]
pour la classer sur deux colonnes.dd[ order(-dd[,4], dd[,1]), ]
mais ne peut pas être utiliséwith
pour le sous-ensemble basé sur le nom.xtfrm
, par exempledd[ order(-xtfrm(dd[,4]), dd[,1]), ]
.Vos choix
order
debase
arrange
dedplyr
setorder
etsetorderv
dedata.table
arrange
deplyr
sort
detaRifx
orderBy
dedoBy
sortData
deDeducer
La plupart du temps, vous devez utiliser les solutions
dplyr
ordata.table
, sauf s'il est important de ne pas avoir de dépendances, auquel cas utilisez-lesbase::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:
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 deplyr
comme Hadley l'a souligné dans le fil ci-dessus: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
sort
le retour "Les objets suivants sont masqués de" x (position 17) ": b, x, y, z", et le chargement du package Deducer remplacesort.data.frame
Kevin Wright ou le package taRifx.Temps médians:
dd[with(dd, order(-z, b)), ]
778dd[order(-dd$z, dd$b),]
788Temps médian: 1567
Temps médian: 862
Temps médian: 1694
Notez que doBy prend un peu de temps pour charger le package.
Impossible de charger le déducteur. Nécessite une console JGR.
Ne semble pas être compatible avec la référence microbienne en raison de l'attachement / détachement.
(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 à
arrange
l'plyr
emballage . 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éssort(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.la source
taRifx::autoplot.microbenchmark
.b
est trié dans l'échantillon. La valeur par défaut est le tri par ordre croissant, vous ne devez donc pas l'envelopperdesc
. Croissant à la fois:arrange(dd,z,b)
. Descendant dans les deux:arrange(dd,desc(z),desc(b))
.?arrange
: "# REMARQUE: les fonctions plyr ne conservent PAS les noms de lignes". Cela rend l'excellentearrange()
fonction sous-optimale si l'on veut la conserverrow.names
.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.frame
s etdata.table
s: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'appellequarterlyreport
. Il devient :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: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.table
nous 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.i
est évalué dans le cadre dedd
déjà, automatiquement. Tu n'as pas besoinwith()
du tout.Au lieu de
c'est juste
Et au lieu de
c'est juste
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.
la source
subset()
juste pour éviter d'avoir à faire référence à plusieurs reprises au même objet au cours d'un même appel.setorder
fonction ici, car ce fil est l'endroit où nous envoyons tous lesorder
types de dupes.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):
Pour le problème du PO:
la source
dd[order(-z, b)]
assez facile à utiliser et à mémoriser.data.table
c'est une énorme contribution àR
bien 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.arrange()
c'est complètement déclaratif,dd[order(-z, b)]
non.Le package R
data.table
fournit à 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 fonctionsetorder()
depuis lors. Dev1.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:
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).data.table
LaDT[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 quedplyr
.data.table
« ssetorder()
était ~ 14x plus rapide que le plus rapide des autres méthodes (dplyr
), tout en prenant simplement 0.4GB de mémoire supplémentaire .dat
est 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:
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éesby 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:
Il soutient
integer
,logical
,numeric
,character
et mêmebit64::integer64
types.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)]
ousetorder(dat, -x)
.la source
Avec cette fonction (très utile) de Kevin Wright , publiée dans la section des astuces du wiki R, cela est facilement réalisable.
la source
ou vous pouvez utiliser le package doBy
la source
Supposons que vous en ayez un
data.frame
A
et que vous souhaitiez le trier à l'aide d'une colonne appeléex
ordre décroissant. Appelez le triédata.frame
newdata
Si vous voulez un ordre croissant, remplacez-le
"-"
par rien. Vous pouvez avoir quelque chose commeoù
x
etz
sont des colonnesdata.frame
A
. Cela signifie trierdata.frame
A
par ordrex
décroissant,y
croissant etz
décroissant.la source
si SQL vous vient naturellement, le
sqldf
package se gèreORDER BY
comme Codd le souhaitait.la source
Alternativement, en utilisant le package Deducer
la source
En réponse à un commentaire ajouté dans l'OP pour savoir comment trier par programme:
Utilisation de
dplyr
etdata.table
dplyr
Utilisez simplement
arrange_
, qui est la version d'évaluation standard pourarrange
.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
la source
J'ai pris connaissance
order
de l'exemple suivant qui m'a ensuite longtemps confondu:La seule raison pour laquelle cet exemple fonctionne est parce que le
order
tri est effectué parvector Age
, et non par la colonne nomméeAge
dans ledata frame data
.Pour voir cela, créez un bloc de données identique en utilisant
read.table
des noms de colonne légèrement différents et sans utiliser aucun des vecteurs ci-dessus:La structure de ligne ci-dessus
order
ne fonctionne plus car il n'y a pas de vecteur nomméage
:La ligne suivante fonctionne car
order
trie sur la colonneage
dansmy.data
.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.call
code 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 undata.frame
inR
. J'ai donc pensé que ma version développée de cedo.call
code d' origine pourrait être utile.la source
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 [].data.frame
s à utiliserwith
ou$
.do.call
cela simplifie le tri d'un bloc de données multicolonnes. Tout simplementdo.call(sort, mydf.obj)
et une belle sorte de cascade sera obtenue.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:
la source
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
la source
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:la source
Par souci d'exhaustivité: vous pouvez également utiliser la
sortByCol()
fonction duBBmisc
package:Comparaison des performances:
la source
data.frame
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.
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.
Ce n'est peut-être pas le plus rapide, mais c'est certainement simple et fiable
la source
Une autre alternative, en utilisant le
rgr
package:la source
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
psych
package pour le faire de manière simple:où
columnIndices
sont 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'
la source