Como classificar um quadro de dados por várias colunas

1316

Eu quero classificar um data.frame por várias colunas. Por exemplo, com o data.frame abaixo, gostaria de classificar por coluna z(decrescente) e depois por coluna b(crescente):

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
Christopher DuBois
fonte

Respostas:

1626

Você pode usar a order()função diretamente sem recorrer a ferramentas complementares - veja esta resposta mais simples, que usa um truque logo no início do example(order)código:

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 mais de 2 anos depois: foi perguntado como fazer isso pelo índice da coluna. A resposta é simplesmente passar a (s) coluna (s) de classificação desejada (s) para a order()função:

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> 

em vez de usar o nome da coluna (e with()para acesso mais fácil / mais direto).

Dirk Eddelbuettel
fonte
@Dirk Eddelbuettel, existe um método igualmente simples para matrizes?
Jota
14
Deve funcionar da mesma maneira, mas você não pode usar with. Tente M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))criar uma matriz Me use-a M[order(M[,"a"],-M[,"b"]),]para ordená-la em duas colunas.
Dirk Eddelbuettel
4
Fácil: dd[ order(-dd[,4], dd[,1]), ]mas não pode ser usado withpara subconjuntos baseados em nomes.
Dirk Eddelbuettel 21/10/12
18
Eu tenho o erro "argumento inválido para o operador unário" ao executar o segundo exemplo.
Nailgun
21
O erro "argumento inválido para o operador unário" ocorre quando você usa menos com uma coluna de caracteres. Resolva-o envolvendo a coluna xtfrm, por exemplo dd[ order(-xtfrm(dd[,4]), dd[,1]), ].
Richie Cotton
477

Suas escolhas

  • order de base
  • arrange de dplyr
  • setordere setordervdedata.table
  • arrange de plyr
  • sort de taRifx
  • orderBy de doBy
  • sortData de Deducer

Na maioria das vezes, você deve usar as soluções dplyrou data.table, a menos que não seja necessário ter nenhuma dependência, nesse caso, use base::order.


Recentemente, adicionei sort.data.frame a um pacote CRAN, tornando-o compatível com a classe, conforme discutido aqui: Melhor maneira de criar consistência genérica / método para sort.data.frame?

Portanto, dado o data.frame dd, você pode classificar da seguinte maneira:

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 você é um dos autores originais desta função, entre em contato comigo. A discussão sobre domínio público está aqui: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Você também pode usar a arrange()função plyrcomo Hadley apontou no tópico acima:

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

Benchmarks: Observe que eu carreguei cada pacote em uma nova sessão R, pois havia muitos conflitos. Em particular, o carregamento do pacote doBy faz sortcom que retorne "O (s) objeto (s) a seguir estão mascarados de 'x (posição 17)': b, x, y, z" e o carregamento das substituições sort.data.framedo pacote do Dedutor de Kevin Wright ou do pacote 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
)

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

Tempo médio: 1,567

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

Tempo médio: 862

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

Tempo médio: 1.694

Observe que o doBy leva um bom tempo para carregar o pacote.

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

Não foi possível carregar o Dedutor. Precisa do 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)

Não parece ser compatível com a marca de microbench devido à conexão / remoção.


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

plotagem de marca de microbench

(as linhas se estendem do quartil inferior ao quartil superior, ponto é a mediana)


Dados esses resultados e pesando simplicidade versus velocidade, eu teria que concordar com arrangeo plyrpacote . Ele tem uma sintaxe simples e, no entanto, é quase tão rápido quanto o R básico comanda com suas maquinações complicadas. Um trabalho tipicamente brilhante de Hadley Wickham. Minha única preocupação é que ele quebra a nomenclatura R padrão pela qual os objetos de classificação são chamados sort(object), mas eu entendo por que Hadley fez dessa maneira devido a questões discutidas na pergunta acima.

Ari B. Friedman
fonte
3
A função marca de microbench ggplot2 acima agora está disponível como taRifx::autoplot.microbenchmark.
Ari B. Friedman
@ AriB.Friedman Quais são os intervalos dos eixos y / qual é a escala?
naught101
@ naught101 O eixo y começa em 0. A escala deve ser microssegundos.
Ari B. Friedman
2
@AME veja como bé classificada na amostra. O padrão é classificar por crescente, para que você não o envolva desc. Ascendente em ambos: arrange(dd,z,b). Descendente em ambos: arrange(dd,desc(z),desc(b)).
Ari B. Friedman
2
Conforme ?arrange: "# NOTA: as funções plyr NÃO preservam row.names". Isso torna a excelente arrange()função abaixo do ideal, se alguém quiser manter row.names.
landroni
149

