Balken im Balkendiagramm ggplot2 bestellen

301

Ich versuche, ein Balkendiagramm zu erstellen, bei dem der größte Balken der y-Achse am nächsten und der kürzeste Balken am weitesten entfernt ist. Das ist also ein bisschen wie der Tisch, den ich habe

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

Ich versuche also, ein Balkendiagramm zu erstellen, das die Anzahl der Spieler nach Position anzeigt

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

Die Grafik zeigt jedoch zuerst die Torwartleiste, dann die Verteidigung und schließlich die Stürmerleiste. Ich möchte, dass die Grafik so angeordnet wird, dass der Verteidigungsbalken der y-Achse am nächsten liegt, der Torhüter und schließlich der Stürmer. Vielen Dank

Julio Diaz
quelle
12
kann ggplot sie nicht für Sie neu anordnen, ohne mit der Tabelle (oder dem Datenrahmen) herumspielen zu müssen?
tumultous_rooster
1
@ MattO'Brien Ich finde es unglaublich, dass dies nicht in einem einzigen, einfachen Befehl
erledigt wird
@Zimano Schade, dass du das von meinem Kommentar bekommst. Meine Beobachtung richtete sich an die Schöpfer von ggplot2, nicht an den OP
Euler_Salter
2
@Euler_Salter Danke für die Klarstellung, ich entschuldige mich aufrichtig dafür, dass ich so auf dich gesprungen bin. Ich habe meine ursprüngliche Bemerkung gelöscht.
Zimano

Antworten:

214

Der Schlüssel bei der Bestellung besteht darin, die Ebenen des Faktors in der gewünschten Reihenfolge festzulegen. Ein geordneter Faktor ist nicht erforderlich. Die zusätzlichen Informationen in einem geordneten Faktor sind nicht erforderlich. Wenn diese Daten in einem statistischen Modell verwendet werden, kann dies zu einer falschen Parametrisierung führen. Polynomkontraste sind für nominelle Daten wie diese nicht geeignet.

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

Barplot Figur

Im allgemeinsten Sinne müssen wir einfach die Faktorstufen so einstellen, dass sie in der gewünschten Reihenfolge liegen. Wenn nicht angegeben, werden die Ebenen eines Faktors alphabetisch sortiert. Sie können auch die Ebenenreihenfolge innerhalb des Aufrufs zum Faktor wie oben angeben. Andere Möglichkeiten sind ebenfalls möglich.

theTable$Position <- factor(theTable$Position, levels = c(...))
Gavin Simpson
quelle
1
@ Gavin: 2 Vereinfachungen: Da Sie bereits verwenden within, ist keine Verwendung erforderlich theTable$Position, und Sie können dies nur tun, sort(-table(...))um die Reihenfolge zu verringern.
Prasad Chalasani
2
@ Prasad der erstere war ein Überbleibsel von Tests, also danke, dass Sie darauf hingewiesen haben. In letzterem -Fall decreasing = TRUEfrage ich lieber explizit nach der umgekehrten Sortierung als nach der von Ihnen verwendeten, da es weitaus einfacher ist, die Absicht zu ermitteln, als die -im gesamten Rest des Codes zu bemerken .
Gavin Simpson
2
@ GavinSimpson; Ich denke, der Teil über levels(theTable$Position) <- c(...)führt zu unerwünschtem Verhalten, bei dem die tatsächlichen Einträge des Datenrahmens neu angeordnet werden und nicht nur die Ebenen des Faktors. Siehe diese Frage . Vielleicht sollten Sie diese Zeilen ändern oder entfernen?
Anton
2
Stimme Anton sehr zu. Ich habe gerade diese Frage gesehen und mich umgesehen, wo sie den schlechten Rat bekommen haben levels<-. Ich werde diesen Teil zumindest vorläufig herausarbeiten.
Gregor Thomas
2
@Anton Danke für den Vorschlag (und Gregor für die Bearbeitung); Ich würde das heute nie tun levels<-(). Dies ist etwas von vor 8 Jahren und ich kann mich nicht erinnern, ob die Dinge damals anders waren oder ob ich einfach falsch lag, aber trotzdem ist es falsch und sollte gelöscht werden! Vielen Dank!
Gavin Simpson
220

@ GavinSimpson: reorderist eine leistungsstarke und effektive Lösung dafür:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
Alex Brown
quelle
7
In der Tat +1, und insbesondere in diesem Fall, in dem es eine logische Reihenfolge gibt, die wir numerisch ausnutzen können. Wenn wir eine willkürliche Reihenfolge der Kategorien berücksichtigen und keine alphabetische Reihenfolge wünschen, ist es genauso einfach (einfacher?), Die Ebenen direkt wie gezeigt anzugeben.
Gavin Simpson
2
Das ist das ordentlichste. Aufheben der Notwendigkeit, den ursprünglichen Datenrahmen zu ändern
T.Fung
Schön, habe gerade bemerkt, dass Sie dies etwas prägnanter tun können, wenn Sie nur nach der Längenfunktion bestellen möchten und die aufsteigende Reihenfolge in Ordnung ist, was ich oft tun möchte:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem
146

