Jak posortować ramkę danych według wielu kolumn


1316

Chcę posortować ramkę danych według wielu kolumn. Na przykład z data.frame poniżej chciałbym sortować według kolumny z(malejąco), a następnie według kolumny b(rosnąco):

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

Odpowiedzi:


1624

Możesz użyć tej order()funkcji bezpośrednio, bez uciekania się do dodatkowych narzędzi - zapoznaj się z prostszą odpowiedzią, która wykorzystuje sztuczkę bezpośrednio z góry example(order)kodu:

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

Edytuj ponad 2 lata później: właśnie zapytano, jak to zrobić według indeksu kolumn. Odpowiedzią jest po prostu przekazanie żądanych kolumn sortujących do order()funkcji:

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> 

zamiast używać nazwy kolumny (i with()dla łatwiejszego / bardziej bezpośredniego dostępu).


@Dirk Eddelbuettel czy istnieje podobnie prosta metoda dla matryc?
Jota

14
Powinien działać w ten sam sposób, ale nie możesz go użyć with. Spróbuj M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))utworzyć macierz M, a następnie użyj, M[order(M[,"a"],-M[,"b"]),]aby zamówić ją w dwóch kolumnach.
Dirk Eddelbuettel

4
Dość łatwe:, dd[ order(-dd[,4], dd[,1]), ]ale nie można go używać withdo podzbiorów opartych na nazwach.
Dirk Eddelbuettel

18
Wystąpił błąd „nieprawidłowy argument dla operatora jednoargumentowego” podczas uruchamiania drugiego przykładu.
Nailgun

21
Błąd „nieprawidłowy argument dla operatora jednoargumentowego” występuje, gdy używasz minus z kolumną znaków. Rozwiąż to xtfrm, na przykład zawijając kolumnę dd[ order(-xtfrm(dd[,4]), dd[,1]), ].
Richie Cotton

476

Twoje wybory

  • order od base
  • arrange od dplyr
  • setorderi setordervoddata.table
  • arrange od plyr
  • sort od taRifx
  • orderBy od doBy
  • sortData od Deducer

Przez większość czasu powinieneś korzystać z rozwiązań dplyrlub data.table, chyba że ważne jest posiadanie niezależności, w takim przypadku użyj base::order.


Niedawno dodałem sort.data.frame do pakietu CRAN, dzięki czemu jest on zgodny z klasą, jak omówiono tutaj: Najlepszy sposób tworzenia spójności ogólnej / metody dla sort.data.frame?

Dlatego biorąc pod uwagę data.frame dd, możesz sortować w następujący sposób:

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 )

Jeśli jesteś jednym z oryginalnych autorów tej funkcji, skontaktuj się ze mną. Dyskusja na temat domeny publicznej jest tutaj: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Możesz także użyć arrange()funkcji z, plyrjak wskazał Hadley w powyższym wątku:

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

Benchmarki: Zauważ, że załadowałem każdy pakiet w nowej sesji R, ponieważ było wiele konfliktów. W szczególności ładowanie pakietu doBy powoduje sortzwrócenie „Następujące obiekty są maskowane z„ x (pozycja 17) ”: b, x, y, z”, a ładowanie pakietu Deducer zastępuje sort.data.frameKevin Wright lub pakiet 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
)

Mediana razy:

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

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

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

Mediana czasu: 1567

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

Mediana czasu: 862

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

Mediana czasu: 1,694

Pamiętaj, że doBy zajmuje dużo czasu, aby załadować pakiet.

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

Nie można zmusić Deducer do obciążenia. Potrzebuje konsoli JGR.

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

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

Wygląda na to, że nie jest kompatybilny z mikrodrukiem ze względu na dołączanie / odłączanie.


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

wykres mikrodruku

(linie rozciągają się od dolnego kwartylu do górnego kwartylu, kropka jest medianą)