A resposta de Dirk é ótima. Ele também destaca uma diferença fundamental na sintaxe utilizada para a indexação data.frames e 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)]

A diferença entre as duas chamadas é pequena, mas pode ter consequências importantes. Especialmente se você escrever um código de produção e / ou se preocupar com a correção em sua pesquisa, é melhor evitar a repetição desnecessária de nomes de variáveis. data.table ajuda você a fazer isso.

Aqui está um exemplo de como a repetição de nomes de variáveis ​​pode causar problemas:

Vamos mudar o contexto da resposta de Dirk e dizer que isso faz parte de um projeto maior, onde há muitos nomes de objetos e eles são longos e significativos; em vez de ddser chamado quarterlyreport. Se torna :

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

Ok, tudo bem. Nada de errado com isso. Em seguida, seu chefe pede que você inclua o relatório do último trimestre no relatório. Você analisa seu código, adiciona um objeto lastquarterlyreportem vários lugares e, de alguma forma (como está o mundo?), Acaba com isso:

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

Não foi isso que você quis dizer, mas não o localizou porque o fez rápido e está aninhado em uma página de código semelhante. O código não cai (sem aviso e sem erro) porque R pensa que é isso que você quis dizer. Você espera que quem lê o seu relatório o veja, mas talvez não. Se você trabalha muito com linguagens de programação, essa situação pode ser familiar. Foi um "erro de digitação", você dirá. Vou consertar o "erro de digitação" que você dirá ao seu chefe.

Em data.tableque estamos preocupados com detalhes minúsculos como este. Então, fizemos algo simples para evitar digitar nomes de variáveis ​​duas vezes. Algo muito simples. ié avaliado dentro do quadro de ddjá, automaticamente. Você não precisa de with()nada.

Ao invés de

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

é apenas

dd[order(-z, b)]

E ao invés de

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

é apenas

quarterlyreport[order(-z,b)]

É uma diferença muito pequena, mas pode salvar seu pescoço um dia. Ao ponderar as diferentes respostas a essa pergunta, considere contar as repetições de nomes de variáveis ​​como um dos critérios para decidir. Algumas respostas têm algumas repetições, outras não.

Matt Dowle
fonte
9
+1 Este é um ótimo ponto e detalha a sintaxe de R que muitas vezes me irritou. Às vezes, uso subset()apenas para evitar ter que me referir repetidamente ao mesmo objeto em uma única chamada.
Josh O'Brien
2
@ naught101 A FAQ do data.table 1.9 responde a isso?
precisa
5
Eu acho que você pode adicionar a nova setorderfunção também aqui, pois é neste segmento que enviamos todo o ordertipo de dupes.
David Arenburg
125

Há muitas respostas excelentes aqui, mas o dplyr fornece a única sintaxe que eu consigo lembrar rápida e facilmente (e agora uso com muita frequência):

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 o problema do 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
Ben
fonte
2
A resposta aceita não funciona quando minhas colunas são ou são do tipo fator (ou algo parecido) e eu quero classificar de maneira decrescente para essa coluna de fator seguida pela coluna inteira de maneira crescente. Mas isso funciona muito bem! Obrigado!
Saheel Godhane 22/02
10
Por que "apenas"? Acho que data.table é dd[order(-z, b)]muito fácil de usar e lembrar.
precisa
2
Concordado, não há muito entre esses dois métodos, e também data.tableé uma enorme contribuição de Rmuitas outras maneiras. Suponho que, para mim, ter um conjunto a menos de colchetes (ou um tipo a menos de colchetes) nesse caso reduz a carga cognitiva em uma quantidade quase imperceptível.
Ben
7
Para mim, tudo se resume ao fato de arrange()ser completamente declarativo, dd[order(-z, b)]não é.
27415
83

O pacote R data.tablefornece uma ordenação rápida e eficiente na memória de data.tables com uma sintaxe direta (uma parte da qual Matt destacou bastante bem em sua resposta ). Houve muitas melhorias e também uma nova função setorder()desde então. De v1.9.5+, setorder()também trabalha com data.frames .

Primeiro, criaremos um conjunto de dados grande o suficiente e avaliaremos os diferentes métodos mencionados em outras respostas e, em seguida, listaremos os recursos do data.table .

Dados:

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)

