So sortieren Sie einen Datenrahmen nach mehreren Spalten

1316

Ich möchte einen data.frame nach mehreren Spalten sortieren. Zum Beispiel möchte ich mit dem Datenrahmen unten nach Spalte z(absteigend) und dann nach Spalte b(aufsteigend) sortieren :

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
quelle

Antworten:

1626

Sie können die order()Funktion direkt verwenden, ohne auf Add-On-Tools zurückgreifen zu müssen. Sehen Sie sich diese einfachere Antwort an, bei der ein Trick direkt am Anfang des example(order)Codes verwendet wird:

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

Bearbeiten Sie einige 2+ Jahre später: Es wurde nur gefragt, wie dies nach Spaltenindex zu tun ist. Die Antwort besteht darin, einfach die gewünschten Sortierspalten an die order()Funktion zu übergeben:

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> 

anstatt den Namen der Spalte zu verwenden (und with()für einen einfacheren / direkteren Zugriff).

Dirk Eddelbuettel
quelle
@Dirk Eddelbuettel gibt es eine ähnlich einfache Methode für Matrizen?
Jota
14
Sollte genauso funktionieren, kann aber nicht verwendet werden with. Versuchen Sie M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b"))), eine Matrix zu erstellen M, und M[order(M[,"a"],-M[,"b"]),]ordnen Sie sie dann in zwei Spalten an.
Dirk Eddelbuettel
4
Einfach genug :, dd[ order(-dd[,4], dd[,1]), ]kann aber nicht withfür namenbasierte Teilmengen verwendet werden.
Dirk Eddelbuettel
18
Beim Ausführen des zweiten Beispiels ist der Fehler "Ungültiges Argument für unären Operator" aufgetreten.
Nagelpistole
21
Der Fehler "Ungültiges Argument für unären Operator" tritt auf, wenn Sie Minus mit einer Zeichenspalte verwenden. Lösen Sie es durch das Einwickeln der Säule in xtfrm, zum Beispiel dd[ order(-xtfrm(dd[,4]), dd[,1]), ].
Richie Cotton
477

Deine Entscheidungen

  • order von base
  • arrange von dplyr
  • setorderund setordervvondata.table
  • arrange von plyr
  • sort von taRifx
  • orderBy von doBy
  • sortData von Deducer

In den meisten Fällen sollten Sie die dplyroder data.table-Lösungen verwenden, es sei denn, es ist wichtig, keine Abhängigkeiten zu haben. In diesem Fall sollten Sie sie verwenden base::order.


Ich habe kürzlich sort.data.frame zu einem CRAN-Paket hinzugefügt, um es klassenkompatibel zu machen, wie hier beschrieben: Beste Methode zum Erstellen einer generischen / Methodenkonsistenz für sort.data.frame?

Daher können Sie angesichts des data.frame dd wie folgt sortieren:

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 )

Wenn Sie einer der ursprünglichen Autoren dieser Funktion sind, kontaktieren Sie mich bitte. Die Diskussion über Public Domaininess finden Sie hier: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Sie können auch die arrange()Funktion von verwenden, plyrwie Hadley im obigen Thread ausgeführt hat:

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

Benchmarks: Beachten Sie, dass ich jedes Paket in eine neue R-Sitzung geladen habe, da es viele Konflikte gab. Insbesondere das Laden des doBy-Pakets führt sortdazu, dass "Die folgenden Objekte werden von 'x (Position 17)' maskiert: b, x, y, z" zurückgegeben wird und das Laden des Deducer-Pakets sort.data.framevon Kevin Wright oder dem taRifx-Paket überschrieben wird .

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

Medianzeiten:

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

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

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

Medianzeit: 1.567

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

Medianzeit: 862

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

Medianzeit: 1.694

Beachten Sie, dass doBy einige Zeit zum Laden des Pakets benötigt.

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

Deducer konnte nicht geladen werden. Benötigt JGR-Konsole.

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

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

Scheint aufgrund des Anbringens / Entfernens nicht mit Microbenchmark kompatibel zu sein.


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

Mikrobenchmark-Plot

(Linien erstrecken sich vom unteren zum oberen Quartil, Punkt ist der Median)


