Barre di ordine nel grafico a barre ggplot2


301

Sto cercando di creare un grafico a barre in cui la barra più grande sarebbe più vicina all'asse y e la barra più corta sarebbe più lontana. Quindi è un po 'come il tavolo che ho

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

Quindi sto cercando di costruire un grafico a barre che mostri il numero di giocatori in base alla posizione

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

ma il grafico mostra prima la barra del portiere e poi la difesa, e infine quella dell'attaccante. Vorrei che il grafico fosse ordinato in modo che la barra di difesa fosse più vicina all'asse y, quella del portiere e infine quella dell'attaccante. Grazie


12
ggplot non può riordinarli per te senza dover giocare con la tabella (o il frame di dati)?
tumultous_rooster il

1
@ MattO'Brien Trovo incredibile che ciò non avvenga in un unico, semplice comando
Euler_Salter

@Zimano Peccato che sia quello che stai ricevendo dal mio commento. La mia osservazione era rivolta ai creatori di ggplot2, non
all'OP

2
@Euler_Salter Grazie per il chiarimento, le mie sincere scuse per averti saltato addosso in quel modo. Ho cancellato la mia osservazione originale.
Zimano,

Risposte:


214

La chiave con l'ordinamento è impostare i livelli del fattore nell'ordine desiderato. Non è richiesto un fattore ordinato; le informazioni extra in un fattore ordinato non sono necessarie e se questi dati vengono utilizzati in qualsiasi modello statistico, potrebbe verificarsi una parametrizzazione errata - i contrasti polinomiali non sono corretti per dati nominali come questo.

## 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 del barplot

Nel senso più generale, dobbiamo semplicemente impostare i livelli dei fattori nell'ordine desiderato. Se non specificato, i livelli di un fattore verranno ordinati alfabeticamente. È inoltre possibile specificare l'ordine di livello all'interno della chiamata per fattorizzare come sopra e sono possibili anche altri modi.

theTable$Position <- factor(theTable$Position, levels = c(...))

1
@Gavin: 2 semplificazioni: dal momento che stai già utilizzando within, non è necessario utilizzare theTable$Positione potresti fare solo sort(-table(...))per ordine decrescente.
Prasad Chalasani,

2
@Prasad il primo è stato un residuo dei test, quindi grazie per averlo sottolineato. Per quanto riguarda quest'ultimo, preferisco chiedere esplicitamente il tipo invertito rispetto a quello -che usi in quanto è molto più facile ottenere l'intenzione decreasing = TRUEpiuttosto che notarlo -in tutto il resto del codice.
Gavin Simpson,

2
@GavinSimpson; Penso che la parte relativa levels(theTable$Position) <- c(...)porti a comportamenti indesiderati in cui le voci effettive del frame di dati vengono riordinate e non solo i livelli del fattore. Vedere questa domanda . Forse dovresti modificare o rimuovere quelle righe?
Anton,

2
Sono pienamente d'accordo con Anton. Ho appena visto questa domanda e sono andato a cercare dove hanno ricevuto i cattivi consigli da usare levels<-. Ho intenzione di modificare quella parte, almeno provvisoriamente.
Gregor Thomas,

2
@Anton Grazie per il suggerimento (e a Gregor per la modifica); Non lo farei mai tramite levels<-()oggi. Questo è qualcosa di 8 anni fa e non ricordo se le cose fossero diverse all'epoca o se stavo semplicemente sbagliando, ma a prescindere, è sbagliato e dovrebbe essere cancellato! Grazie!
Gavin Simpson,

220

@GavinSimpson: reorderè una soluzione potente ed efficace per questo:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()

7
Anzi +1, e specialmente in questo caso in cui esiste un ordine logico che possiamo sfruttare numericamente. Se consideriamo l'ordinamento arbitrario delle categorie e non vogliamo alfabetico, è altrettanto facile (più facile?) Specificare i livelli direttamente come mostrato.
Gavin Simpson,

