Barras de pedidos no gráfico de barras ggplot2

301

Estou tentando criar um gráfico de barras em que a barra maior esteja mais próxima do eixo y e a barra mais curta esteja mais distante. Então isso é como a tabela que eu tenho

    Name   Position
1   James  Goalkeeper
2   Frank  Goalkeeper
3   Jean   Defense
4   Steve  Defense
5   John   Defense
6   Tim    Striker

Estou tentando criar um gráfico de barras que mostre o número de jogadores de acordo com a posição

p <- ggplot(theTable, aes(x = Position)) + geom_bar(binwidth = 1)

mas o gráfico mostra a barra do goleiro primeiro, depois a defesa e, finalmente, a atacante. Eu gostaria que o gráfico fosse ordenado para que a barra de defesa fique mais próxima do eixo y, do goleiro e, finalmente, do atacante. obrigado

Julio Diaz
fonte
12
o ggplot não pode reordená-los para você sem ter que mexer com a tabela (ou quadro de dados)?
tumultous_rooster
1
@ MattO'Brien Acho incrível que isso não é feito em um único comando, simples
Euler_Salter
@Zimano Pena que é isso que você está recebendo do meu comentário. Minha observação foi em relação aos criadores ggplot2, e não ao OP
Euler_Salter 24/01
2
@Euler_Salter Obrigado por esclarecer, minhas sinceras desculpas por pular em você assim. Eu apaguei minha observação original.
Zimano 24/01

Respostas:

214

A chave da encomenda é definir os níveis do fator na ordem desejada. Um fator ordenado não é necessário; as informações extras em um fator ordenado não são necessárias e, se esses dados estiverem sendo usados ​​em qualquer modelo estatístico, pode resultar em uma parametrização errada - contrastes polinomiais não são adequados para dados nominais como esse.

## set the levels in order we want
theTable <- within(theTable, 
                   Position <- factor(Position, 
                                      levels=names(sort(table(Position), 
                                                        decreasing=TRUE))))
## plot
ggplot(theTable,aes(x=Position))+geom_bar(binwidth=1)

figura do gráfico de barras

No sentido mais geral, simplesmente precisamos definir os níveis dos fatores para que estejam na ordem desejada. Se não especificado, os níveis de um fator serão classificados em ordem alfabética. Você também pode especificar a ordem do nível na chamada para fatorar, como acima, e outras maneiras também são possíveis.

theTable$Position <- factor(theTable$Position, levels = c(...))
Gavin Simpson
fonte
1
@ Gavin: 2 simplificações: como você já está usando within, não há necessidade de usar theTable$Position, e você pode fazer isso apenas sort(-table(...))por ordem decrescente.
Prasad Chalasani
2
@Prasad o primeiro era um remanescente dos testes, então obrigado por apontar isso. Quanto ao último, prefiro pedir explicitamente o tipo invertido do que o que -você usa, pois é muito mais fácil obter a intenção do decreasing = TRUEque perceber -o restante do código.
Gavin Simpson
2
@GavinSimpson; Eu acho que a parte sobre levels(theTable$Position) <- c(...)leva a um comportamento indesejado, onde as entradas reais do quadro de dados são reordenadas, e não apenas os níveis do fator. Veja esta pergunta . Talvez você deva modificar ou remover essas linhas?
Anton
2
Concordo plenamente com Anton. Acabei de ver essa pergunta e fui bisbilhotar onde eles receberam os maus conselhos levels<-. Vou editar essa parte, pelo menos provisoriamente.
Gregor Thomas
2
@Anton Obrigado pela sugestão (e Gregor pela edição); Eu nunca faria isso via levels<-()hoje. Isso é algo de 8 anos atrás e não me lembro se as coisas eram diferentes na época ou se eu estava completamente errado, mas, independentemente disso, está errado e deve ser apagado! Obrigado!
Gavin Simpson
220

@GavinSimpson: reorderé uma solução poderosa e eficaz para isso:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
Alex Brown
fonte
7
De fato +1, e especialmente neste caso em que existe uma ordem lógica que podemos explorar numericamente. Se considerarmos a ordenação arbitrária de categorias e não queremos alfabética, é igualmente fácil (mais fácil?) Especificar os níveis diretamente, como mostrado.
Gavin Simpson
2
Este é o mais arrumado. Anular a necessidade de modificar trama de dados originais
T.Fung
Adorável, observei que você pode fazer isso de forma um pouco mais sucinta, se tudo o que você deseja é ordenar pela função length e a ordem crescente está correta, o que é algo que eu sempre quero fazer:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem 17/04
146