Angesichts dieser Ergebnisse und der Abwägung von Einfachheit und Geschwindigkeit müsste ich arrangedem plyrPaket zustimmen . Es hat eine einfache Syntax und ist dennoch fast so schnell wie die Basis-R-Befehle mit ihren verschlungenen Machenschaften. Typisch brillante Hadley Wickham Arbeit. Mein einziger Kritikpunkt ist, dass es die Standard-R-Nomenklatur bricht, nach der Sortierobjekte aufgerufen werden sort(object), aber ich verstehe, warum Hadley dies aufgrund von Problemen getan hat, die in der oben verlinkten Frage erörtert wurden.

Ari B. Friedman
quelle
3
Die obige Mikrobenchmark-Funktion ggplot2 ist jetzt als verfügbar taRifx::autoplot.microbenchmark.
Ari B. Friedman
@ AriB.Friedman Was sind die Intervalle der y-Achse / wie groß ist die Skala?
Naught101
@ naught101 Die y-Achse beginnt bei 0. Die Skalierung sollte Mikrosekunden betragen.
Ari B. Friedman
2
@AME schau dir an, wie bim Beispiel sortiert ist. Die Standardeinstellung ist Sortieren nach aufsteigend, sodass Sie sie einfach nicht einpacken desc. In beiden aufsteigend : arrange(dd,z,b). Absteigend in beiden : arrange(dd,desc(z),desc(b)).
Ari B. Friedman
2
Per ?arrange: "# HINWEIS: plyr Funktionen erhalten kein row.names". Dies macht die hervorragende arrange()Funktion suboptimal, wenn man sie behalten will row.names.
Landroni
149

Dirks Antwort ist großartig. Es wird auch ein wesentlicher Unterschied in der Syntax für die Indizierung von data.frames und data.tables hervorgehoben:

## 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)]

Der Unterschied zwischen den beiden Anrufen ist gering, kann jedoch wichtige Konsequenzen haben. Insbesondere wenn Sie Produktionscode schreiben und / oder bei Ihrer Recherche auf Korrektheit achten, ist es am besten, unnötige Wiederholungen von Variablennamen zu vermeiden. data.table hilft Ihnen dabei.

Hier ist ein Beispiel dafür, wie die Wiederholung von Variablennamen zu Problemen führen kann:

Lassen Sie uns den Kontext von Dirks Antwort ändern und sagen, dass dies Teil eines größeren Projekts ist, bei dem es viele Objektnamen gibt und diese lang und aussagekräftig sind. statt ddheißt es quarterlyreport. Es wird :

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

Ok, gut. Daran ist nichts auszusetzen. Als nächstes bittet Sie Ihr Chef, den Bericht des letzten Quartals in den Bericht aufzunehmen. Sie gehen Ihren Code durch, fügen an lastquarterlyreportverschiedenen Stellen ein Objekt hinzu und erhalten (wie um alles in der Welt?) Am Ende Folgendes:

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

Das haben Sie nicht gemeint, aber Sie haben es nicht erkannt, weil Sie es schnell gemacht haben und es sich auf einer Seite mit ähnlichem Code befindet. Der Code fällt nicht um (keine Warnung und kein Fehler), weil R denkt, dass es das ist, was Sie gemeint haben. Sie würden hoffen, dass jeder, der Ihren Bericht liest, ihn entdeckt, aber vielleicht nicht. Wenn Sie viel mit Programmiersprachen arbeiten, ist diese Situation möglicherweise allzu vertraut. Es war ein "Tippfehler", den Sie sagen werden. Ich werde den "Tippfehler" beheben, den Sie Ihrem Chef sagen werden.

In sind data.tablewir besorgt über winzige Details wie dieses. Wir haben also etwas Einfaches getan, um zu vermeiden, dass Variablennamen zweimal eingegeben werden. Etwas sehr Einfaches. iwird im Rahmen von ddbereits automatisch ausgewertet . Du brauchst überhaupt nicht with().

Anstatt

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

es ist nur

dd[order(-z, b)]

Und statt

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

es ist nur

quarterlyreport[order(-z,b)]

Es ist ein sehr kleiner Unterschied, aber es könnte eines Tages Ihren Hals retten. Berücksichtigen Sie beim Abwägen der verschiedenen Antworten auf diese Frage die Wiederholungen von Variablennamen als eines Ihrer Entscheidungskriterien. Einige Antworten haben einige Wiederholungen, andere keine.

