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
with
. TenteM <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))
criar uma matrizM
e use-aM[order(M[,"a"],-M[,"b"]),]
para ordená-la em duas colunas.dd[ order(-dd[,4], dd[,1]), ]
mas não pode ser usadowith
para subconjuntos baseados em nomes.xtfrm
, por exemplodd[ order(-xtfrm(dd[,4]), dd[,1]), ]
.Suas escolhas
order
debase
arrange
dedplyr
setorder
esetorderv
dedata.table
arrange
deplyr
sort
detaRifx
orderBy
dedoBy
sortData
deDeducer
Na maioria das vezes, você deve usar as soluções
dplyr
oudata.table
, a menos que não seja necessário ter nenhuma dependência, nesse caso, usebase::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:
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çãoplyr
como Hadley apontou no tópico acima: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
sort
com 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çõessort.data.frame
do pacote do Dedutor de Kevin Wright ou do pacote taRifx.Tempos medianos:
dd[with(dd, order(-z, b)), ]
778dd[order(-dd$z, dd$b),]
788Tempo médio: 1,567
Tempo médio: 862
Tempo médio: 1.694
Observe que o doBy leva um bom tempo para carregar o pacote.
Não foi possível carregar o Dedutor. Precisa do console JGR.
Não parece ser compatível com a marca de microbench devido à conexão / remoção.
(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
arrange
oplyr
pacote . 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 chamadossort(object)
, mas eu entendo por que Hadley fez dessa maneira devido a questões discutidas na pergunta acima.fonte
taRifx::autoplot.microbenchmark
.b
é classificada na amostra. O padrão é classificar por crescente, para que você não o envolvadesc
. Ascendente em ambos:arrange(dd,z,b)
. Descendente em ambos:arrange(dd,desc(z),desc(b))
.?arrange
: "# NOTA: as funções plyr NÃO preservam row.names". Isso torna a excelentearrange()
função abaixo do ideal, se alguém quiser manterrow.names
.A resposta de Dirk é ótima. Ele também destaca uma diferença fundamental na sintaxe utilizada para a indexação
data.frame
s edata.table
s: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
dd
ser chamadoquarterlyreport
. Se torna :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
lastquarterlyreport
em vários lugares e, de alguma forma (como está o mundo?), Acaba com isso: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.table
que 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 dedd
já, automaticamente. Você não precisa dewith()
nada.Ao invés de
é apenas
E ao invés de
é apenas
É 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.
fonte
subset()
apenas para evitar ter que me referir repetidamente ao mesmo objeto em uma única chamada.setorder
função também aqui, pois é neste segmento que enviamos todo oorder
tipo de dupes.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):
Para o problema do OP:
fonte
dd[order(-z, b)]
muito fácil de usar e lembrar.data.table
é uma enorme contribuição deR
muitas 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.arrange()
ser completamente declarativo,dd[order(-z, b)]
não é.O pacote R
data.table
fornece 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çãosetorder()
desde então. Dev1.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:
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).data.table
ADT[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 quedplyr
.data.table
'ssetorder()
foi ~ 14x mais rápido do que o mais rápido de outros métodos (dplyr
), tendo apenas 0.4GB de memória extra .dat
agora 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:
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.tablesby reference
( no local ), sem fazer cópias adicionais. Ele usa apenas memória extra igual ao tamanho de uma coluna.Outras características:
Ele suporta
integer
,logical
,numeric
,character
e até mesmobit64::integer64
tipos.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)]
ousetorder(dat, -x)
.fonte
Com essa função (muito útil) de Kevin Wright , publicada na seção de dicas do wiki R, isso é facilmente alcançado.
fonte
ou você pode usar o pacote doBy
fonte
Suponha que você tenha um
data.frame
A
e deseje classificá-lo usando a coluna chamadax
ordem decrescente. Ligue para o classificadodata.frame
newdata
Se você deseja ordem crescente, substitua
"-"
por nada. Você pode ter algo comoonde
x
ez
estão algumas colunasdata.frame
A
. Isso significa classificardata.frame
A
porx
descendente,y
ascendente ez
descendente.fonte
se o SQL vier naturalmente para você, o
sqldf
pacote manipulaORDER BY
como o Codd pretendia.fonte
Como alternativa, usando o pacote Dedutor
fonte
Em resposta a um comentário adicionado no OP sobre como classificar programaticamente:
Usando
dplyr
edata.table
dplyr
Basta usar
arrange_
, que é a versão de avaliação padrão paraarrange
.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
fonte
Aprendi
order
com o exemplo a seguir, que depois me confundiu por um longo tempo:A única razão pela qual este exemplo funciona é porque
order
é classificada pelavector Age
, não pela coluna nomeadaAge
nadata frame data
.Para ver isso, crie um quadro de dados idêntico usando
read.table
nomes de colunas ligeiramente diferentes e sem usar nenhum dos vetores acima:A estrutura de linha acima para
order
não funciona mais porque não há um vetor chamadoage
:A linha a seguir funciona porque
order
classifica a colunaage
emmy.data
.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.call
có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 umdata.frame
noR
. Então, pensei que minha versão expandida dessedo.call
código original poderia ser útil.fonte
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 [].data.frame
s para usarwith
ou$
.do.call
isso 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.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:
fonte
O arranjo () no dplyer é minha opção favorita. Use o operador do tubo e passe do aspecto menos importante para o mais importante
fonte
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:fonte
Por uma questão de integridade: você também pode usar a
sortByCol()
função doBBmisc
pacote:Comparação de desempenho:
fonte
data.frame
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.
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.
Pode não ser o mais rápido, mas é certamente simples e confiável
fonte
Outra alternativa, usando o
rgr
pacote:fonte
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
psych
pacote para fazer isso de uma maneira direta:Onde
columnIndices
estã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'
fonte