2
Questo è il più pulito. Annullare la necessità di modificare il frame di dati originale
T.Fung

Adorabile, ho appena notato che puoi farlo un po 'più succintamente, se tutto ciò che vuoi è ordinare in base alla funzione lunghezza e l'ordine ascendente va bene, cosa che spesso voglio fare:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem

146

Utilizzo scale_x_discrete (limits = ...)per specificare l'ordine delle barre.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)

12
La tua soluzione è la più adatta alla mia situazione, poiché voglio programmare per tracciare con x che è una colonna arbitraria espressa da una variabile in un data.frame. Gli altri suggerimenti sarebbero più difficili da esprimere la disposizione dell'ordine di x da un'espressione che coinvolge la variabile. Grazie! Se c'è interesse, posso condividere la mia soluzione usando il tuo suggerimento. Ancora un altro problema, aggiungendo scale_x_discrete (limits = ...), ho scoperto che c'è uno spazio vuoto largo quanto il grafico a barre, sulla destra del grafico. Come posso liberarmi dello spazio vuoto? Poiché non serve a nessuno scopo.
Yu Shen,

Ciò sembra necessario per ordinare le barre dell'istogramma
geotheory

9
QIBIN: Wow ... le altre risposte qui funzionano, ma la tua risposta sembra di gran lunga non solo la più concisa ed elegante, ma la più ovvia quando si pensa all'interno del framework di ggplot. Grazie.
Dan Nguyen,

Quando ho provato questa soluzione, sui miei dati, non ho tracciato graficamente i NA. Esiste un modo per utilizzare questa soluzione e avere grafici NA?
user2460499

Questa è una soluzione elegante e semplice - grazie !!
Kalif Vaughn,

91

Penso che le soluzioni già fornite siano eccessivamente dettagliate. È un modo più conciso per creare un grafico a barre ordinato in frequenza con ggplot

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

È simile a quanto suggerito da Alex Brown, ma un po 'più corto e funziona senza una definizione di funzione qualsiasi.

Aggiornare

Penso che la mia vecchia soluzione fosse buona al momento, ma al giorno d'oggi preferirei usare l' forcats::fct_infreqordinamento dei livelli dei fattori per frequenza:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()

Non capisco il secondo argomento per riordinare la funzione e cosa fa. Puoi gentilmente spiegare cosa sta succedendo?
user3282777

1
@ user3282777 hai provato i documenti stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Holger Brandl,

1
Ottima soluzione! È bello vedere gli altri impiegare soluzioni ordinate!
Mike

29

Come reorder()nella risposta di Alex Brown, potremmo anche usare forcats::fct_reorder(). Fondamentalmente ordinerà i fattori specificati nel 1o argomento, in base ai valori nel 2o argomento dopo aver applicato una funzione specificata (default = mediana, che è ciò che usiamo qui come avere solo un valore per livello di fattore).

È un peccato che nella domanda del PO, l'ordine richiesto sia anche alfabetico in quanto è l'ordinamento predefinito quando si creano i fattori, quindi nasconderà ciò che questa funzione sta effettivamente facendo. Per renderlo più chiaro, sostituirò "Portiere" con "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')

inserisci qui la descrizione dell'immagine


1
La migliore soluzione di IMHO come forcats è dplyr un pacchetto ordinato.
c0bra,

pollice in alto per Zoalkeeper
otwtm

23

Un semplice riordino dei fattori basato su dplyr può risolvere questo 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

19

Devi solo specificare che la Positioncolonna deve essere un fattore ordinato in cui i livelli sono ordinati in base ai loro conteggi:

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

(Notare che table(Position)produce un conteggio di frequenza della Positioncolonna.)

Quindi la tua ggplotfunzione mostrerà le barre in ordine decrescente di conteggio. Non so se esiste un'opzione geom_barper farlo senza dover creare esplicitamente un fattore ordinato.