Matt Dowle
quelle
9
+1 Dies ist ein großartiger Punkt und geht auf ein Detail der Syntax von R ein, das mich oft irritiert hat. Ich verwende manchmal subset()nur, um zu vermeiden, dass ich innerhalb eines einzelnen Aufrufs wiederholt auf dasselbe Objekt verweisen muss.
Josh O'Brien
2
@ naught101 Beantwortet data.table FAQ 1.9 das?
Matt Dowle
5
Ich denke, Sie könnten die neue setorderFunktion auch hier hinzufügen , da wir in diesem Thread alle orderTyp-Dupes senden .
David Arenburg
125

Hier gibt es viele hervorragende Antworten, aber dplyr gibt die einzige Syntax an, an die ich mich schnell und einfach erinnern kann (und die ich jetzt sehr oft verwende):

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)

Für das Problem des 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
Ben
quelle
2
Die akzeptierte Antwort funktioniert nicht, wenn meine Spalten oder Typfaktor (oder so ähnlich) sind und ich für diese Faktorspalte absteigend sortieren möchte, gefolgt von einer Ganzzahlspalte in aufsteigender Weise. Aber das funktioniert gut! Vielen Dank!
Saheel Godhane
10
Wieso nur"? Ich finde data.table dd[order(-z, b)]ziemlich einfach zu bedienen und erinnere mich.
Matt Dowle
2
Einverstanden, es gibt nicht viel zwischen diesen beiden Methoden und es data.tableist auch Rin vielerlei Hinsicht ein großer Beitrag dazu . Ich nehme an, es könnte sein, dass ein Satz weniger Klammern (oder ein Klammertyp weniger) in diesem Fall die kognitive Belastung um einen kaum wahrnehmbaren Betrag verringert.
Ben
7
Für mich kommt es darauf an, dass arrange()das völlig deklarativ dd[order(-z, b)]ist, nicht.
Mullefa
83

Das R-Paket data.tablebietet sowohl eine schnelle als auch speichereffiziente Bestellung von data.tables mit einer einfachen Syntax (ein Teil davon hat Matt in seiner Antwort sehr gut hervorgehoben ). Es gab viele Verbesserungen und auch eine neue Funktionsetorder() . Von v1.9.5+, setorder()arbeitet auch mit data.frames .

Zuerst erstellen wir einen Datensatz, der groß genug ist, und vergleichen die verschiedenen Methoden, die aus anderen Antworten hervorgehen, und listen dann die Funktionen von data.table auf .

Daten:

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)

Benchmarks:

Die angegebenen Timings stammen aus den system.time(...)unten gezeigten Funktionen. Die Zeiten sind unten tabellarisch aufgeführt (in der Reihenfolge der langsamsten bis schnellsten).

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.tableDie DT[order(...)]Syntax war ~ 10x schneller als die schnellste aller anderen Methoden ( dplyr), während die gleiche Speichermenge verbraucht wurde wiedplyr .

  • data.table‚s setorder()war ~ 14x schneller als die schnellste von anderen Methoden ( dplyr), während der Einnahme von nur 0.4GB zusätzlichem Speicher . datist jetzt in der Reihenfolge, die wir benötigen (da es durch Bezugnahme aktualisiert wird).

data.table Funktionen:

Geschwindigkeit:

  • Die Bestellung von data.table ist extrem schnell, da sie die Radix-Reihenfolge implementiert .

  • Die Syntax DT[order(...)]ist intern optimiert, um auch die schnelle Bestellung von data.table zu nutzen . Sie können weiterhin die bekannte Basis-R-Syntax verwenden, aber den Prozess beschleunigen (und weniger Speicher verwenden).

Erinnerung:

  • In den meisten Fällen benötigen wir nach der Neuordnung weder den ursprünglichen data.frame noch die data.table . Das heißt, wir weisen das Ergebnis normalerweise demselben Objekt zu, zum Beispiel:

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

    Das Problem ist, dass dies mindestens das Doppelte (2x) des Speichers des ursprünglichen Objekts erfordert. Um speichereffizient zu sein , bietet data.table daher auch eine Funktion setorder().

    setorder()ordnet data.tables by reference ( an Ort und Stelle ) neu an, ohne zusätzliche Kopien anzufertigen . Es wird nur zusätzlicher Speicher verwendet, der der Größe einer Spalte entspricht.