Biorąc pod uwagę te wyniki i ważenie prostoty w porównaniu z szybkością, musiałbym skinąć głową arrangew plyrpakiecie . Ma prostą składnię, a mimo to jest prawie tak szybki, jak komendy podstawowego R z ich skomplikowanymi machinacjami. Zazwyczaj genialna praca Hadleya Wickhama. Jedyną moją wadą jest to, że łamie standardową nomenklaturę R, w której wywoływane są obiekty sortujące sort(object), ale rozumiem, dlaczego Hadley zrobił to w ten sposób z powodu problemów omówionych w pytaniu powiązanym powyżej.


3
Powyższa funkcja mikrodruku ggplot2 jest teraz dostępna jako taRifx::autoplot.microbenchmark.
Ari B. Friedman

@ AriB.Friedman Jakie są interwały osi y / jaka jest skala?
naught101

@ naught101 Oś y zaczyna się od 0. Skala powinna wynosić mikrosekundy.
Ari B. Friedman

2
@AME sprawdź, jak bjest posortowany w próbce. Domyślnie sortuj według rosnącego, więc po prostu go nie zawijaj desc. Rosnąco zarówno: arrange(dd,z,b). Malejąco zarówno: arrange(dd,desc(z),desc(b)).
Ari B. Friedman,

2
Zgodnie z ?arrange: „# UWAGA: funkcje plyr NIE zachowują nazw row.names”. To sprawia, że ​​doskonała arrange()funkcja nie jest optymalna, jeśli chce się ją zachować row.names.
landroni

148

Odpowiedź Dirka jest świetna. Podkreśla również kluczową różnicę w składni do indeksowania data.frames i 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)]

Różnica między tymi dwoma połączeniami jest niewielka, ale może mieć poważne konsekwencje. Zwłaszcza jeśli piszesz kod produkcyjny i / lub martwisz się o poprawność swoich badań, najlepiej unikać niepotrzebnego powtarzania nazw zmiennych. data.table pomaga ci to zrobić.

Oto przykład, w jaki sposób powtarzanie nazw zmiennych może powodować kłopoty:

Zmieńmy kontekst z odpowiedzi Dirka i powiedzmy, że jest to część większego projektu, w którym istnieje wiele nazw obiektów, które są długie i znaczące; zamiast ddnazywa się quarterlyreport. Staje się :

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

Ok dobrze. Nic w tym złego. Następnie szef prosi o uwzględnienie raportu z ostatniego kwartału. Przechodzisz przez swój kod, dodając obiekt lastquarterlyreportw różnych miejscach i jakoś (jak to możliwe, do licha):

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

Nie to miałeś na myśli, ale nie zauważyłeś tego, ponieważ zrobiłeś to szybko i jest on umieszczony na stronie podobnego kodu. Kod się nie przewraca (bez ostrzeżenia i bez błędu), ponieważ R myśli, że to właśnie miałeś na myśli. Miałbyś nadzieję, że ktokolwiek przeczyta twój raport, zauważy to, ale może nie. Jeśli dużo pracujesz z językami programowania, ta sytuacja może być znana. To powiesz „literówka”. Naprawię literówkę, którą powiesz swojemu szefowi.

W data.tablejesteśmy zaniepokojeni drobnych szczegółów, takich jak ten. Zrobiliśmy więc coś prostego, aby uniknąć dwukrotnego wpisywania nazw zmiennych. Coś bardzo prostego. ijest oceniany w ramach ddjuż, automatycznie. W ogóle nie potrzebujesz with().

Zamiast

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

to poprostu

dd[order(-z, b)]

I zamiast

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

to poprostu

quarterlyreport[order(-z,b)]

To bardzo mała różnica, ale pewnego dnia może po prostu zaoszczędzić szyję. Analizując różne odpowiedzi na to pytanie, rozważ liczenie powtórzeń nazw zmiennych jako jedno z kryteriów przy podejmowaniu decyzji. Niektóre odpowiedzi mają kilka powtórzeń, inne nie.


