Ordre des barres dans le graphique à barres ggplot2

301

J'essaie de faire un graphique à barres où la plus grande barre serait la plus proche de l'axe y et la barre la plus courte serait la plus éloignée. Donc, c'est un peu comme le tableau que j'ai

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

J'essaie donc de construire un graphique à barres qui montrerait le nombre de joueurs selon la position

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

mais le graphique montre d'abord la barre du gardien de but, puis la défense et enfin celle de l'attaquant. Je voudrais que le graphique soit ordonné de sorte que la barre de défense soit la plus proche de l'axe y, celui du gardien de but et enfin celui de l'attaquant. Merci

Julio Diaz
la source
12
ggplot ne peut-il pas les réorganiser pour vous sans avoir à jouer avec la table (ou le dataframe)?
tumultous_rooster
1
@ MattO'Brien Je trouve incroyable que cela ne se fasse pas en une seule commande simple
Euler_Salter
@Zimano Dommage que c'est ce que vous obtenez de mon commentaire. Mon observation portait sur les créateurs de ggplot2, pas sur l'OP
Euler_Salter
2
@Euler_Salter Merci pour la clarification, mes sincères excuses pour vous avoir sauté dessus comme ça. J'ai supprimé ma remarque d'origine.
Zimano

Réponses:

214

La clé de la commande est de définir les niveaux du facteur dans l'ordre souhaité. Un facteur ordonné n'est pas requis; les informations supplémentaires dans un facteur ordonné ne sont pas nécessaires et si ces données sont utilisées dans n'importe quel modèle statistique, un mauvais paramétrage pourrait en résulter - les contrastes polynomiaux ne sont pas appropriés pour des données nominales comme celle-ci.

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

figure de barplot

Dans le sens le plus général, nous devons simplement définir les niveaux de facteur pour qu'ils soient dans l'ordre souhaité. S'il n'est pas spécifié, les niveaux d'un facteur seront triés par ordre alphabétique. Vous pouvez également spécifier l'ordre de niveau dans l'appel à factoriser comme ci-dessus, et d'autres moyens sont également possibles.