Verwenden Sie scale_x_discrete (limits = ...), um die Reihenfolge der Balken festzulegen.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
QIBIN LI
quelle
12
Ihre Lösung ist für meine Situation am besten geeignet, da ich das Plotten so programmieren möchte, dass x eine beliebige Spalte ist, die durch eine Variable in einem data.frame ausgedrückt wird. Die anderen Vorschläge wären schwieriger, die Anordnung der Ordnung von x durch einen Ausdruck auszudrücken, an dem die Variable beteiligt ist. Vielen Dank! Bei Interesse kann ich meine Lösung anhand Ihres Vorschlags teilen. Nur noch ein Problem: Beim Hinzufügen von scale_x_discrete (limit = ...) stellte ich fest, dass rechts neben dem Diagramm ein Leerzeichen vorhanden ist, das so breit ist wie das Balkendiagramm. Wie kann ich die Leerstelle entfernen? Da es keinen Zweck erfüllt.
Yu Shen
Dies scheint notwendig für die Bestellung von Histogramm-Balken
Geotheory
9
QIBIN: Wow ... die anderen Antworten hier funktionieren, aber Ihre Antwort scheint bei weitem nicht nur die prägnanteste und eleganteste zu sein, sondern auch die offensichtlichste, wenn Sie im Rahmen von ggplot denken. Danke dir.
Dan Nguyen
Als ich diese Lösung ausprobierte, wurden in meinen Daten keine NAs grafisch dargestellt. Gibt es eine Möglichkeit, diese Lösung zu verwenden und NAs grafisch darstellen zu lassen?
user2460499
Dies ist eine elegante und einfache Lösung - danke !!
Kalif Vaughn
91

Ich denke, die bereits bereitgestellten Lösungen sind zu ausführlich. Eine präzisere Möglichkeit, ein frequenzsortiertes Barplot mit ggplot zu erstellen, ist

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

Es ähnelt dem, was Alex Brown vorgeschlagen hat, ist jedoch etwas kürzer und funktioniert ohne eine beliebige Funktionsdefinition.

Aktualisieren

Ich denke, meine alte Lösung war zu der Zeit gut, aber heutzutage würde ich lieber forcats::fct_infreqdie Sortierung der Faktorstufen nach Häufigkeit verwenden:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()
Holger Brandl
quelle
Ich verstehe das zweite Argument zur Neuordnung der Funktion nicht und was es tut. Können Sie mir bitte erklären, was passiert?
user3282777
1
@ user3282777 Haben Sie die Dokumente stat.ethz.ch/R-manual/R-devel/library/stats/html/… ausprobiert ?
Holger Brandl
1
Tolle Lösung! Gut zu sehen, dass andere Tidyverse-Lösungen einsetzen!
Mike
29

Wie reorder()in Alex Browns Antwort könnten wir auch verwenden forcats::fct_reorder(). Grundsätzlich werden die im 1. Argument angegebenen Faktoren nach den Werten im 2. Argument sortiert, nachdem eine bestimmte Funktion angewendet wurde (Standard = Median, was wir hier verwenden, da nur ein Wert pro Faktorstufe vorhanden ist).

Es ist eine Schande, dass in der OP-Frage die erforderliche Reihenfolge auch alphabetisch ist, da dies die Standardsortierreihenfolge ist, wenn Sie Faktoren erstellen. Dadurch wird verborgen, was diese Funktion tatsächlich tut. Um es klarer zu machen, werde ich "Torhüter" durch "Zoalhüter" ersetzen.

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

Geben Sie hier die Bildbeschreibung ein

user2739472
quelle
1
IMHO beste Lösung als forcats ist sowie dplyr ein tidyverse Paket.
c0bra
Daumen hoch für Zoalkeeper
otwtm
23

Eine einfache dplyr-basierte Neuordnung von Faktoren kann dieses Problem lösen:

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
quelle
19

Sie müssen nur die PositionSpalte angeben , die ein geordneter Faktor sein soll , bei dem die Ebenen nach ihrer Anzahl geordnet sind:

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

(Beachten Sie, dass das table(Position)eine Frequenzzählung der erzeugtPosition Spalte erzeugt.)

Dann zeigt Ihre ggplotFunktion die Balken in absteigender Reihenfolge der Anzahl an. Ich weiß nicht, ob es eine Option gibt, geom_barohne explizit einen geordneten Faktor erstellen zu müssen.