9
+1 To jest świetna uwaga i dochodzi do szczegółów składni R, która często mnie irytuje. Czasami używam subset()tylko, aby uniknąć konieczności wielokrotnego odwoływania się do tego samego obiektu w ramach jednego połączenia.
Josh O'Brien

2
@ naught101 Czy data.table FAQ 1.9 na to odpowiada?
Matt Dowle,

5
Wydaje mi się, że można tu również dodać nową setorderfunkcję, ponieważ w tym wątku wysyłamy wszystkie orderduplikaty typów.
David Arenburg,

125

Istnieje tutaj wiele doskonałych odpowiedzi, ale dplyr podaje jedyną składnię, którą mogę szybko i łatwo zapamiętać (a więc teraz bardzo często używam):

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)

W przypadku problemu 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

2
Akceptowana odpowiedź nie działa, gdy moje kolumny są lub mają współczynnik typu (lub coś w tym rodzaju) i chcę sortować w porządku malejącym dla tej kolumny czynników, a następnie kolumny liczb całkowitych w sposób rosnący. Ale to działa dobrze! Dziękuję Ci!
Saheel Godhane

10
Dlaczego „tylko”? Uważam, że data.table jest dd[order(-z, b)]dość łatwy w użyciu i pamiętam.
Matt Dowle,

2
Zgadzając się, nie ma wiele między tymi dwiema metodami i data.tablejest to ogromny wkład Rw wiele innych sposobów. Przypuszczam, że dla mnie może być tak, że posiadanie jednego mniejszego zestawu nawiasów (lub jednego mniejszego rodzaju nawiasów) w tym przypadku zmniejsza obciążenie poznawcze o ledwo zauważalną ilość.
Ben

7
Dla mnie sprowadza się to do tego, że arrange()jest całkowicie deklaratywne, dd[order(-z, b)]nie jest.
Mullefa

83

Pakiet R data.tablezapewnia szybkie i efektywne pod względem pamięci uporządkowanie tabel data.tab z prostą składnią (której część Matt dość ładnie podkreślił w swojej odpowiedzi ). Od tego czasu wprowadzono wiele ulepszeń, a także nowa funkcja setorder(). Od v1.9.5+, setorder()współpracuje również z data.frames .

Najpierw utworzymy zestaw danych wystarczająco duży i porównamy różne metody wymienione w innych odpowiedziach, a następnie wymienimy funkcje data.table .

Dane:

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)

Benchmarki:

Zgłoszone czasy pochodzą z działania system.time(...)tych funkcji pokazanych poniżej. Czasy są zestawione poniżej (w kolejności od najwolniejszej do najszybszej).

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.tablejest DT[order(...)]składnia było ~ 10x szybciej niż największe innych metod ( dplyr), zaś zużywa tyle samo jak pamięć dplyr.

  • data.table„s setorder()było ~ 14x szybciej niż najszybszy z innymi metodami ( dplyr), a jednocześnie tylko 0.4GB dodatkowej pamięci . datjest teraz w wymaganej przez nas kolejności (ponieważ jest aktualizowany przez odniesienie).

Funkcje data.table:

Prędkość:

  • Kolejność data.table jest niezwykle szybka, ponieważ implementuje porządkowanie w Radix .

  • Składnia DT[order(...)]jest zoptymalizowana wewnętrznie w celu wykorzystania również szybkiego zamawiania data.table . Możesz nadal używać znanej podstawowej składni R, ale przyspieszyć proces (i zużywać mniej pamięci).

Pamięć:

  • W większości przypadków nie wymagamy oryginalnego data.frame lub data.table po zmianie kolejności. Oznacza to, że zwykle przypisujemy wynik z powrotem do tego samego obiektu, na przykład:

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

    Problem polega na tym, że wymaga to przynajmniej dwukrotnie (2x) pamięci oryginalnego obiektu. Dlatego, aby zapewnić wydajność pamięci , data.table udostępnia również funkcję setorder().

    setorder()zmienia kolejność danych. tabele by reference ( na miejscu ), bez wykonywania dodatkowych kopii. Wykorzystuje tylko dodatkową pamięć równą wielkości jednej kolumny.