theTable$Position <- factor(theTable$Position, levels = c(...))
Gavin Simpson
la source
1
@Gavin: 2 simplifications: puisque vous utilisez déjà within, il n'y a pas besoin d'utiliser theTable$Position, et vous pouvez simplement le faire sort(-table(...))pour un ordre décroissant.
Prasad Chalasani
2
@Prasad l'ancien était un reliquat des tests, donc merci de l'avoir signalé. En ce qui concerne ce dernier, je préfère demander explicitement le tri inversé que celui que -vous utilisez, car il est beaucoup plus facile d'obtenir l'intention decreasing = TRUEque de remarquer -dans tout le reste du code.
Gavin Simpson
2
@GavinSimpson; Je pense que la partie sur levels(theTable$Position) <- c(...)conduit à un comportement indésirable où les entrées réelles de la trame de données sont réorganisées, et pas seulement les niveaux du facteur. Voir cette question . Vous devriez peut-être modifier ou supprimer ces lignes?
Anton
2
Tout à fait d'accord avec Anton. Je viens de voir cette question et je suis allé chercher où ils ont obtenu les mauvais conseils à utiliser levels<-. Je vais éditer cette partie, au moins provisoirement.
Gregor Thomas
2
@Anton Merci pour la suggestion (et à Gregor pour l'édition); Je ne ferais jamais ça via levels<-()aujourd'hui. C'est quelque chose d'il y a 8 ans et je ne me souviens pas si les choses étaient différentes à l'époque ou si j'avais simplement tort, mais quoi qu'il en soit, c'est faux et devrait être effacé! Merci!
Gavin Simpson
220

@GavinSimpson: reorderest une solution puissante et efficace pour cela:

ggplot(theTable,
       aes(x=reorder(Position,Position,
                     function(x)-length(x)))) +
       geom_bar()
Alex Brown
la source
7
En effet +1, et surtout dans ce cas où il y a un ordre logique que l'on peut exploiter numériquement. Si nous considérons l'ordre arbitraire des catégories et que nous ne voulons pas alphabétique, il est tout aussi facile (plus facile?) De spécifier les niveaux directement comme indiqué.
Gavin Simpson
2
C'est le plus soigné. Annulez la nécessité de modifier la trame de données d'origine
T.Fung
Charmant, je viens de remarquer que vous pouvez le faire un peu plus avec succès, si tout ce que vous voulez est de commander par la fonction de longueur et l'ordre croissant est correct, ce que je veux souvent faire:ggplot(theTable,aes(x=reorder(Position,Position,length))+geom_bar()
postylem
146

Utilisez scale_x_discrete (limits = ...)pour spécifier l'ordre des barres.

positions <- c("Goalkeeper", "Defense", "Striker")
p <- ggplot(theTable, aes(x = Position)) + scale_x_discrete(limits = positions)
QIBIN LI
la source
12
Votre solution est la plus adaptée à ma situation, car je souhaite programmer pour tracer avec x étant une colonne arbitraire exprimée par une variable dans un data.frame. Les autres suggestions seraient plus difficiles à exprimer l'arrangement de l'ordre de x par une expression impliquant la variable. Merci! S'il y a un intérêt, je peux partager ma solution en utilisant votre suggestion. Juste un autre problème, en ajoutant scale_x_discrete (limites = ...), j'ai trouvé qu'il y a un espace vide aussi large que le graphique à barres, à droite du graphique. Comment puis-je me débarrasser de l'espace vide? Comme cela ne sert à rien.
Yu Shen
Cela semble nécessaire pour commander des barres d'histogramme
geotheory
9
QIBIN: Wow ... les autres réponses ici fonctionnent, mais votre réponse semble de loin non seulement la plus concise et la plus élégante, mais la plus évidente lorsque vous pensez dans le cadre de ggplot. Je vous remercie.
Dan Nguyen
Lorsque j'ai essayé cette solution, sur mes données, je n'ai pas représenté graphiquement les NA. Existe-t-il un moyen d'utiliser cette solution et de lui faire représenter graphiquement les NA?
user2460499
Ceci est une solution élégante et simple - merci !!
Kalif Vaughn,
91

Je pense que les solutions déjà fournies sont trop verbeuses. Une façon plus concise de faire un barplot trié en fréquence avec ggplot est

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

C'est similaire à ce qu'Alex Brown a suggéré, mais un peu plus court et fonctionne sans définition de fonction quelconque.

Mettre à jour

Je pense que mon ancienne solution était bonne à l'époque, mais de nos jours, je préfère utiliser le forcats::fct_infreqtri des niveaux de facteur par fréquence:

require(forcats)

ggplot(theTable, aes(fct_infreq(Position))) + geom_bar()
Holger Brandl
la source
Je ne comprends pas le deuxième argument pour réorganiser la fonction et que fait-il. Pouvez-vous bien vouloir expliquer ce qui se passe?
user3282777
1
@ user3282777 avez-vous essayé les documents stat.ethz.ch/R-manual/R-devel/library/stats/html/… ?
Holger Brandl
1
Excellente solution! C'est bien de voir que d'autres utilisent des solutions tidyverse!
Mike
29

Comme reorder()dans la réponse d'Alex Brown, nous pourrions également utiliser forcats::fct_reorder(). Il triera fondamentalement les facteurs spécifiés dans le 1er argument, selon les valeurs dans le 2ème argument après avoir appliqué une fonction spécifiée (par défaut = médiane, c'est ce que nous utilisons ici car il n'y a qu'une seule valeur par niveau de facteur).

Il est dommage que dans la question de l'OP, l'ordre requis soit également alphabétique car c'est l'ordre de tri par défaut lorsque vous créez des facteurs, donc cela masquera ce que cette fonction fait réellement. Pour que ce soit plus clair, je remplacerai "Gardien de but" par "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')

entrez la description de l'image ici

user2739472
la source
1
IMHO meilleure solution que forcats est ainsi que dplyr un paquet tidyverse.
c0bra
bravo pour Zoalkeeper
otwtm
23

Une simple réorganisation basée sur dplyr des facteurs peut résoudre ce problème:

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
la source
19

Il vous suffit de spécifier que la Positioncolonne doit être un facteur ordonné où les niveaux sont classés par leur nombre:

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

(Notez que le table(Position)produit un compte de fréquence de la Positioncolonne.)

Ensuite, votre ggplotfonction affichera les barres par ordre décroissant de comptage. Je ne sais pas s'il y a une option geom_barpour le faire sans avoir à créer explicitement un facteur ordonné.

