0

I am making the following 2 figures in ggplot:

  • 1 describing acceptance towards a trapping season, and
  • 1 describing acceptance towards a hunting season.

The problem is this: the order in which the populations appear on the x-axis (left side of graph) swaps around between the two, and I cannot figure out how to change this and manually select the order. I thought adding labels and such might enforce the change, but I was wrong. For whatever reason, I was able to figure out how to enforce order in the legend & stacks, but not the x-axis.

Furthermore, how does R/ggplot even decide which goes where? As you can see, the code between the 2 figures is virtually identical. Why is there this difference in the first place?

My preferred order of populations on the x-axis is:

  1. General public, 2. UP, 3. NL, 4. SL, 5. Hunters, 6. Trappers.

How can I enforce these constraints?

See the two graphs:

Graph 1: Acceptance towards trapping season

image 1

Graph 2: Acceptance towards hunting season

image 2

And, of course, the code and data to run them:

## load libraries & palette
library(ggplot2)
library(tidyverse)
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

## trapping graph

pop <- c("hunt", "hunt", "hunt",
         "trap", "trap", "trap",
         "gp", "gp", "gp",
         "UP", "UP", "UP", 
         "SL", "SL", "SL", 
         "NL", "NL", "NL")

res <- c(1, 2, 3,
         1, 2, 3,
         1, 2, 3,
         1, 2, 3, 
         1, 2, 3, 
         1, 2, 3)

per <- c(13.66,14.84,71.51,
         4.05,4.63,91.31,
         34.15,18.85,47,
         26.7672,14.98586,58.24694,
         45.04065,21.78862,33.17073,
         35.66622,21.93809,42.39569)

mydata <- data.frame(pop, res, per)

mydata <- mutate(mydata, tres = recode(res, "1" = "Oppose trapping season for wolves",
                                       "2" = "Undecided",
                                       "3" = "Support trapping season for wolves"))


leg_ord <- levels(with(mydata, reorder(tres, res)))
mydata$tres <- factor(mydata$tres, rev(leg_ord))

ggplot()+
  geom_bar(data = mydata, aes(x = reorder(pop,per), y=per, fill=tres), position="stack", stat="identity")+
  coord_flip() + 
  # ggtitle("")+
  xlab("Population")+
  #  scale_fill_brewer(palette="PRGn")+
  scale_x_discrete(breaks=c("gp", "UP", "NL", "SL", "hunt", "trap"),
                   labels=c("General public", "UP", "NL", "SL", "Hunters", "Trappers")) + 
  theme(legend.position="bottom")+ 
  scale_fill_manual(breaks=leg_ord, 
                    values=c("Oppose trapping season for wolves"="#E69F00",
                             "Undecided"="#999999", 
                             "Support trapping season for wolves"="#56B4E9")) + 
  labs(fill="    Response 
       selected")

## hunting graph

pop <- c("hunt", "hunt", "hunt",
         "trap", "trap", "trap",
         "gp", "gp", "gp",
         "UP", "UP", "UP", 
         "SL", "SL", "SL", 
         "NL", "NL", "NL")

res <- c(1, 2, 3,
         1, 2, 3,
         1, 2, 3,
         1, 2, 3, 
         1, 2, 3, 
         1, 2, 3)

per <- c(6.58,9.86,83.56,
         1.69,2.49,95.81,
         22.70,17.00, 60.30,
         17.98493, 12.42938, 69.58569,
         30.95624,21.39384,47.64992,
         22.58065,19.89247,57.52688)

mydata <- data.frame(pop, res, per)

mydata <- mutate(mydata, tres = recode(res, "1" = "Oppose hunting season for wolves",
                                       "2" = "Undecided",
                                       "3" = "Support hunting season for wolves"))


leg_ord <- levels(with(mydata, reorder(tres, res)))
mydata$tres <- factor(mydata$tres, rev(leg_ord))

ggplot()+
  geom_bar(data = mydata, aes(x = reorder(pop,per), y=per, fill=tres), position="stack", stat="identity")+
  coord_flip() + 
  # ggtitle("")+
  xlab("Population")+
  #  scale_fill_brewer(palette="PRGn")+
  scale_x_discrete(breaks=c("gp", "UP", "NL", "SL", "hunt", "trap"),
                   labels=c("General public", "UP", "NL", "SL", "Hunters", "Trappers")) + 
  theme(legend.position="bottom")+ 
  scale_fill_manual(breaks=leg_ord, 
                    values=c("Oppose hunting season for wolves"="#E69F00",
                             "Undecided"="#999999", 
                             "Support hunting season for wolves"="#56B4E9")) + 
  labs(fill="    Response 
       selected")


Please let me know your thoughts & how you would resolve this (and what is even causing it?) Thank you!

Meg
  • 39
  • 5
  • 1
    This is a frequently asked question and has some good answers like here: https://stackoverflow.com/questions/5208679/order-bars-in-ggplot2-bar-graph. The short answer is you need to make your sorted variable a factor so it doesn't show up alphabetically. I like the `forcats` package for this, since it offers a wide variety of ways to specify factor order with consistent syntax. – Jon Spring Jan 07 '22 at 21:14
  • I think just as important (since the code uses `factor` already) is that the two calls to `factor()` need the same `levels=`. – r2evans Jan 07 '22 at 21:15
  • Being alphabetical would make sense, but the order is different between the two despite the code being the same. Why? – Meg Jan 07 '22 at 21:15
  • 1
    Does running `mydata$pop <- factor(mydata$pop, levels = c("gp", "UP", "NL", "SL", "hunt", "trap"))` before your ggplot and then just using `aes(x = pop, ...` resolve it? Seems to work for me. – Jon Spring Jan 07 '22 at 21:23
  • @JonSpring - yes, that works! Except, with the order of the levels in your code reversed. I assume because of ```coord_flip() + ```. Thank you for the help - I haven't used R or ggplot since 2015 and am quite rusty, so run into little things that frustrate me and I lack an intuitive understanding of the code. Your feedback helped a lot and I will apply it often. – Meg Jan 07 '22 at 21:29
  • Glad that worked. I'm guessing the issue is that y axis on ggplot goes up from bottom, so first values of factor will be on bottom. You could plop on a `%>% forcats::fct_rev()` to reverse the order of the factor if you don't want to retype. – Jon Spring Jan 07 '22 at 21:36
  • @JonSpring That is... too complicated for me. I am a novice when it comes to this stuff, just muddling through. Your feedback was so helpful and I really, really appreciate you taking the time to explain this! – Meg Jan 07 '22 at 21:39
  • SOLVED IT! `mydata$per` looks like a "share of group" measure, so it totals to 1 for each group, and averages close to 33.33 since every group has three values. `reorder(pop, per)` is sorting by default using the mean of `per`, which is always about 33.33, so will be basically tied each time, but probably sorting based on minute meaningless differences in the floating point numbers, ie between 33.3333333333332 and 33.33333333333333 sort of thing. – Jon Spring Jan 07 '22 at 21:45
  • @JonSpring Ahh that makes sense. But, I would've never figured that out, so thank you! I was annoyed that the code was the same between the two, only slight differences in the numbers, but the orders were different and I couldn't make sense of it. – Meg Jan 07 '22 at 21:56

0 Answers0