Inne funkcje:

  1. Obsługuje integer, logical, numeric, charactera nawet bit64::integer64rodzaje.

    Należy zauważyć, że factor, Date, POSIXctetc .. zajęcia są wszystkie integer/ numericrodzaje spodu z dodatkowych atrybutów, a zatem są obsługiwane.

  2. W bazie R nie możemy używać -wektora znaków do sortowania według tej kolumny w malejącej kolejności. Zamiast tego musimy użyć -xtfrm(.).

    Jednak w data.table możemy po prostu zrobić, na przykład, dat[order(-x)]lub setorder(dat, -x).


Dzięki za bardzo pouczającą odpowiedź na temat data.table. Chociaż nie rozumiem, co to jest „pamięć szczytowa” i jak ją obliczono. Czy mógłbyś wyjaśnić? Dziękuję Ci !
Julien Navarre

Użyłem Instrumentów -> przydziałów i zgłosiłem rozmiar maszyny wirtualnej „Wszystkie sterty i przydziały”.
Arun

2
@Uruchom link Instruments w twoim komentarzu jest martwy. Chcesz opublikować aktualizację?
MichaelChirico

@MichaelChirico Oto link do informacji o instrumentach wyprodukowanych przez Apple: developer.apple.com/library/content/documentation/…
n1k31t4



39

Załóżmy, że masz data.frame Ai chcesz je posortować za pomocą kolumny zwanej xkolejnością malejącą. Zadzwoń do posortowanegodata.frame newdata

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

Jeśli chcesz porządek rosnący, zastąp "-"go niczym. Możesz mieć coś takiego

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

gdzie xi zsą niektóre kolumny w data.frame A. Oznacza to sortowanie data.frame Awedług xmalejącego, yrosnącego i zmalejącego.


32

jeśli SQL przychodzi ci naturalnie, sqldfpakiet obsługuje się ORDER BYtak, jak zamierzał Codd.


7
MJM, dzięki za wskazanie tego pakietu. Jest niesamowicie elastyczny, a ponieważ połowa mojej pracy jest już wykonywana przez pobieranie baz danych SQL, jest to łatwiejsze niż uczenie się dużo mniej niż intuicyjnej składni R.
Brandon Bertelsen

31

Alternatywnie, używając pakietu Deducer

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

19

W odpowiedzi na komentarz dodany w PO dotyczący programowego sortowania:

Korzystanie dplyridata.table

library(dplyr)
library(data.table)

dplyr

Wystarczy użyć arrange_, która jest wersją Standardową wersję testową 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)

więcej informacji tutaj: https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

Lepiej jest użyć formuły, ponieważ przechwytuje ona także środowisko do oceny wyrażenia

Tabela danych

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

18

Dowiedziałem się o tym orderz poniższego przykładu, który przez długi czas mylił mnie:

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

Jedynym powodem, dla którego ten przykład działa, jest ordersortowanie według vector Age, a nie według kolumny o nazwie Agew data frame data.

Aby to zobaczyć, utwórz identyczną ramkę danych, używając read.tablenieco innych nazw kolumn i bez użycia żadnego z powyższych wektorów:

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)

Powyższa struktura linii orderjuż nie działa, ponieważ nie ma wektora o nazwie age:

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

Poniższy wiersz działa, ponieważ ordersortuje według kolumny agew my.data.

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

Pomyślałem, że warto to opublikować, biorąc pod uwagę, jak długo byłem zdezorientowany tym przykładem. Jeśli ten post nie zostanie uznany za odpowiedni dla tego wątku, mogę go usunąć.

EDYCJA: 13 maja 2014 r

Poniżej znajduje się ogólny sposób sortowania ramki danych według każdej kolumny bez podawania nazw kolumn. Poniższy kod pokazuje, jak sortować od lewej do prawej lub od prawej do lewej. Działa to, jeśli każda kolumna jest liczbą. Nie próbowałem z dodaną kolumną znaków.