Prasad Chalasani
la source
Je n'ai pas complètement analysé votre code là-haut, mais je suis presque sûr que reorder()la bibliothèque de statistiques accomplit la même tâche.
Chase
@Chase comment proposez-vous d'utiliser reorder()dans ce cas? Le facteur nécessitant une réorganisation doit être réorganisé par une fonction de lui-même et j'ai du mal à voir un bon moyen de le faire.
Gavin Simpson
ok, with(theTable, reorder(Position, as.character(Position), function(x) sum(duplicated(x))))c'est une manière, et une autre with(theTable, reorder(Position, as.character(Position), function(x) as.numeric(table(x))))mais ce sont tout aussi compliqués ...
Gavin Simpson
J'ai simplifié légèrement la réponse à utiliser sortplutôt queorder
Prasad Chalasani
@Gavin - j'ai peut-être mal compris le code original de Prasad (je n'ai pas R sur cette machine à tester ...) mais il semblait qu'il réorganisait les catégories en fonction de la fréquence, ce qui reorderest habile à le faire. Je suis d'accord pour cette question que quelque chose de plus impliqué est nécessaire. Désolé pour la confusion.
Chase
17

En plus de forcats :: fct_infreq, mentionné par @HolgerBrandl, il existe forcats :: fct_rev, qui inverse l'ordre des facteurs.

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)             

sortie gplot

Robert McDonald
la source
"fct_infreq (Position)" est la petite chose qui fait tant de choses, merci !!
Paul
12

Je suis d'accord avec zach que compter au sein de dplyr est la meilleure solution. J'ai trouvé que c'était la version la plus courte:

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

Cela sera également beaucoup plus rapide que de réorganiser les niveaux de facteur à l'avance, car le comptage est effectué dans dplyr et non dans ggplot ou using table.

Alexandru Papiu
la source
12

Si les colonnes du graphique proviennent d'une variable numérique comme dans le cadre de données ci-dessous, vous pouvez utiliser une solution plus simple:

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

Le signe moins avant la variable de tri (-Qté) contrôle le sens du tri (croissant / décroissant)

Voici quelques données à tester:

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

Quand j'ai trouvé ce fil, c'était la réponse que je cherchais. J'espère que c'est utile pour les autres.

JColares
la source
8

Une autre alternative consiste à réorganiser pour ordonner les niveaux d'un facteur. Dans l'ordre croissant (n) ou décroissant (-n) en fonction du nombre. Très similaire à celui utilisé fct_reorderdans le forcatspackage:

Ordre décroissant

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

entrez la description de l'image ici

Ordre croissant

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

entrez la description de l'image ici

Trame de données:

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
la source
5

Puisque nous ne regardons que la distribution d'une seule variable ("Position") par opposition à la relation entre deux variables , alors peut-être un histogramme serait le graphique le plus approprié. ggplot a geom_histogram () qui le rend facile:

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

entrez la description de l'image ici

Utilisation de geom_histogram ():

Je pense que geom_histogram ( ) est un peu excentrique car il traite différemment les données continues et discrètes.

Pour les données continues , vous pouvez simplement utiliser geom_histogram () sans paramètres. Par exemple, si nous ajoutons un vecteur numérique "Score" ...

    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

et utilisez geom_histogram () sur la variable "Score" ...

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

entrez la description de l'image ici

Pour les données discrètes comme "Position", nous devons spécifier une statistique calculée calculée par l'esthétique pour donner la valeur y pour la hauteur des barres en utilisant stat = "count":

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

Remarque: Curieusement et confusément, vous pouvez également utiliser stat = "count"pour des données continues et je pense que cela fournit un graphique plus esthétique.

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

entrez la description de l'image ici

Modifications : réponse étendue en réponse aux suggestions utiles de DebanjanB .

indubitablement
la source
0

J'ai trouvé très ennuyeux de ggplot2ne pas proposer de solution «automatique» pour cela. C'est pourquoi j'ai créé la bar_chart()fonction dans ggcharts.

ggcharts::bar_chart(theTable, Position)

entrez la description de l'image ici

Par défaut, bar_chart()trie les barres et affiche un tracé horizontal. Pour changer cet ensemble horizontal = FALSE. De plus, bar_chart()supprime l '«espace» disgracieux entre les barres et l'axe.

Thomas Neitmann
la source