Come ordinare un dataframe per più colonne
Domanda
Voglio ordinare un data.frame per più colonne.Ad esempio, con data.frame di seguito vorrei ordinare per colonna z
(discendente) quindi per colonna b
(ascendente):
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
Soluzione
È possibile utilizzare il order()
funzione direttamente senza ricorrere a strumenti aggiuntivi - vedere questa risposta più semplice che utilizza un proprio trucco dalla parte superiore del codice example(order)
:
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
modificare alcuni 2+ anni dopo: è stato solo chiesto come fare questo in base all'indice di colonna. La risposta è passare semplicemente la colonna di ordinamento desiderato (s) alla funzione order()
:
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>
, piuttosto che utilizzare il nome della colonna (e with()
per / accesso diretto più facile).
Altri suggerimenti
Le scelte
-
order
dabase
-
arrange
dadplyr
-
setorder
esetorderv
dadata.table
-
arrange
daplyr
-
sort
dataRifx
-
orderBy
dadoBy
-
sortData
daDeducer
La maggior parte del tempo si consiglia di utilizzare le soluzioni dplyr
o data.table
, a meno che non avendo-dipendenze è importante, in questo caso l'uso base::order
.
Recentemente ho aggiunto sort.data.frame a un pacchetto CRAN, rendendo compatibile è di classe come discusso qui: modo migliore per creare generico / metodo di coerenza per l'ordinamento .data.frame?
Pertanto, data la dd data.frame, è possibile ordinare i seguenti:
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 )
Se siete uno degli autori originali di questa funzione, si prega di contattare me. Discussione da domaininess pubblico è qui: http://chat.stackoverflow.com/transcript/message/1094290#1094290
È anche possibile utilizzare la funzione arrange()
da plyr
come Hadley ha sottolineato nel thread di cui sopra:
library(plyr)
arrange(dd,desc(z),b)
Benchmark: Si noti che ho caricato ogni pacchetto in una nuova sessione di R dato che c'erano un sacco di conflitti. In particolare il carico confezione DOBY provoca sort
to return "Il seguente oggetto (s) sono mascherati da 'x (posizione 17)': b, x, y, z", e caricando il pacchetto Deducer sovrascrive sort.data.frame
da Kevin Wright o taRifx pacchetto.
#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
)
mediani volte:
dd[with(dd, order(-z, b)), ]
778
dd[order(-dd$z, dd$b),]
788
library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)
Il tempo mediano: 1567
library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)
Il tempo mediano: 862
library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)
Il tempo mediano: 1694
Si noti che DOBY prende un bel po 'di tempo per caricare il pacchetto.
library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)
Impossibile fare carico Deducer. Ha bisogno 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)
Non sembra essere compatibile con microbenchmark a causa della / scollegamento allegare.
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))
(linee si estendono da quartile inferiore a quartile superiore, dot è la mediana)
Alla luce di questi risultati e del peso di semplicità vs. velocità, avrei dovuto dare il via libera a arrange
nel pacchetto plyr
. Ha una sintassi semplice e tuttavia è quasi altrettanto rapida come la base di R comandi con le loro macchinazioni contorti. In genere brillante lavoro Hadley Wickham. La mia unica lamentela con essa è che si rompe la nomenclatura R standard in cui gli oggetti di smistamento vengono chiamati da sort(object)
, ma capisco perché Hadley ha fatto in questo modo a causa di questioni discusse nella questione linkato sopra.
La risposta di Dirk è fantastica.Evidenzia inoltre una differenza fondamentale nella sintassi utilizzata per l'indicizzazione data.frame
sabbia data.table
S:
## 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 differenza tra i due appelli è piccola, ma può avere conseguenze importanti.Soprattutto se scrivi codice di produzione e/o sei interessato alla correttezza della tua ricerca, è meglio evitare ripetizioni inutili di nomi di variabili. data.table
ti aiuta a fare questo.
Ecco un esempio di come la ripetizione dei nomi delle variabili potrebbe metterti nei guai:
Cambiamo il contesto rispetto alla risposta di Dirk e diciamo che fa parte di un progetto più ampio in cui ci sono molti nomi di oggetti e sono lunghi e significativi;invece di dd
è chiamato quarterlyreport
.Diventa :
quarterlyreport[with(quarterlyreport,order(-z,b)),]
Ok bene.Non c'è niente di sbagliato in questo.Successivamente il tuo capo ti chiede di includere il rapporto dell'ultimo trimestre nel rapporto.Esamini il tuo codice, aggiungendo un oggetto lastquarterlyreport
in vari posti e in qualche modo (come diavolo?) ti ritrovi con questo:
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
Non è quello che intendevi, ma non l'hai notato perché l'hai fatto velocemente ed è annidato in una pagina di codice simile.Il codice non cade (nessun avviso e nessun errore) perché R pensa che sia quello che intendevi.Spereresti che chiunque legga il tuo rapporto se ne accorga, ma forse non è così.Se lavori molto con i linguaggi di programmazione, questa situazione potrebbe esserti troppo familiare.È stato un "errore di battitura" direte voi.Correggerò l'"errore di battitura" che dirai al tuo capo.
In data.table
siamo preoccupati per piccoli dettagli come questo.Quindi abbiamo fatto qualcosa di semplice per evitare di digitare due volte i nomi delle variabili.Qualcosa di molto semplice. i
viene valutato nell'ambito di dd
già, automaticamente.Non ne hai bisogno with()
affatto.
Invece di
dd[with(dd, order(-z, b)), ]
è appena
dd[order(-z, b)]
E invece di
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
è appena
quarterlyreport[order(-z,b)]
È una differenza molto piccola, ma un giorno potrebbe salvarti il collo.Quando soppesi le diverse risposte a questa domanda, considera il conteggio delle ripetizioni dei nomi delle variabili come uno dei criteri per decidere.Alcune risposte hanno parecchie ripetizioni, altre non ne hanno nessuna.
Ci sono un sacco di risposte eccellenti qui, ma dplyr dà l'unica sintassi che posso in modo rapido e facile da ricordare (e così ora utilizzare molto spesso):
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)
Per il problema del 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
Il pacchetto data.table
R fornisce sia veloce e efficiente della memoria ordinamento dei data.tables con una sintassi semplice (una parte del quale Matt ha evidenziato abbastanza bene nella sua risposta ). C'è stato un bel po 'di miglioramenti e anche una nuova funzione setorder()
da allora. Da v1.9.5+
, setorder()
funziona anche con data.frames .
Per prima cosa, creeremo un insieme di dati abbastanza grande e punto di riferimento i diversi metodi citati da altre risposte e quindi elencare le caratteristiche di data.table .
Dati:
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)
Benchmark:
I tempi riportati sono l'esecuzione system.time(...)
su queste funzioni mostrate di seguito. I tempi sono tabulati di seguito (in ordine di lenta alla più veloce).
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
# ------------------------------------------------------------
-
sintassi
data.table
diDT[order(...)]
stato ~ 10x più veloce del più veloce di altri metodi (dplyr
), pur consumando la stessa quantità di memoria comedplyr
. -
data.table
disetorder()
stato ~ 14x più veloce del più veloce di altri metodi (dplyr
), durante l'assunzione di solo 0.4GB di memoria aggiuntiva .dat
è ora in ordine abbiamo bisogno (in quanto viene aggiornato per riferimento).
caratteristiche data.table:
Velocità:
-
data.table s 'ordinamento è estremamente veloce perché implementa radix ordinare .
-
Il
DT[order(...)]
sintassi è ottimizzato internamente utilizzare data.table s 'ordinamento veloce pure. È possibile continuare a utilizzare la sintassi di base R familiare ma accelerare il processo (e utilizzare meno memoria).
Memory:
-
La maggior parte delle volte, non richiedono l'originale data.frame o data.table dopo il riordino. Cioè, di solito assegnare il risultato di nuovo allo stesso oggetto, ad esempio:
DF <- DF[order(...)]
Il problema è che questo richiede almeno due volte (2x) la memoria dell'oggetto originale. Essere efficiente della memoria , data.table quindi fornisce anche una funzione
setorder()
.riordini
setorder()
data.tablesby reference
( in-place ), senza apportare ulteriori copie. Si utilizza solo memoria aggiuntiva pari alla dimensione di una colonna.
Altre caratteristiche:
-
Supporta
integer
,logical
,numeric
,character
e tipi anchebit64::integer64
.Si noti che
factor
,Date
,POSIXct
ecc .. classi sono tutti i tipiinteger
/numeric
sotto con attributi aggiuntivi e sono pertanto supportati. -
In base di R, non possiamo usare
-
su un vettore di carattere per ordinare per quella colonna in ordine decrescente. Invece dobbiamo usare-xtfrm(.)
.Tuttavia, in data.table , possiamo solo fare, per esempio, o
dat[order(-x)]
setorder(dat, -x)
.
Con questa funzione (molto utile) da Kevin Wright , pubblicato nella sezione consigli del wiki R, questo è facilmente ottenibile.
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
oppure è possibile utilizzare il pacchetto DOBY
library(doBy)
dd <- orderBy(~-z+b, data=dd)
Supponiamo di avere un data.frame
A
e si desidera ordinare utilizzando colonna chiamata x
ordine decrescente. Chiamare il data.frame
newdata
ordinato
newdata <- A[order(-A$x),]
Se vuoi per poi sostituirlo con niente "-"
ascendente. Si può avere qualcosa di simile
newdata <- A[order(-A$x, A$y, -A$z),]
dove x
e z
sono alcune colonne in data.frame
A
. Questo significa ordina data.frame
A
da x
discendente, y
ascendente e discendente z
.
In alternativa, utilizzando il pacchetto Deducer
library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))
se SQL viene naturalmente a voi, sqldf maniglie ORDER BY come Codd destinato.
La risposta di Dirk è buona, ma se è necessario il tipo di persistere ti consigliamo di applicare l'ordinamento di nuovo sul nome di quella data frame. Utilizzando il codice di esempio:
dd <- dd[with(dd, order(-z, b)), ]
Ho imparato a conoscere order
con il seguente esempio che poi mi ha confuso per molto tempo:
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
L'unica ragione per questo esempio funziona è perché order
è l'ordinamento per l'vector Age
, non dalla colonna denominata Age
nel data frame data
.
Per vedere questo crea un frame di dati identici utilizzando read.table
con leggermente diversi nomi di colonna e senza fare uso di qualsiasi dei vettori di cui sopra:
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 struttura di linea di cui sopra per order
non funziona più perché non c'è nessun vettore denominato age
:
databyage = my.data[order(age),]
La seguente riga funziona perché i tipi order
sulla age
colonna my.data
.
databyage = my.data[order(my.data$age),]
Ho pensato che questo valeva la pubblicazione dato come ero confuso da questo esempio per così tanto tempo. Se questo post non si ritiene opportuno per il thread che posso toglierlo.
EDIT: 13 maggio 2014
Di seguito è un modo generalizzato di ordinare un frame di dati da ogni colonna senza specificare nomi di colonna. Il codice seguente mostra come ordinare da sinistra a destra o da destra a sinistra. Questo funziona se ogni colonna è numerico. Non ho provato con una colonna di carattere aggiunto.
Ho trovato il codice do.call
un mese o due fa, in un vecchio post su un sito diverso, ma solo dopo ampia e difficile ricerca. Non sono sicuro che avrei potuto spostare quel post ora. La presente discussione è il primo colpo per ordinare un data.frame
in R
. Così, ho pensato che la mia versione ampliata di quel codice do.call
originale potrebbe essere 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
In risposta ad un commento aggiunto nel PO per come ordinare a livello di codice:
Utilizzando dplyr
e data.table
library(dplyr)
library(data.table)
dplyr
Basta usare arrange_
, che è la versione di valutazione standard per 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)
maggiori informazioni qui: https://cran.r-project.org/web /packages/dplyr/vignettes/nse.html
E 'meglio usare la formula in quanto coglie anche l'ambiente per valutare un'espressione in
data.table
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
Arrange () in dplyer è la mia opzione preferita. Utilizzare l'operatore di pipe e passare da almeno importante aspetto più importante
dd1 <- dd %>%
arrange(z) %>%
arrange(desc(x))
Per ragioni di completezza: è anche possibile utilizzare la funzione sortByCol()
dal pacchetto BBmisc
:
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
Confronto delle prestazioni:
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
Proprio come le selezionatrici di carte meccanici di un tempo, Ordina la chiave meno significativo, quindi il prossimo più significativo, ecc Nessun libreria richiesta, funziona con qualsiasi numero di chiavi e qualsiasi combinazione di salire e scendere le chiavi.
dd <- dd[order(dd$b, decreasing = FALSE),]
Ora siamo pronti a fare la chiave più significativo. L'ordinamento è stabile, e gli eventuali legami nella chiave più significativi sono già stati risolti.
dd <- dd[order(dd$z, decreasing = TRUE),]
Questo può non essere il più veloce, ma è certamente semplice e affidabile
Un'altra alternativa, utilizzando il pacchetto rgr
:
> 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
Stavo lottando con le soluzioni di cui sopra quando ho voluto automatizzare il mio processo di ordinazione per le colonne n, i cui nomi colonna potrebbe essere diverso ogni volta. Ho trovato una funzione super disponibile dal pacchetto psych
per fare questo in modo semplice:
dfOrder(myDf, columnIndices)
dove columnIndices
sono indici di una o più colonne, nell'ordine in cui si desidera ordinarli. Maggiori informazioni qui:
Solo per ragioni di completezza, dal momento che non molto è stato detto circa l'ordinamento per numeri di colonna ... Si può sicuramente affermare che spesso non è desiderabile (perché l'ordine delle colonne potrebbe cambiare, aprendo la strada ad errori ), ma in alcune situazioni specifiche (quando, per esempio, è necessario un lavoro rapido e non vi sia alcun rischio del genere di colonne mutevoli ordini), potrebbe essere la cosa più sensata da fare, soprattutto quando si tratta di un gran numero di colonne.
In questo caso, do.call()
viene in soccorso:
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
## (...)