Benchmarks:

Os tempos relatados são de execução system.time(...)nessas funções mostradas abaixo. Os horários estão tabulados abaixo (na ordem do mais lento para o mais 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.tableA DT[order(...)]sintaxe era ~ 10x mais rápida que a mais rápida de outros métodos ( dplyr), enquanto consumia a mesma quantidade de memória que dplyr.

  • data.table's setorder()foi ~ 14x mais rápido do que o mais rápido de outros métodos ( dplyr), tendo apenas 0.4GB de memória extra . datagora está na ordem que exigimos (como é atualizado por referência).

recursos data.table:

Rapidez:

  • O pedido do data.table é extremamente rápido porque implementa o pedido de radix .

  • A sintaxe DT[order(...)]é otimizada internamente para usar também a ordem rápida do data.table . Você pode continuar usando a sintaxe básica R familiar, mas acelere o processo (e use menos memória).

Memória:

  • Na maioria das vezes, não exigimos o data.frame ou data.table original após a reordenação. Ou seja, geralmente atribuímos o resultado ao mesmo objeto, por exemplo:

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

    O problema é que isso requer pelo menos duas vezes (2x) a memória do objeto original. Para ser eficiente em termos de memória , o data.table também fornece uma função setorder().

    setorder()reordena data.tables by reference ( no local ), sem fazer cópias adicionais. Ele usa apenas memória extra igual ao tamanho de uma coluna.

Outras características:

  1. Ele suporta integer, logical, numeric, charactere até mesmo bit64::integer64tipos.

    Observe que factor, etc Date, as POSIXctclasses são todos integer/ numerictipos abaixo de atributos adicionais e, portanto, também são suportadas.

  2. Na base R, não podemos usar -um vetor de caracteres para classificar por essa coluna em ordem decrescente. Em vez disso, temos que usar -xtfrm(.).

    No entanto, em data.table , podemos apenas fazer, por exemplo, dat[order(-x)]ou setorder(dat, -x).

Uma corrida
fonte
Obrigado por esta resposta muito instrutiva sobre data.table. No entanto, não entendo o que é "memória de pico" e como você a calculou. Você poderia explicar por favor? Obrigado !
Julien Navarre
Usei Instrumentos -> alocações e relatei o tamanho "Todas as VMs de heap e alocação".
Arun
2
@Arun o link Instrumentos no seu comentário está morto. Gostaria de publicar uma atualização?
22816 MichaelChirico
@MichaelChirico Aqui está um link para informações sobre os instrumentos fabricados pela Apple: developer.apple.com/library/content/documentation/…
n1k31t4
73

Com essa função (muito útil) de Kevin Wright , publicada na seção de dicas do wiki R, isso é facilmente alcançado.

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
Christopher DuBois
fonte
2
Veja minha resposta para o benchmarking do algoritmo usado nesta função.
Ari B. Friedman
39

ou você pode usar o pacote doBy

library(doBy)
dd <- orderBy(~-z+b, data=dd)
George Dontas
fonte
39

Suponha que você tenha um data.frame Ae deseje classificá-lo usando a coluna chamada xordem decrescente. Ligue para o classificadodata.frame newdata

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

Se você deseja ordem crescente, substitua "-"por nada. Você pode ter algo como

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

onde xe zestão algumas colunas data.frame A. Isso significa classificar data.frame Apor xdescendente, yascendente e zdescendente.

Khayelihle
fonte
32

se o SQL vier naturalmente para você, o sqldfpacote manipula ORDER BYcomo o Codd pretendia.

Malecki
fonte
7
MJM, obrigado por apontar este pacote. É incrivelmente flexível e, como metade do meu trabalho já é feito com base em bancos de dados sql, é mais fácil do que aprender muito da sintaxe menos do que intuitiva de R.
Brandon Bertelsen
31

Como alternativa, usando o pacote Dedutor

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

Em resposta a um comentário adicionado no OP sobre como classificar programaticamente:

Usando dplyredata.table

library(dplyr)
library(data.table)

dplyr

Basta usar arrange_, que é a versão de avaliação padrão 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)

mais informações aqui: https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

É melhor usar a fórmula, pois também captura o ambiente para avaliar uma expressão em

Tabela de dados

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
info_seekeR
fonte
18

Aprendi ordercom o exemplo a seguir, que depois me confundiu por um longo 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

A única razão pela qual este exemplo funciona é porque orderé classificada pela vector Age, não pela coluna nomeada Agena data frame data.