Znalazłem do.callkod miesiąc lub dwa lata temu w starym poście na innej stronie, ale dopiero po obszernym i trudnym wyszukiwaniu. Nie jestem pewien, czy mógłbym teraz przenieść ten post. Obecny wątek jest pierwszym hitem na zamówienie data.framew R. Pomyślałem więc, że moja rozszerzona wersja tego oryginalnego do.callkodu może się przydać.

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

4
Ta składnia działa, jeśli przechowujesz dane w tabeli data.tame zamiast data.frame: require(data.table); my.dt <- data.table(my.data); my.dt[order(age)]Działa to, ponieważ nazwy kolumn są dostępne w nawiasach [].
Frank

Nie sądzę, aby głosowanie tutaj było konieczne, ale nie sądzę też, by to dodawało wiele do pytania , szczególnie biorąc pod uwagę istniejący zestaw odpowiedzi, z których niektóre już wychwytują wymaganie z data.framealbo do użycia withalbo $.
A5C1D2H2I1M1N2O1R2T1

1
Upvote na do.callto sprawia, że ​​krótka praca nad sortowaniem wielokolumnowej ramki danych. Po prostu do.call(sort, mydf.obj)będzie piękny rodzaj kaskady.
AdamO,

17

Odpowiedź Dirka jest dobra, ale jeśli chcesz zachować sortowanie, powinieneś zastosować sortowanie z powrotem do nazwy tej ramki danych. Za pomocą przykładowego kodu:

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

13

Arrange () w dplyerze to moja ulubiona opcja. Użyj operatora potoku i przejdź od najmniej ważnego do najważniejszego aspektu

dd1 <- dd %>%
    arrange(z) %>%
    arrange(desc(x))

7

Ze względu na kompletność, ponieważ niewiele powiedziano o sortowaniu według numerów kolumn ... Z pewnością można argumentować, że często nie jest pożądane (ponieważ kolejność kolumn może się zmienić, torując drogę do błędów), ale w niektórych szczególnych sytuacjach (gdy na przykład potrzebujesz szybkiej pracy i nie ma takiego ryzyka zmiany kolejności kolumn), może to być najbardziej rozsądne, szczególnie w przypadku dużej liczby kolumn.

W takim przypadku do.call()przychodzi na ratunek:

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

6

Dla kompletności: możesz również użyć sortByCol()funkcji z BBmiscpakietu:

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

Porównanie wydajności:

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

4
dziwne jest dodanie porównania wydajności, gdy twoja metoda jest najwolniejsza ... w każdym razie wątpliwa jest wartość testu porównawczego w 4-rzędowymdata.frame
MichaelChirico

5

Podobnie jak dawno temu mechaniczne sortowniki kart, najpierw sortuj według najmniej znaczącego klucza, a następnie następnego najbardziej znaczącego itd. Nie jest wymagana biblioteka, działa z dowolną liczbą kluczy i dowolną kombinacją kluczy rosnących i malejących.

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

Teraz jesteśmy gotowi zrobić najbardziej znaczący klucz. Sortowanie jest stabilne, a wszelkie powiązania w najbardziej znaczącym kluczu zostały już rozwiązane.

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

To może nie być najszybszy, ale z pewnością jest prosty i niezawodny


4

Inna alternatywa, przy użyciu rgrpakietu:

> 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

4

Walczyłem z powyższymi rozwiązaniami, gdy chciałem zautomatyzować proces zamawiania dla n kolumn, których nazwy kolumn mogą być za każdym razem inne. Znalazłem bardzo przydatną funkcję z psychpakietu, aby to zrobić w prosty sposób:

dfOrder(myDf, columnIndices)

gdzie columnIndicessą indeksy jednej lub więcej kolumn, w kolejności, w której chcesz je posortować. Więcej informacji tutaj:

Funkcja dfOrder z pakietu „psych”

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.