Andere Eigenschaften:

  1. Es unterstützt integer, logical, numeric, characterund sogar bit64::integer64Typen.

    Beachten Sie, dass factor, Date, POSIXctetc .. Klassen sind alle integer/numeric Typen unterhalb mit zusätzlichen Attributen und werden daher auch nicht unterstützt.

  2. In Basis R können wir nicht verwenden - Zeichenvektor verwenden, um nach dieser Spalte in absteigender Reihenfolge zu sortieren. Stattdessen müssen wir verwenden -xtfrm(.).

    In data.table können wir dies jedoch beispielsweise dat[order(-x)]oder tun setorder(dat, -x).

Arun
quelle
Vielen Dank für diese sehr lehrreiche Antwort zu data.table. Ich verstehe jedoch nicht, was "Spitzenspeicher" ist und wie Sie es berechnet haben. Könnten Sie das bitte erklären? Vielen Dank !
Julien Navarre
Ich habe Instrumente -> Zuordnungen verwendet und die Größe "Alle Heap- und Zuordnungs-VMs" angegeben.
Arun
2
@Arun the Instruments Link in Ihrem Kommentar ist tot. Möchtest du ein Update veröffentlichen?
MichaelChirico
@ MichaelChirico Hier ist ein Link zu Informationen über Instrumente von Apple: developer.apple.com/library/content/documentation/…
n1k31t4
73

Mit dieser (sehr hilfreichen) Funktion von Kevin Wright , die im Abschnitt "Tipps" des R-Wikis veröffentlicht wurde, ist dies leicht zu erreichen.

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
quelle
2
Siehe meine Antwort zum Benchmarking des in dieser Funktion verwendeten Algorithmus.
Ari B. Friedman
39

oder Sie können das Paket doBy verwenden

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

Angenommen, Sie haben eine data.frame Aund möchten diese nach einer Spalte sortieren, die als xabsteigende Reihenfolge bezeichnet wird. Rufen Sie die sortiertedata.frame newdata

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

Wenn Sie eine aufsteigende Reihenfolge wünschen, ersetzen Sie diese "-"durch nichts. Sie können so etwas haben

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

wo xund zsind einige Spalten in data.frame A. Dies bedeutet Sortieren data.frame Anach xabsteigend, yaufsteigend und zabsteigend.

Khayelihle
quelle
32

Wenn SQL für Sie selbstverständlich ist, wird das sqldfPaket ORDER BYwie von Codd beabsichtigt behandelt.

Malecki
quelle
7
MJM, vielen Dank für den Hinweis auf dieses Paket. Es ist unglaublich flexibel und da die Hälfte meiner Arbeit bereits durch das Abrufen von SQL-Datenbanken erledigt wird, ist es einfacher, als viel von Rs weniger als intuitiver Syntax zu lernen.
Brandon Bertelsen
31

Alternativ können Sie das Paket Deducer verwenden

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

Als Antwort auf einen im OP hinzugefügten Kommentar zum programmgesteuerten Sortieren:

Verwenden von dplyrunddata.table

library(dplyr)
library(data.table)

dplyr

Verwenden Sie einfach arrange_die Standard-Evaluierungsversion für 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)

Weitere Informationen hier: https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

Es ist besser, eine Formel zu verwenden, da sie auch die Umgebung erfasst, in der ein Ausdruck ausgewertet wird

Datentabelle

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
quelle
18

Ich habe ordermit folgendem Beispiel davon erfahren, was mich dann lange verwirrt hat:

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

Der einzige Grund, warum dieses Beispiel funktioniert, ist die orderSortierung nach der vector Ageund nicht nach der Agein der Spalte genannten Spalte data frame data.

Um dies zu sehen, erstellen Sie einen identischen Datenrahmen read.tablemit leicht unterschiedlichen Spaltennamen und ohne Verwendung eines der oben genannten Vektoren:

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)

Die obige Linienstruktur orderfunktioniert nicht mehr, da kein Vektor mit dem Namen age:

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

Die folgende Zeile funktioniert, weil orderdie Spalte agein sortiert wird my.data.

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

Ich dachte, das wäre es wert, veröffentlicht zu werden, da ich so lange von diesem Beispiel verwirrt war. Wenn dieser Beitrag für den Thread nicht geeignet ist, kann ich ihn entfernen.

EDIT: 13. Mai 2014

Im Folgenden finden Sie eine allgemeine Methode zum Sortieren eines Datenrahmens nach jeder Spalte, ohne Spaltennamen anzugeben. Der folgende Code zeigt, wie von links nach rechts oder von rechts nach links sortiert wird. Dies funktioniert, wenn jede Spalte numerisch ist. Ich habe es nicht mit einer hinzugefügten Zeichenspalte versucht.