Non ho analizzato completamente il tuo codice lassù, ma sono abbastanza sicuro che reorder()dalla libreria delle statistiche compia lo stesso compito.
Chase

@Chase come proponi di utilizzare reorder()in questo caso? Il fattore che richiede il riordino deve essere riordinato da qualche funzione di se stesso e sto lottando per vedere un buon modo per farlo.
Gavin Simpson,

ok, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))è un modo, e un altro with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))ma questi sono altrettanto contorti ...
Gavin Simpson

Ho semplificato leggermente la risposta da usare sortpiuttosto cheorder
Prasad Chalasani il

@Gavin - forse ho frainteso il codice originale di Prasad (non ho R su questa macchina per testare ...) ma sembrava che stesse riordinando le categorie in base alla frequenza, che reorderè abile nel fare. Sono d'accordo per questa domanda che è necessario qualcosa di più coinvolto. Dispiace per la confusione.
Chase

17

Oltre a forcats :: fct_infreq, menzionato da @HolgerBrandl, c'è forcats :: fct_rev, che inverte l'ordine dei fattori.

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)             

uscita gplot


"fct_infreq (Position)" è la piccola cosa che fa così tanto, grazie !!
Paul,

12

Sono d'accordo con zach che contare all'interno di dplyr è la soluzione migliore. Ho trovato che questa è la versione più breve:

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

Questo sarà anche significativamente più veloce del riordino dei livelli di fattore in anticipo poiché il conteggio viene eseguito in dplyr non in ggplot o usando table.


12

Se le colonne del grafico provengono da una variabile numerica come nel frame di dati seguente, è possibile utilizzare una soluzione più semplice:

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

Il segno meno prima della variabile di ordinamento (-Qtà) controlla la direzione di ordinamento (crescente / decrescente)

Ecco alcuni dati per i test:

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 ho trovato questa discussione, quella era la risposta che stavo cercando. Spero sia utile per gli altri.


8

Un'altra alternativa che utilizza il riordino per ordinare i livelli di un fattore. In ordine crescente (n) o decrescente (-n) in base al conteggio. Molto simile a quello utilizzato fct_reorderdal forcatspacchetto:

Ordine decrescente

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

inserisci qui la descrizione dell'immagine

Ordine ascendente

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

inserisci qui la descrizione dell'immagine

Data frame:

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

5

Dato che stiamo solo guardando la distribuzione di una singola variabile ("Posizione") invece di guardare la relazione tra due variabili , forse un istogramma sarebbe il grafico più appropriato. ggplot ha geom_histogram () che semplifica:

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

inserisci qui la descrizione dell'immagine

Utilizzando geom_histogram ():

Penso che geom_histogram ( ) sia un po 'bizzarro in quanto tratta i dati continui e discreti in modo diverso.

Per dati continui , puoi semplicemente usare geom_histogram () senza parametri. Ad esempio, se aggiungiamo un vettore numerico "Punteggio" ...

    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 usa geom_histogram () sulla variabile "Punteggio" ...

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

inserisci qui la descrizione dell'immagine

Per dati discreti come "Posizione" dobbiamo specificare una statistica calcolata calcolata dall'estetica per dare il valore y per l'altezza delle barre usando stat = "count":

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

Nota: Curiosamente e in modo confuso puoi anche usare stat = "count"per dati continui e penso che fornisca un grafico esteticamente più piacevole.

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

inserisci qui la descrizione dell'immagine

Modifiche : risposta estesa in risposta ai suggerimenti utili di DebanjanB .


0

Ho trovato molto fastidioso che ggplot2non offre una soluzione 'automatica' per questo. Ecco perché ho creato la bar_chart()funzione in ggcharts.

ggcharts::bar_chart(theTable, Position)

inserisci qui la descrizione dell'immagine

Per impostazione predefinita bar_chart()ordina le barre e visualizza un grafico orizzontale. Per cambiare quel set horizontal = FALSE. Inoltre, bar_chart()rimuove lo sgradevole "spazio" tra le barre e l'asse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.