Usando scale_x_discrete (limits = ...)para especificar a ordem das barras.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
QIBIN LI
fonte
12
Sua solução é a mais adequada para minha situação, pois quero programar para plotar com x sendo uma coluna arbitrária expressa por uma variável em um data.frame. As outras sugestões seriam mais difíceis de expressar o arranjo da ordem de x por uma expressão envolvendo a variável. Obrigado! Se houver interesse, posso compartilhar minha solução usando sua sugestão. Apenas mais uma questão, adicionando scale_x_discrete (limites = ...), descobri que há espaço em branco tão largo quanto o gráfico de barras, à direita do gráfico. Como posso me livrar do espaço em branco? Como não serve para nenhum propósito.
Yu Shen
Este parece ser necessário para barras do histograma de ordenação
geotheory
9
QIBIN: Uau ... as outras respostas aqui funcionam, mas sua resposta parece não ser apenas a mais concisa e elegante, mas a mais óbvia quando se pensa da estrutura do ggplot. Obrigado.
Dan Nguyen
Quando tentei esta solução, ela não representava graficamente os NAs. Existe uma maneira de usar essa solução e fazer o gráfico de NAs?
User2460499
Esta é uma solução elegante e simples - obrigado !!
Kalif Vaughn
91

Eu acho que as soluções já fornecidas são excessivamente detalhadas. Uma maneira mais concisa de fazer um gráfico de barras classificado por frequência com o ggplot é

ggplot(theTable, aes(x=reorder(Position, -table(Position)[Position]))) + geom_bar()

É semelhante ao sugerido por Alex Brown, mas um pouco mais curto e funciona sem uma definição de função anônima.

Atualizar

Acho que minha solução antiga era boa na época, mas hoje em dia prefiro usar os forcats::fct_infreqníveis de fator de classificação por frequência:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()
Holger Brandl
fonte
Não entendo o segundo argumento para reordenar a função e o que ela faz. Você pode gentilmente explicar o que está acontecendo?
user3282777
1
@ user3282777 você já tentou os documentos stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
precisa saber é o seguinte
1
Ótima solução! É bom ver outros empregando soluções arrumadas!
1155 Mike
29

Como reorder()na resposta de Alex Brown, também poderíamos usar forcats::fct_reorder(). Basicamente, classificará os fatores especificados no 1º arg, de acordo com os valores do 2º arg após a aplicação de uma função especificada (padrão = mediana, que é o que usamos aqui como apenas um valor por nível de fator).

É uma pena que, na pergunta do OP, a ordem necessária também seja alfabética, pois é a ordem de classificação padrão quando você cria fatores, para ocultar o que essa função está realmente fazendo. Para deixar mais claro, substituirei "Goleiro" por "Zoalkeeper".

library(tidyverse)
library(forcats)

theTable <- data.frame(
                Name = c('James', 'Frank', 'Jean', 'Steve', 'John', 'Tim'),
                Position = c('Zoalkeeper', 'Zoalkeeper', 'Defense',
                             'Defense', 'Defense', 'Striker'))

theTable %>%
    count(Position) %>%
    mutate(Position = fct_reorder(Position, n, .desc = TRUE)) %>%
    ggplot(aes(x = Position, y = n)) + geom_bar(stat = 'identity')

insira a descrição da imagem aqui

user2739472
fonte
1
A melhor solução da IMHO como forcats é também fornecer um pacote arrumado.
C0bra
thumbs up for Zoalkeeper
otwtm
23

Uma simples reordenação de fatores baseada em dplyr pode resolver esse problema:

library(dplyr)

#reorder the table and reset the factor to that ordering
theTable %>%
  group_by(Position) %>%                              # calculate the counts
  summarize(counts = n()) %>%
  arrange(-counts) %>%                                # sort by counts
  mutate(Position = factor(Position, Position)) %>%   # reset factor
  ggplot(aes(x=Position, y=counts)) +                 # plot 
    geom_bar(stat="identity")                         # plot histogram
zach
fonte
19

Você só precisa especificar a Positioncoluna como um fator ordenado em que os níveis são ordenados por suas contagens:

theTable <- transform( theTable,
       Position = ordered(Position, levels = names( sort(-table(Position)))))

(Observe que table(Position)produz uma contagem de frequência da Positioncoluna.)

Então sua ggplotfunção mostrará as barras em ordem decrescente de contagem. Não sei se há uma opção geom_barpara fazer isso sem ter que criar explicitamente um fator ordenado.

Prasad Chalasani
fonte
Não analisei completamente seu código lá em cima, mas tenho certeza reorder()de que a biblioteca de estatísticas realiza a mesma tarefa.
Perseguição
@ Phase: como você propõe usar reorder()neste caso? O fator que requer reordenação precisa ser reordenado por alguma função em si e estou lutando para encontrar uma boa maneira de fazer isso.
Gavin Simpson
ok, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))é uma maneira, e outro with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x)))), mas estes são tão complicado ...
Gavin Simpson
I simplificou a resposta ligeiramente para usar sortem vez deorder
Prasad Chalasani
@ Gavin - talvez eu tenha entendido mal o código original de Prasad (não tenho R nesta máquina para testar ...), mas parecia que ele estava reorganizando as categorias com base na frequência, que reorderé hábil em fazer. Concordo com esta questão de que é necessário algo mais envolvido. Desculpe pela confusão.
Chase
17

Além de forcats :: fct_infreq, mencionado por @HolgerBrandl, há forcats :: fct_rev, que reverte a ordem dos fatores.

theTable <- data.frame(
    Position= 
        c("Zoalkeeper", "Zoalkeeper", "Defense",
          "Defense", "Defense", "Striker"),
    Name=c("James", "Frank","Jean",
           "Steve","John", "Tim"))