Ich habe den do.callCode vor ein oder zwei Monaten in einem alten Beitrag auf einer anderen Site gefunden, aber erst nach umfangreicher und schwieriger Suche. Ich bin mir nicht sicher, ob ich diesen Beitrag jetzt verschieben könnte. Der vorliegende Thread ist der erste Treffer für die Bestellung eines data.frameIn R. Daher dachte ich, meine erweiterte Version dieses Originalcodes do.callkönnte nützlich sein.

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
quelle
4
Diese Syntax funktioniert, wenn Sie Ihre Daten in einer data.table anstelle eines data.frame speichern: require(data.table); my.dt <- data.table(my.data); my.dt[order(age)]Dies funktioniert, weil die Spaltennamen in den Klammern [] verfügbar gemacht werden.
Frank
Ich denke nicht, dass die Abwertung hier notwendig ist, aber ich denke auch nicht, dass dies viel zur vorliegenden Frage beiträgt , insbesondere angesichts der vorhandenen Antworten, von denen einige bereits die Anforderung mit data.frames erfassen , entweder withoder zu verwenden $.
A5C1D2H2I1M1N2O1R2T1
1
Mit upvote können Sie do.calleinen mehrspaltigen Datenrahmen in kurzer Zeit sortieren. Einfach do.call(sort, mydf.obj)und eine schöne Kaskadensorte wird man haben.
AdamO
17

Dirks Antwort ist gut, aber wenn Sie die Sortierung beibehalten möchten, möchten Sie die Sortierung wieder auf den Namen dieses Datenrahmens anwenden. Verwenden Sie den Beispielcode:

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

Das arrangieren () in dplyer ist meine Lieblingsoption. Verwenden Sie den Rohrbetreiber und wechseln Sie vom am wenigsten wichtigen zum wichtigsten Aspekt

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

Nur der Vollständigkeit halber, da nicht viel über das Sortieren nach Spaltennummern gesagt wurde ... Es kann sicherlich argumentiert werden, dass dies oft nicht wünschenswert ist (weil sich die Reihenfolge der Spalten ändern könnte, was den Weg zu Fehlern ebnet), aber In bestimmten Situationen (wenn Sie beispielsweise eine schnelle Arbeit erledigen müssen und kein derartiges Risiko besteht, dass Spalten die Reihenfolge ändern) ist dies möglicherweise am sinnvollsten, insbesondere wenn Sie mit einer großen Anzahl von Spalten arbeiten.

In diesem Fall do.call()kommt zur Rettung:

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
quelle
6

Der Vollständigkeit halber: Sie können auch die sortByCol()Funktion aus dem BBmiscPaket verwenden:

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

Leistungsvergleich:

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
quelle
4
Es ist seltsam, einen Leistungsvergleich hinzuzufügen, wenn Ihre Methode die langsamste ist. Trotzdem ist der Wert der Verwendung eines Benchmarks für eine 4-reihige data.frame
zweifelhaft
5

Genau wie bei den mechanischen Kartensortierern von vor langer Zeit, sortieren Sie zuerst nach dem niedrigstwertigen Schlüssel, dann nach dem nächstwertigsten usw. Keine Bibliothek erforderlich, funktioniert mit einer beliebigen Anzahl von Schlüsseln und einer beliebigen Kombination von aufsteigenden und absteigenden Schlüsseln.

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

Jetzt sind wir bereit, den wichtigsten Schlüssel zu tun. Die Sortierung ist stabil, und alle Bindungen im wichtigsten Schlüssel wurden bereits gelöst.

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

Dies ist vielleicht nicht die schnellste, aber sicherlich einfach und zuverlässig

Rick
quelle
4

Eine andere Alternative, die das rgrPaket verwendet:

> 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
quelle
4

Ich hatte Probleme mit den oben genannten Lösungen, als ich meinen Bestellvorgang für n Spalten automatisieren wollte, deren Spaltennamen jedes Mal unterschiedlich sein können. Ich habe eine super hilfreiche Funktion aus dem psychPaket gefunden, um dies auf einfache Weise zu tun:

dfOrder(myDf, columnIndices)

Wo columnIndicessind Indizes einer oder mehrerer Spalten in der Reihenfolge, in der Sie sie sortieren möchten? Weitere Informationen hier:

dfOrder-Funktion aus dem 'psych'-Paket

AHegde
quelle