Frage Wie sortiere ich einen Datenrahmen nach mehreren Spalten?


Ich möchte ein data.frame nach mehreren Spalten sortieren. Mit dem unten stehenden data.frame möchte ich zum Beispiel nach Spalten sortieren z (absteigend) dann nach Spalte b (aufsteigend):

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

1126
2017-08-18 21:33


Ursprung


Antworten:


Du kannst den ... benutzen order() Funktion direkt, ohne auf Add-On-Tools zurückgreifen zu müssen - siehe diese einfachere Antwort, die einen Trick direkt von oben verwendet 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

Bearbeiten Sie etwas mehr als 2 Jahre später:  Es wurde nur gefragt, wie dies nach Spaltenindex zu tun ist. Die Antwort ist, einfach die gewünschte (n) Sortierspalte (n) an die order() Funktion:

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 (und with() für leichteren / direkteren Zugang).


1416
2017-08-18 21:51



Deine Entscheidungen

  • order von base
  • arrange von dplyr
  • setorder und setorderv von data.table
  • arrange von plyr
  • sort von taRifx
  • orderBy von doBy
  • sortData von Deducer

Die meiste Zeit solltest du das benutzen dplyr oder data.table Lösungen, wenn keine Abhängigkeiten wichtig sind, in diesem Fall verwenden base::order.


Ich habe vor kurzem sort.data.frame zu einem CRAN-Paket hinzugefügt, so dass es wie hier besprochen kompatibel ist: Der beste Weg, generische / methodische Konsistenz für sort.data.frame zu erstellen?

Wenn Sie also data.frame dd verwenden, können Sie 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 die öffentliche Sphäre ist hier: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Sie können auch die arrange() Funktion von plyr wie Hadley im obigen Thread darauf hingewiesen 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 verursacht das Laden des DoBy-Pakets sort zurückgeben "Die folgenden Objekte werden von 'x (Position 17)' maskiert: b, x, y, z" und das Laden des Deducer-Pakets wird überschrieben sort.data.frame von Kevin Wright oder dem taRifx-Paket.

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

Mediane Zeiten:

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

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

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

Mittlere Zeit: 1.567

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

Mittlere Zeit: 862

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

Mittlere Zeit: 1,694

Beachten Sie, dass doBy viel Zeit braucht, um das Paket zu laden.

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 nicht kompatibel mit Microbenchmark aufgrund der Attach / Detach.


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

microbenchmark plot

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


Angesichts dieser Ergebnisse und der Abwägung von Einfachheit und Geschwindigkeit müsste ich dem zustimmen arrange in dem plyr Paket. Es hat eine einfache Syntax und ist dennoch fast so schnell wie die Basis-R-Befehle mit ihren gewundenen Machenschaften. Typisch brillante Hadley Wickham Arbeit. Mein einziger Kritikpunkt ist, dass es die Standard-R-Nomenklatur durchbricht, in der das Sortieren von Objekten aufgerufen wird sort(object), aber ich verstehe, warum Hadley es aufgrund der in der oben stehenden Frage erörterten Fragen so gemacht hat.


382
2017-07-29 10:48



Dirks Antwort ist großartig. Es hebt auch einen Hauptunterschied in der für die Indizierung verwendeten Syntax hervor data.frames und 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)]

Der Unterschied zwischen den beiden Aufrufen ist gering, aber er kann wichtige Konsequenzen haben. Besonders wenn Sie in Ihrer Recherche Produktionscode schreiben und / oder sich mit Korrektheit beschäftigen, vermeiden Sie am besten unnötige Wiederholungen von Variablennamen. data.tablehilft dir dabei.

Hier ist ein Beispiel dafür, wie die Wiederholung von Variablennamen Sie in Schwierigkeiten bringen könnte:

Lassen Sie uns den Kontext von Dirks Antwort ändern und sagen, dass dies Teil eines größeren Projekts ist, in dem es viele Objektnamen gibt und sie lang und aussagekräftig sind; Anstatt von dd es heißt quarterlyreport. Es wird :

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

Ok, gut. Daran ist nichts falsch. Als nächstes bittet Ihr Chef Sie, den Bericht des letzten Quartals in den Bericht aufzunehmen. Sie gehen durch Ihren Code und fügen ein Objekt hinzu lastquarterlyreport an verschiedenen Orten und irgendwie (wie auf der Erde?) endet man damit:

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

Das ist nicht das, was du meintest, aber du hast es nicht bemerkt, weil du es schnell gemacht hast 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 Sie damit gemeint sind. Sie würden hoffen, dass derjenige, der Ihren Bericht liest, es entdeckt, aber vielleicht nicht. Wenn Sie viel mit Programmiersprachen arbeiten, dürfte Ihnen diese Situation vertraut vorkommen. Es war ein "Tippfehler", wirst du sagen. Ich werde den "Tippfehler" beheben, den du deinem Chef sagen wirst.

