por - reorganizar columnas en r
¿Cómo ordenar un marco de datos por varias columnas? (18)
Tus opciones
-
order
desde labase
-
arrange
desdedplyr
-
setorder
ysetorderv
desdedata.table
-
arrange
deplyr
-
sort
detaRifx
-
orderBy
pordoBy
-
sortData
deDeducer
La mayoría de las veces debe usar las soluciones dplyr
o data.table
, a menos que sea importante no tener dependencias, en cuyo caso use base::order
.
Recientemente agregué sort.data.frame a un paquete CRAN, haciéndolo compatible con la clase como se explica aquí: ¿La mejor manera de crear una consistencia genérica / de método para sort.data.frame?
Por lo tanto, dado el data.frame dd, puede ordenar 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 eres uno de los autores originales de esta función, contáctame. La discusión sobre dominio público está aquí: http://chat.stackoverflow.com/transcript/message/1094290#1094290
También puede usar la función de arrange()
de plyr
como Hadley señaló en el hilo anterior:
library(plyr)
arrange(dd,desc(z),b)
Puntos de referencia: Tenga en cuenta que cargué cada paquete en una nueva sesión R ya que hubo muchos conflictos. En particular, la carga del paquete doBy hace que la sort
devuelva "Los siguientes objetos están enmascarados desde ''x (posición 17)'': b, x, y, z", y al cargar el paquete del sort.data.frame
sobrescribe sort.data.frame
de Kevin Wright o el paquete 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
)
Tiempos medianos:
dd[with(dd, order(-z, b)), ]
778
dd[order(-dd$z, dd$b),]
788
library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)
Tiempo medio: 1,567.
library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)
Tiempo medio: 862
library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)
Tiempo medio: 1,694
Tenga en cuenta que doBy tarda un poco de tiempo en cargar el paquete.
library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)
No se pudo hacer carga Deducer. Necesita la 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 parece ser compatible con microbenchmark debido a la conexión / desconexión.
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))
(las líneas se extienden desde el cuartil inferior al cuartil superior, el punto es la mediana)
Dados estos resultados y un peso simple frente a la velocidad, tendría que dar el visto bueno para arrange
el paquete plyr
. Tiene una sintaxis simple y, sin embargo, es casi tan veloz como los comandos de la base R con sus complicadas maquinaciones. Típicamente brillante trabajo de Hadley Wickham. Mi única queja es que rompe la nomenclatura R estándar donde los objetos se clasifican por sort(object)
, pero entiendo por qué Hadley lo hizo de esa manera debido a los problemas tratados en la pregunta vinculada anteriormente.
Quiero ordenar un data.frame por varias columnas. Por ejemplo, con el cuadro de datos a continuación, me gustaría ordenar por la columna z
(descendente) y luego por la columna 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
Al igual que los clasificadores mecánicos de tarjetas de hace mucho tiempo, primero se ordenan por la clave menos significativa, luego por la siguiente más significativa, etc. No se requiere una biblioteca, funciona con cualquier número de teclas y cualquier combinación de teclas ascendentes y descendentes.
dd <- dd[order(dd$b, decreasing = FALSE),]
Ahora estamos listos para hacer la clave más significativa. El orden es estable, y todos los vínculos en la clave más significativa ya se han resuelto.
dd <- dd[order(dd$z, decreasing = TRUE),]
Esto puede no ser el más rápido, pero es ciertamente simple y confiable
Alternativamente, utilizando el paquete Deducer.
library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))
Aprendí sobre el 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 por la que este ejemplo funciona es porque el order
se order
por el vector Age
, no por la columna denominada Age
en los data frame data
.
Para ver esto, cree un marco de datos idéntico utilizando read.table
con nombres de columnas ligeramente diferentes y sin hacer uso de ninguno 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 línea anterior para el order
ya no funciona porque no hay un vector llamado age
:
databyage = my.data[order(age),]
La siguiente línea funciona porque el order
clasifica en la age
la columna en my.data
.
databyage = my.data[order(my.data$age),]
Pensé que valía la pena publicar esto dado lo confundido que estaba con este ejemplo durante tanto tiempo. Si esta publicación no se considera apropiada para el hilo, puedo eliminarlo.
EDITAR: 13 de mayo de 2014
A continuación se muestra una forma generalizada de ordenar un marco de datos por 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érica. No he probado con una columna de caracteres añadida.
Encontré el código de do.call
uno o dos meses en una publicación anterior en un sitio diferente, pero solo después de una búsqueda extensa y difícil. No estoy seguro de poder reubicar ese puesto ahora. El hilo presente es el primer hit para ordenar un data.frame
en R
Entonces, pensé que mi versión expandida de ese código original de do.call
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
Aquí hay muchas respuestas excelentes, pero dplyr ofrece la única sintaxis que puedo recordar rápida y fácilmente (y que ahora utilizo 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 del 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
Con esta función (muy útil) de Kevin Wright , publicada en la sección de consejos de la wiki de R, 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
El arreglo () en dplyer es mi opción favorita. Utilice el operador de tubería y pase del aspecto menos importante al más importante.
dd1 <- dd %>%
arrange(z) %>%
arrange(desc(x))
En aras de la integridad: 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
En respuesta a un comentario agregado en el OP sobre cómo ordenar mediante programación:
Usando dplyr
y data.table
library(dplyr)
library(data.table)
dplyr
Solo use 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 usar una fórmula ya que también captura el entorno para evaluar una expresión en
tabla de datos
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
Estaba luchando con las soluciones anteriores cuando quería automatizar mi proceso de pedido para n columnas, cuyos nombres de columna podrían ser diferentes cada vez. Encontré una función súper útil del paquete psych
para 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í:
La data.table
paquete R proporciona una ordenación rápida y eficiente de la memoria de data.tables con una sintaxis sencilla (una parte de la cual Matt ha destacado bastante bien en su respuesta ). Ha habido muchas mejoras y también una nueva función setorder()
desde entonces. Desde v1.9.5+
, setorder()
también funciona con data.frames .
Primero, crearemos un conjunto de datos lo suficientemente grande, evaluaremos los diferentes métodos mencionados en otras respuestas y luego listaremos 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 tiempos reportados son de la ejecución de system.time(...)
en estas funciones que 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
# ------------------------------------------------------------
data.table
DT[order(...)]
data.table fue ~ 10 veces más rápida que la de otros métodos (dplyr
), mientras que consumía la misma cantidad de memoria quedplyr
.El
setorder()
fue ~ 14x más rápido que el más rápido de los otros métodos (dplyr
), mientras que solo tomó 0.4GB de memoria extra .dat
está ahora en el orden que requerimos (ya que se actualiza por referencia).
Características de la tabla de datos:
Velocidad:
El ordenamiento de data.table es extremadamente rápido porque implementa el ordenamiento de radix .
La sintaxis
DT[order(...)]
está optimizada internamente para usar el orden rápido de data.table 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, no requerimos el data.rame original o data.table después de reordenar. Es decir, generalmente asignamos 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 que la memoria sea eficiente , data.table , por lo tanto, también proporciona una función
setorder()
.setorder()
reordena data.tablesby reference
( en el lugar ), sin hacer copias adicionales. Solo usa memoria extra igual al tamaño de una columna.
Otras características:
bit64::integer64
tiposinteger
,logical
,numeric
, decharacter
e inclusobit64::integer64
.Tenga en cuenta que las clases de
factor
,Date
,POSIXct
, etc. son todos tiposinteger
/numeric
debajo con atributos adicionales y, por lo tanto, también son compatibles.En la base R, no podemos usar
-
en un vector de caracteres para ordenar por esa columna en orden decreciente. En su lugar tenemos que usar-xtfrm(.)
.Sin embargo, en data.table , solo podemos hacer, por ejemplo,
dat[order(-x)]
osetorder(dat, -x)
.
La respuesta de Dirk es buena, pero si necesita que la ordenación persista, querrá volver a aplicar la ordenación en el nombre de ese marco de datos. Usando el código de ejemplo:
dd <- dd[with(dd, order(-z, b)), ]
La respuesta de Dirk es genial. También destaca una diferencia clave en la sintaxis utilizada para la indexación de data.frame
sy 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ña, pero puede tener consecuencias importantes. Especialmente si escribe un código de producción y / o está preocupado por la corrección en su investigación, es mejor evitar la repetición innecesaria de nombres de variables. data.table
te ayuda a hacer esto.
Aquí hay un ejemplo de cómo la repetición de nombres de variables podría causarle problemas:
Cambiemos el contexto de la respuesta de Dirk y digamos que esto es parte de un proyecto más grande donde hay muchos nombres de objetos y son largos y significativos; en lugar de dd
se llama quarterlyreport
. Se vuelve :
quarterlyreport[with(quarterlyreport,order(-z,b)),]
Está bien. Nada de malo con eso. A continuación, su jefe le pide que incluya el informe del último trimestre en el informe. lastquarterlyreport
tu código, lastquarterlyreport
un objeto al lastquarterlyreport
en varios lugares y de alguna manera (¿cómo demonios?) lastquarterlyreport
con esto:
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
Eso no es lo que quisiste decir, pero no lo viste porque lo hiciste rápido y está ubicado en una página de código similar. El código no se cae (sin advertencia ni error) porque R cree que es lo que querías decir. Esperaría que quienquiera que lea su informe lo encuentre, pero tal vez no lo hagan. Si trabajas mucho con lenguajes de programación, esta situación puede ser muy familiar. Fue un "error tipográfico", dirás. Arreglaré el "error tipográfico" que le dirás a tu jefe.
En data.table
estamos preocupados por pequeños detalles como este. Así que hemos hecho algo simple para evitar escribir nombres de variables dos veces. Algo muy simple. i
se evalúa dentro del marco de dd
ya, automáticamente. No necesitas with()
en absoluto.
En lugar de
dd[with(dd, order(-z, b)), ]
es solo
dd[order(-z, b)]
Y en lugar de
quarterlyreport[with(lastquarterlyreport,order(-z,b)),]
es solo
quarterlyreport[order(-z,b)]
Es una diferencia muy pequeña, pero podría salvarte el cuello algún día. Al sopesar las diferentes respuestas a esta pregunta, considere contar las repeticiones de nombres de variables como uno de sus criterios para decidir. Algunas respuestas tienen bastantes repeticiones, otras no tienen ninguna.
Otra alternativa, usando 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
Puede usar la función order()
directamente sin recurrir a herramientas adicionales. Vea esta respuesta más simple que utiliza un truco desde la parte superior del código de 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
Edite unos 2 años o más después: se le preguntó cómo hacer esto por índice de columna. La respuesta es simplemente pasar la (s) columna (s) de clasificación deseada 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 usar el nombre de la columna (y with()
para un acceso más fácil / más directo).
Si SQL le resulta natural, sqldf maneja ORDER BY según lo previsto por Codd.
Supongamos que tiene un data.frame
A
y quiere clasificarlo usando la columna llamada x
orden descendente. Llame a los datos ordenados newdata
newdata <- A[order(-A$x),]
Si desea un orden ascendente, entonces reemplace "-"
con nada. Puedes 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 ordenar data.frame
A
por x
descendente, y
ascendente z
descendente.
o puedes usar el paquete doBy
library(doBy)
dd <- orderBy(~-z+b, data=dd)