p1 <- ggplot(theTable, aes(x = Position)) + geom_bar()
p2 <- ggplot(theTable, aes(x = fct_infreq(Position))) + geom_bar()
p3 <- ggplot(theTable, aes(x = fct_rev(fct_infreq(Position)))) + geom_bar()

gridExtra::grid.arrange(p1, p2, p3, nrow=3)             

saída gplot

Robert McDonald
fonte
"fct_infreq (Position)" é a pequena coisa que faz muito, obrigado!
Paul Paul
12

Concordo com o zach que contar no dplyr é a melhor solução. Eu descobri que esta é a versão mais curta:

dplyr::count(theTable, Position) %>%
          arrange(-n) %>%
          mutate(Position = factor(Position, Position)) %>%
          ggplot(aes(x=Position, y=n)) + geom_bar(stat="identity")

Isso também será significativamente mais rápido do que reordenar os níveis dos fatores previamente, pois a contagem é feita no dplyr, não no ggplot ou no uso table.

Alexandru Papiu
fonte
12

Se as colunas do gráfico vierem de uma variável numérica como no quadro de dados abaixo, você poderá usar uma solução mais simples:

ggplot(df, aes(x = reorder(Colors, -Qty, sum), y = Qty)) 
+ geom_bar(stat = "identity")  

O sinal de menos antes da variável de classificação (-Qty) controla a direção da classificação (ascendente / descendente)

Aqui estão alguns dados para teste:

df <- data.frame(Colors = c("Green","Yellow","Blue","Red","Yellow","Blue"),  
                 Qty = c(7,4,5,1,3,6)
                )

**Sample data:**
  Colors Qty
1  Green   7
2 Yellow   4
3   Blue   5
4    Red   1
5 Yellow   3
6   Blue   6

Quando encontrei este tópico, essa era a resposta que estava procurando. Espero que seja útil para os outros.

JColares
fonte
8

Outra alternativa usando o reordenamento para ordenar os níveis de um fator. Em ordem crescente (n) ou decrescente (-n) com base na contagem. Muito parecido com o usado fct_reordernoforcats pacote:

Ordem decrescente

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, -n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

insira a descrição da imagem aqui

Ordem ascendente

df %>%
  count(Position) %>%
  ggplot(aes(x = reorder(Position, n), y = n)) +
  geom_bar(stat = 'identity') +
  xlab("Position")

insira a descrição da imagem aqui

Quadro de dados:

df <- structure(list(Position = structure(c(3L, 3L, 1L, 1L, 1L, 2L), .Label = c("Defense", 
"Striker", "Zoalkeeper"), class = "factor"), Name = structure(c(2L, 
1L, 3L, 5L, 4L, 6L), .Label = c("Frank", "James", "Jean", "John", 
"Steve", "Tim"), class = "factor")), class = "data.frame", row.names = c(NA, 
-6L))
mpalanco
fonte
5

Como estamos apenas olhando para a distribuição de uma única variável ("Posição") em vez de olhar para o relacionamento entre duas variáveis , talvez um histograma seja o gráfico mais apropriado. O ggplot possui geom_histogram () que facilita:

ggplot(theTable, aes(x = Position)) + geom_histogram(stat="count")

insira a descrição da imagem aqui

Usando geom_histogram ():

Eu acho que geom_histogram ( ) é um pouco peculiar, pois trata dados contínuos e discretos de maneira diferente.

Para dados contínuos , você pode apenas usar geom_histogram () sem parâmetros. Por exemplo, se adicionarmos um vetor numérico "Pontuação" ...

    Name   Position   Score  
1   James  Goalkeeper 10
2   Frank  Goalkeeper 20
3   Jean   Defense    10
4   Steve  Defense    10
5   John   Defense    20
6   Tim    Striker    50

e use geom_histogram () na variável "Score" ...

ggplot(theTable, aes(x = Score)) + geom_histogram()

insira a descrição da imagem aqui

Para dados discretos como "Position", precisamos especificar uma estatística calculada calculada pela estética para fornecer o valor y da altura das barras usando stat = "count":

 ggplot(theTable, aes(x = Position)) + geom_histogram(stat = "count")

Nota: Curiosa e confusa, você também pode usar stat = "count"para dados contínuos e acho que ele fornece um gráfico mais esteticamente agradável.

ggplot(theTable, aes(x = Score)) + geom_histogram(stat = "count")

insira a descrição da imagem aqui

Edições : resposta estendida em resposta às sugestões úteis de DebanjanB .

indubitavelmente
fonte
0

Achei muito irritante que ggplot2não ofereça uma solução 'automática' para isso. Por isso criei a bar_chart()função ggcharts.

ggcharts::bar_chart(theTable, Position)

insira a descrição da imagem aqui

Por padrão, bar_chart()classifica as barras e exibe uma plotagem horizontal. Para mudar esse conjunto horizontal = FALSE. Além disso, bar_chart()remove a 'lacuna' feia entre as barras e o eixo.

Thomas Neitmann
fonte