Im data.table Wir sind besorgt über kleine Details wie diese. Wir haben also etwas Einfaches getan, um zu vermeiden, dass Variablennamen zweimal eingegeben werden. Etwas sehr einfaches. i wird im Rahmen von ausgewertet dd schon automatisch. Du brauchst es nicht with() überhaupt.

Anstatt von

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 nur einen Hals retten. Wenn Sie die verschiedenen Antworten auf diese Frage abwägen, sollten Sie erwägen, die Wiederholungen von Variablennamen als eines Ihrer Entscheidungskriterien zu zählen. Einige Antworten haben einige Wiederholungen, andere keine.


128
2018-05-25 16:25



Es gibt viele ausgezeichnete Antworten hier, aber dplyr gibt die einzige Syntax, an die ich mich schnell und einfach erinnern kann (und jetzt sehr oft benutze):

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 OP-Problem:

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

105
2018-02-18 21:29



Das R-Paket data.table bietet beides schnell und speichereffizient Bestellung von Daten.tabellen mit einer einfachen Syntax (ein Teil, den Matt sehr schön hervorgehoben hat) in seiner Antwort). Es gab eine Menge Verbesserungen und auch eine neue Funktion setorder() seit damals. Von v1.9.5+, setorder() arbeitet auch mit data.frames.

Zunächst erstellen wir einen Datensatz, der groß genug ist, und benchmarken die verschiedenen Methoden, die in anderen Antworten erwähnt wurden, und listen dann die Funktionen auf Datentabelle.

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 Zeiten stammen vom Ausführen system.time(...) auf diesen Funktionen, die unten gezeigt werden. Die Zeitangaben sind unten aufgelistet (in der Reihenfolge von den langsamsten zu den 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.tableist es DT[order(...)] Syntax war ~ 10x schneller als die schnellsten anderen Methoden (dplyr), während die gleiche Menge an Speicher wie verbraucht wird dplyr.

  • data.tableist es setorder() war ~ 14x schneller als die schnellsten anderen Methoden (dplyr) während der Einnahme nur 0,4 GB zusätzlichen Speicher. dat ist jetzt in der Reihenfolge, die wir benötigen (wie es durch Bezugnahme aktualisiert wird).

data.table Funktionen:

Geschwindigkeit:

  • DatentabelleDie Reihenfolge ist extrem schnell, weil es implementiert Radix-Bestellung.

  • Die Syntax DT[order(...)] ist intern optimiert zu verwenden Datentabelleist auch schnell zu bestellen. Sie können weiterhin die bekannte Basis-R-Syntax verwenden, den Prozess jedoch beschleunigen (und weniger Arbeitsspeicher verwenden).

Erinnerung:

  • Meistens benötigen wir das Original nicht Datenrahmen oder Datentabelle nach der Neuordnung. Das heißt, wir ordnen das Ergebnis normalerweise demselben Objekt zu, zum Beispiel:

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

    Das Problem besteht darin, dass dies mindestens zweimal (2x) den Speicher des ursprünglichen Objekts erfordert. Sein speichereffizient, Datentabelle bietet daher auch eine Funktion an setorder().

    setorder() Nachbestellungen Daten.tabellen  by reference (an Ort und Stelle), ohne weitere Kopien anzufertigen. Es verwendet nur zusätzlichen Speicher, der der Größe einer Spalte entspricht.

Andere Eigenschaften:

  1. Es unterstützt integer, logical, numeric, character und selbst bit64::integer64 Arten.

    Beachten Sie, dass factor, Date, POSIXct usw. .. Klassen sind alle integer/numeric Typen darunter mit zusätzlichen Attributen und werden daher ebenfalls unterstützt.

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

    Jedoch in Datentabellekönnen wir einfach tun, zum Beispiel dat[order(-x)] oder setorder(dat, -x).


69
2018-03-29 15:52



Mit Diese (sehr hilfreiche) Funktion von Kevin WrightIn den Tipps im R-Wiki wird dies leicht erreicht.

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

57
2017-08-18 21:37



oder Sie können das Paket doBy verwenden

library(doBy)
dd <- orderBy(~-z+b, data=dd)

33
2018-01-19 20:44



Angenommen, Sie haben eine data.frame  A und Sie möchten es mit der Spalte namens sortieren x absteigende Reihenfolge. Ruf die Sortierten an data.frame  newdata

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

Wenn Sie eine aufsteigende Reihenfolge wünschen, ersetzen Sie sie "-" mit nichts. Sie können etwas wie haben

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

woher x und z sind einige Spalten in data.frame  A. Das bedeutet Sortieren data.frame  A durch x absteigend, y aufsteigend und z absteigend.


31
2018-01-25 13:10



Alternativ mit dem Paket Deducer

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

24
2017-08-20 19:43



Wenn SQL für Sie selbstverständlich ist, behandelt sqldf ORDER BY wie von Codd beabsichtigt.


24
2018-03-08 23:30