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
¿Fue útil?

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 de base
  • arrange de dplyr
  • setorder y setorderv de data.table
  • arrange de plyr
  • sort de taRifx
  • orderBy de doBy
  • sortData de Deducer

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

 parcela microbenchmark

(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.frames indexación y 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)]

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 de DT[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 como dplyr.

  • data.table de setorder() 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:

  1. Es compatible integer, logical, numeric, character y tipos incluso bit64::integer64.

      

    Tenga en cuenta que factor, Date, etc .. POSIXct clases son todos los tipos integer / numeric debajo con atributos adicionales y por lo tanto se apoyan también.

  2. 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)] o setorder(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í:

función dfOrder de 'psicología' paquete

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
##    (...)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top