Para ver isso, crie um quadro de dados idêntico usando read.tablenomes de colunas ligeiramente diferentes e sem usar nenhum dos vetores acima:

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)

A estrutura de linha acima para ordernão funciona mais porque não há um vetor chamado age:

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

A linha a seguir funciona porque orderclassifica a coluna ageem my.data.

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

Eu pensei que isso valeria a pena postar, dado o quão confuso eu estava com esse exemplo por tanto tempo. Se este post não for considerado apropriado para o tópico, posso removê-lo.

EDIT: 13 de maio de 2014

Abaixo está uma maneira generalizada de classificar um quadro de dados por cada coluna sem especificar nomes de colunas. O código abaixo mostra como classificar da esquerda para a direita ou da direita para a esquerda. Isso funciona se todas as colunas forem numéricas. Eu não tentei com uma coluna de caracteres adicionada.

Encontrei o do.callcódigo há um ou dois meses em um post antigo em um site diferente, mas somente após uma pesquisa extensa e difícil. Não tenho certeza se poderia mudar esse post agora. O presente da linha é o primeiro hit para encomendar um data.frameno R. Então, pensei que minha versão expandida desse do.callcódigo original poderia 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
Mark Miller
fonte
4
Essa sintaxe funciona se você armazenar seus dados em uma tabela de dados, em vez de em um data.frame: require(data.table); my.dt <- data.table(my.data); my.dt[order(age)]funciona porque os nomes das colunas são disponibilizados dentro dos colchetes [].
Frank
Eu não acho que o voto negativo seja necessário aqui, mas também não acho que isso acrescente muito à pergunta em questão , principalmente considerando o conjunto de respostas existente, algumas das quais já capturam o requisito com data.frames para usar withou $.
A5C1D2H2I1M1N2O1R2T1
1
upvote para do.callisso faz um pequeno trabalho de classificação de um quadro de dados com várias colunas . Simplesmente, do.call(sort, mydf.obj)haverá uma bela cascata.
AdamO 25/05
17

A resposta de Dirk é boa, mas se você precisar que a classificação persista, aplique a classificação novamente no nome desse quadro de dados. Usando o código de exemplo:

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

O arranjo () no dplyer é minha opção favorita. Use o operador do tubo e passe do aspecto menos importante para o mais importante

dd1 <- dd %>%
    arrange(z) %>%
    arrange(desc(x))
Kaden Killpack
fonte
7

Apenas por uma questão de completude, uma vez que pouco se tem dito sobre a classificação por números de colunas ... Certamente, pode-se argumentar que isso geralmente não é desejável (porque a ordem das colunas pode mudar, abrindo caminho para erros), mas em algumas situações específicas (quando, por exemplo, você precisa de um trabalho rápido e não existe o risco de alterar as ordens das colunas), pode ser a coisa mais sensata a ser feita, principalmente ao lidar com um grande número de colunas.

Nesse caso, do.call()vem ao resgate:

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
##    (...)
Dominic Comtois
fonte
6

Por uma questão de integridade: você também pode usar a sortByCol()função do BBmiscpacote:

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

Comparação de desempenho:

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
Lars Kotthoff
fonte
4
estranho para adicionar uma comparação de desempenho quando o seu método é o mais lento ... de qualquer maneira dúbia o valor de usar um ponto de referência em um 4-rowdata.frame
MichaelChirico
5

Assim como os classificadores mecânicos de muito tempo atrás, primeiro classifique pela chave menos significativa, depois a próxima mais significativa, etc. Nenhuma biblioteca necessária funciona com qualquer número de chaves e qualquer combinação de chaves ascendentes e descendentes.

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

Agora estamos prontos para fazer a chave mais significativa. A classificação é estável e quaisquer vínculos na chave mais significativa já foram resolvidos.

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

Pode não ser o mais rápido, mas é certamente simples e confiável

Rick
fonte
4

Outra alternativa, usando o rgrpacote:

> 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
Stéphane Laurent
fonte
4

Eu estava lutando com as soluções acima quando queria automatizar meu processo de pedidos para n colunas, cujos nomes de colunas poderiam ser diferentes a cada vez. Eu encontrei uma função super útil do psychpacote para fazer isso de uma maneira direta:

dfOrder(myDf, columnIndices)

Onde columnIndicesestão os índices de uma ou mais colunas, na ordem em que você deseja classificá-los. Mais informações aqui:

Função dfOrder do pacote 'psych'

AHegde
fonte