Cómo ordenar una trama de datos por múltiples columna (s)
Pregunta
Quiero ordenar una hoja.de.datos por varias columnas. Por ejemplo, con el data.frame debajo quisiera ordenar por z
columna (descendente) luego por b
columna (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
Solución
Puede utilizar el order()
función directamente sin recurrir a herramientas adicionales - ver esta respuesta más simple que utiliza un truco de la derecha de la parte superior del código 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
editar algunos 2+ años después: que era sólo preguntó cómo hacer esto por el índice de la columna. La respuesta es simplemente pasar la columna de clasificación deseado (s) a la función 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>
en lugar de utilizar el nombre de la columna (y with()
para el acceso directo más fácil / más).
Otros consejos
Sus opciones
-
order
debase
-
arrange
dedplyr
-
setorder
ysetorderv
dedata.table
-
arrange
deplyr
-
sort
detaRifx
-
orderBy
dedoBy
-
sortData
deDeducer
La mayoría de las veces se debe utilizar las soluciones dplyr
o data.table
, a menos que no tienen dependencias es importante, en cuyo caso se utilizará base::order
.
Recientemente he añadido a un paquete sort.data.frame CRAN, compatibilizando que la clase como se discute aquí: mejor manera de crear genérica consistencia / método para ordenar .data.frame?
Por lo tanto, dada la hoja.de.datos dd, puede clasificar de la siguiente manera:
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 )
Si usted es uno de los autores originales de esta función, por favor ponerse en contacto conmigo. Discusión en cuanto a domaininess pública está aquí: http://chat.stackoverflow.com/transcript/message/1094290#1094290
También puede utilizar la función de arrange()
plyr
como Hadley señaló en el hilo anterior:
library(plyr)
arrange(dd,desc(z),b)
Los puntos de referencia: Tenga en cuenta que cada paquete cargado en una nueva sesión R ya que había una gran cantidad de conflictos. En particular carga el paquete DOBY causa sort
volver "El siguiente objeto (s) se enmascaran de 'x (posición 17)': b, x, y, z", y cargar el paquete Deducer sobrescribe sort.data.frame
de Kevin Wright o la taRifx paquete.
#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
)
Los tiempos medios:
dd[with(dd, order(-z, b)), ]
778
dd[order(-dd$z, dd$b),]
788
library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)
La mediana de tiempo: 1567
library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)
La mediana de tiempo: 862
library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)
La mediana de tiempo: 1694
Tenga en cuenta que Doby toma una buena cantidad de tiempo para cargar el paquete.
library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)
No se pudo realizar la carga Deducer. Necesidades consola de JGR.
esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}
microbenchmark(esort(dd, -z, b),times=1000)
No aparece para ser compatible con microbenchmark debido a la / detach adjuntar.
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))
(líneas se extienden desde cuartil inferior a cuartil superior, punto es la mediana)
Teniendo en cuenta estos resultados y un peso de simplicidad frente a la velocidad, que tendría que dar el visto bueno a arrange
en el paquete plyr
. Tiene una sintaxis simple y sin embargo es casi tan rápida como la base R comandos con sus maquinaciones complicadas. Típicamente brillante trabajo Hadley Wickham. Mi única queja con él es que rompe la nomenclatura estándar R, donde consiguen clasificar objetos llamados por sort(object)
, pero entiendo por qué Hadley lo hizo de esa manera debido a cuestiones tratadas en la pregunta vinculado anteriormente.
La respuesta de Dirk es grande. También destaca una diferencia clave en la sintaxis utilizada para data.frame
s indexación y 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 diferencia entre las dos llamadas es pequeño, pero puede tener consecuencias importantes. Especialmente si se escribe el código de producción y / o no está conforme con la corrección en su investigación, lo mejor es evitar la repetición innecesaria de los nombres de las variables. data.table
ayuda a hacer esto.
Este es un ejemplo de cómo la repetición de nombres de variables que podría meterse en problemas:
Vamos a cambiar el contexto de la respuesta de Dirk, y dicen que esto es parte de un proyecto más grande, donde hay una gran cantidad de nombres de objetos y son de larga y significativa; en lugar de dd
se llama quarterlyreport
. Se convierte en:
quarterlyreport[with(quarterlyreport,order(-z,b)),]
Está bien, está bien. Nada de malo con eso. Junto a su jefe le pide que incluya el informe del último trimestre en el informe. Vas a través de su código, la adición de un lastquarterlyreport
objeto en diversos lugares y de alguna manera se termina con esto (cómo en la tierra?):
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
Eso no es lo que quería decir pero no lugar, porque lo hizo rápido y está acurrucado en una página de código similar. El código no se caiga (sin previo aviso y sin error) porque R piensa que es lo que quería decir. Usted esperaría cualquiera que lea su informe de manchas, pero quizás no lo hacen. Si se trabaja con lenguajes de programación mucho, entonces esta situación puede ser todo lo que conoce. Fue un "error tipográfico" que va a decir. Voy a arreglar el "error tipográfico" que va a decir a su jefe.
data.table
nos preocupa pequeños detalles como este. Por lo que hemos hecho algo sencillo para evitar tener que escribir los nombres de variables dos veces. Algo muy simple. i
se evalúa en el marco de dd
ya, de forma automática. No es necesario with()
en absoluto.
En lugar de
dd[with(dd, order(-z, b)), ]
es sólo
dd[order(-z, b)]
Y en lugar de
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
es sólo
quarterlyreport[order(-z,b)]
Es una diferencia muy pequeña, pero que sólo podría salvar su cuello un día. Al sopesar las diferentes respuestas a esta pregunta, considere contar las repeticiones de nombres de variables como uno de los criterios para decidir. Algunas respuestas tienen un buen número de repeticiones, otros no tienen ninguno.
Hay una gran cantidad de respuestas excelentes aquí, pero dplyr da la única sintaxis que tengo memoria rápida y fácilmente (y por lo ahora utilizan muy a menudo):
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)
Para el problema de la 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
El data.table
R paquete proporciona tanto rápido y eficiente de la memoria orden de los data.tables con una sintaxis sencilla (una parte del cual Matt tiene puesto de relieve bastante bien en su respuesta ). Ha habido un buen montón de mejoras y también una nueva función setorder()
desde entonces. De v1.9.5+
, setorder()
también trabaja con data.frames .
En primer lugar, vamos a crear un conjunto de datos de referencia lo suficientemente grande y los diferentes métodos mencionados de otras respuestas y luego enumerar las características de data.table .
Datos:
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)
Puntos de referencia:
Los horarios son reportados se ejecute system.time(...)
sobre estas funciones se muestran a continuación. Los tiempos se tabulan a continuación (en el orden de más lento a más rápido).
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
# ------------------------------------------------------------
-
sintaxis
data.table
deDT[order(...)]
era ~ 10x más rápido que el más rápido de otros métodos (dplyr
), mientras que consume la misma cantidad de memoria comodplyr
. -
data.table
desetorder()
era ~ 14x más rápido que el más rápido de otros métodos (dplyr
), teniendo acaba de 0.4GB de memoria adicional .dat
se encuentra ahora en el orden que necesitamos (ya que se actualiza por referencia).
características data.table:
Velocidad:
-
data.table 's pedido es extremadamente rápido ya que implementa radix ordenar .
-
La sintaxis
DT[order(...)]
está optimizado internamente para utilizar data.table 's pedidos de forma rápida también. Puede seguir usando la sintaxis de base R familiar pero acelerar el proceso (y usar menos memoria).
Memoria:
-
La mayoría de las veces, que no requieren el original hoja.de.datos o data.table después de reordenamiento. Es decir, se suelen asignar el resultado de nuevo al mismo objeto, por ejemplo:
DF <- DF[order(...)]
El problema es que esto requiere al menos dos veces (2x) la memoria del objeto original. Para ser eficiente de la memoria , data.table , por tanto, también proporciona una función de
setorder()
.setorder()> data.tables
by reference
( in situ ), sin hacer copias adicionales. Sólo se utiliza la memoria adicional igual al tamaño de una columna.
Otras características:
-
Es compatible
integer
,logical
,numeric
,character
y tipos inclusobit64::integer64
.Tenga en cuenta que
factor
,Date
, etc ..POSIXct
clases son todos los tiposinteger
/numeric
debajo con atributos adicionales y por lo tanto se apoyan también. -
En la base de I, no podemos usar
-
en un vector de caracteres para ordenar por esa columna en orden decreciente. En lugar de ello tenemos que utilizar-xtfrm(.)
.Sin embargo, en data.table , que sólo puede hacer, por ejemplo,
dat[order(-x)]
osetorder(dat, -x)
.
Con esta función (muy útil) por Kevin Wright , publicado en la sección de consejos de la R wiki, esto se logra fácilmente.
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
o puede utilizar el paquete DOBY
library(doBy)
dd <- orderBy(~-z+b, data=dd)
Suponga que tiene un data.frame
A
y desea ordenar usando columna llamada orden descendente x
. Llame a la data.frame
newdata
ordenados
newdata <- A[order(-A$x),]
Si usted quiere orden vuelva a colocar "-"
sin nada ascendente. Puede tener algo como
newdata <- A[order(-A$x, A$y, -A$z),]
donde x
y z
son algunas columnas en data.frame
A
. Esto significa una especie data.frame
A
por x
descendente, ascendente y descendente y
z
.
Alternativamente, utilizando el paquete de Deducer
library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))
si SQL es algo natural para ti, sqldf maneja ORDER BY como Codd pretende.
La respuesta de Dirk es bueno, pero si usted necesita el tipo de persistir querrá aplicar el tipo de nuevo en el nombre de esa trama de datos. Utilizando el código de ejemplo:
dd <- dd[with(dd, order(-z, b)), ]
he aprendido acerca de order
con el siguiente ejemplo que luego me confundió durante mucho tiempo:
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
La única razón este ejemplo funciona es porque order
es la clasificación por el vector Age
, no por el Age
columna llamada en el data frame data
.
Para ver este crear una trama de datos idénticos usando read.table
con ligeramente diferentes nombres de columna y sin hacer uso de cualquiera de los vectores anteriores:
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 estructura de la línea de arriba para order
ya no funciona porque no hay vector llamado age
:
databyage = my.data[order(age),]
La siguiente línea funciona porque las clases order
en la columna en age
my.data
.
databyage = my.data[order(my.data$age),]
pensé que valía la pena publicar teniendo en cuenta lo que estaba confundido por este ejemplo durante tanto tiempo. Si este mensaje no se considera apropiado para el hilo que puedo quitarlo.
EDIT: 13 de mayo de 2014
A continuación se muestra una forma generalizada de clasificación de una trama de datos de cada columna, sin especificar nombres de columna. El código siguiente muestra cómo ordenar de izquierda a derecha o de derecha a izquierda. Esto funciona si cada columna es numérico. No he probado con una columna de caracteres añadió.
He encontrado el código do.call
uno o dos meses atrás en un antiguo puesto en un sitio diferente, pero sólo después de la búsqueda extensa y difícil. No estoy seguro de que podría trasladar ese puesto ahora. El presente hilo es el primer golpe para ordenar un data.frame
en R
. Por lo tanto, pensé que mi versión ampliada de ese código do.call
original podría ser útil.
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
En respuesta a un comentario añadido en el PO de cómo ordenar mediante programación:
Uso dplyr
y data.table
library(dplyr)
library(data.table)
dplyr
Sólo tiene que utilizar arrange_
, que es la versión de evaluación estándar para 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)
más información aquí: https://cran.r-project.org/web /packages/dplyr/vignettes/nse.html
Es mejor utilizar la fórmula, ya que también captura el medio ambiente para evaluar una expresión en
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
La organizar () en dplyer es mi opción favorita. Utilice el operador de la tubería y van desde menos importante aspecto más importante
dd1 <- dd %>%
arrange(z) %>%
arrange(desc(x))
En aras de la exhaustividad: también puede utilizar la función sortByCol()
del paquete 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
Comparación de rendimiento:
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
Al igual que los clasificadores de cartas mecánicas de hace mucho tiempo, primero ordenar por la tecla menos significativo, entonces el siguiente más significativo, etc. requiere ninguna biblioteca, funciona con cualquier número de teclas y cualquier combinación de teclas ascendente y descendente.
dd <- dd[order(dd$b, decreasing = FALSE),]
Ahora estamos listos para hacer la clave más importante. La especie es estable, y cualquier lazo en la clave más importantes ya han sido resueltos.
dd <- dd[order(dd$z, decreasing = TRUE),]
Esto puede no ser el más rápido, pero sin duda es simple y confiable
Otra alternativa, utilizando el paquete 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
He tenido problemas con las soluciones anteriores cuando quería para automatizar mi proceso de pedido de n columnas, cuyos nombres de columna podría ser diferente cada vez. He encontrado una función muy útil del paquete psych
hacer esto de una manera directa:
dfOrder(myDf, columnIndices)
donde columnIndices
son índices de una o más columnas, en el orden en que desea ordenarlos. Más información aquí:
Sólo por el bien de la integridad, ya que no se ha hablado mucho acerca de la clasificación por números de columna ... Sin duda puede argumentar que a menudo no es deseable (ya que el orden de las columnas podría cambiar, allanando el camino a errores ), pero en algunas situaciones específicas (cuando por ejemplo se necesita un trabajo rápido hecho y no existe tal riesgo de columnas cambiantes órdenes), podría ser lo más sensato hacer, sobre todo cuando se trata de un gran número de columnas.
En ese caso, do.call()
viene al rescate:
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
## (...)