Prasad Chalasani
quelle
Ich habe Ihren Code dort oben nicht vollständig analysiert, bin mir aber ziemlich sicher, dass reorder()die Statistikbibliothek dieselbe Aufgabe erfüllt.
Chase
@Chase wie schlagen Sie die Verwendung reorder()in diesem Fall vor? Der Faktor, der eine Neuordnung erfordert, muss durch eine Funktion von sich selbst neu angeordnet werden, und ich habe Mühe, einen guten Weg zu finden, dies zu tun.
Gavin Simpson
ok, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))ist ein Weg und ein anderer, with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))aber diese sind genauso verwickelt ...
Gavin Simpson
Ich habe die Antwort leicht vereinfacht, um sie zu verwenden, sortanstattorder
Prasad Chalasani
@ Gavin - vielleicht habe ich Prasads Originalcode falsch verstanden (ich habe kein R auf diesem Computer zum Testen ...), aber es sah so aus, als würde er die Kategorien basierend auf der Häufigkeit neu ordnen, was reordergeschickt ist. Ich stimme dieser Frage zu, dass etwas mehr involviert ist. Entschuldigung für die Verwirrung.
Chase
17

Zusätzlich zu forcats :: fct_infreq, das von @HolgerBrandl erwähnt wird, gibt es forcats :: fct_rev, das die Faktorreihenfolge umkehrt.

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)             

gplot Ausgabe

Robert McDonald
quelle
"fct_infreq (Position)" ist das kleine Ding, das so viel macht, danke !!
Paul
12

Ich stimme zach zu, dass das Zählen innerhalb von dplyr die beste Lösung ist. Ich habe festgestellt, dass dies die kürzeste Version ist:

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

Dies ist auch erheblich schneller als die vorherige Neuordnung der Faktorstufen, da die Zählung in dplyr und nicht in ggplot oder using erfolgt table.

Alexandru Papiu
quelle
12

Wenn die Diagrammspalten von einer numerischen Variablen wie im folgenden Datenrahmen stammen, können Sie eine einfachere Lösung verwenden:

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

Das Minuszeichen vor der Sortiervariablen (-Qty) steuert die Sortierrichtung (aufsteigend / absteigend)

Hier sind einige Daten zum Testen:

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

Als ich diesen Thread gefunden habe, war das die Antwort, nach der ich gesucht habe. Hoffe, es ist nützlich für andere.

JColares
quelle
8

Eine andere Alternative, bei der die Pegel eines Faktors nachbestellt werden. In aufsteigender (n) oder absteigender Reihenfolge (-n) basierend auf der Anzahl. Sehr ähnlich zu dem fct_reorderaus dem forcatsPaket:

Absteigende Reihenfolge

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

Geben Sie hier die Bildbeschreibung ein

Aufsteigende Reihenfolge

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

Geben Sie hier die Bildbeschreibung ein

Datenrahmen:

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
quelle
5

Da wir nur die Verteilung einer einzelnen Variablen ("Position") betrachten, anstatt die Beziehung zwischen zwei Variablen zu betrachten , wäre möglicherweise ein Histogramm das geeignetere Diagramm. ggplot hat geom_histogram () , das es einfach macht:

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

Geben Sie hier die Bildbeschreibung ein

Verwenden von geom_histogram ():

Ich denke, geom_histogram ( ) ist etwas eigenartig, da es kontinuierliche und diskrete Daten unterschiedlich behandelt.

Für kontinuierliche Daten können Sie einfach geom_histogram () ohne Parameter verwenden. Zum Beispiel, wenn wir einen numerischen Vektor "Score" hinzufügen ...

    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

und benutze geom_histogram () für die Variable "Score" ...

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

Geben Sie hier die Bildbeschreibung ein

Für diskrete Daten wie "Position" müssen wir eine berechnete Statistik angeben, die von der Ästhetik berechnet wird, um den y-Wert für die Höhe der Balken zu erhalten, indem wir stat = "count":

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

Hinweis: Seltsamerweise und verwirrend können Sie es auch stat = "count"für kontinuierliche Daten verwenden, und ich denke, es bietet ein ästhetisch ansprechenderes Diagramm.

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

Geben Sie hier die Bildbeschreibung ein

Änderungen : Erweiterte Antwort als Antwort auf die hilfreichen Vorschläge von DebanjanB .

zweifellos
quelle
0

Ich fand es sehr ärgerlich, dass ggplot2dies keine "automatische" Lösung bietet. Deshalb habe ich die bar_chart()Funktion in erstellt ggcharts.

ggcharts::bar_chart(theTable, Position)

Geben Sie hier die Bildbeschreibung ein

Standardmäßig werden bar_chart()die Balken sortiert und ein horizontales Diagramm angezeigt. Um diesen Satz zu ändern horizontal = FALSE. Darüber hinaus bar_chart()entfernt die unansehnliche ‚Lücke‘ zwischen den Stäben und der Achse.

Thomas Neitmann
quelle