1

How can I add labels to the groups of bars on the x- axis (which is the left side of the graph)? It is easy to label the entire axis, or to allow the labels to be generated based on the data, but I am unable to figure out how to label each group of bars, if that makes sense.

I know I could recode the item data into complete sentences, but that seems inelegant relative to making some change to the ggplot code.

I have tried using the code from a similar question on this site (Customize axis labels) scale_x_discrete(breaks = 1:5, labels=c("foo","bar","baz","phi","fum")) + but it simply causes all of the labels to disappear from my graph, and I'm not sure why. That result is worse than using scale_x_discrete(waiver()) +

First, load libraries + color palette:

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

And given the following data:

item <- c("none","none","none",
          "low", "low", "low", 
          "moderatelow","moderatelow","moderatelow",
          "moderate","moderate","moderate",
          "greatest","greatest","greatest")

mean <- c(2.566, 2.873, 3.286, # none - Scenario A
          3.911, 4.123, 4.519, # low - Scenario B
          4.113, 4.169, 4.174, # moderatelow - Scenario C
          3.88, 3.589, 3.2,    # moderate - Scenario D
          3.065, 2.544, 2.107) # greatest - Scenario E

reg <- c("GP", "hunt", "trap",
         "GP", "hunt", "trap",
         "GP", "hunt", "trap",
         "GP", "hunt", "trap",
         "GP", "hunt", "trap")

mydata <- data.frame(item, mean, reg)

I used the following code to generate the figure

ggplot(mydata, aes(x = item, y = mean, fill = reg)) + 
  ggtitle("How acceptable are each of the following scenarios to you?")+
  coord_flip() + 
  geom_bar(position = "dodge", stat = "identity") + 
  # facet_wrap(~item, scales = "free_x") +   # changed
  scale_fill_manual(values=cbPalette) +
  # scale_fill_grey(start = 0.8, end = 0.2) +
  ylab("1 = highly unacceptable, 7 = highly acceptable") + 
  xlab("") + 
  theme_bw() +
  #theme(legend.position="bottom")+ 
  scale_x_discrete(waiver()) + 
  labs(fill="reg")

Here is the resulting figure: enter image description here

ETA something for the millionth time - I figured out what works for me, which is using

  ggtitle("How acceptable are each of the following scenarios to you?")+
  coord_flip() + 
  geom_bar(position = "dodge", stat = "identity") + 
  # facet_wrap(~item, scales = "free_x") +   # changed
  scale_fill_manual(values=cbPalette) +
  # scale_fill_grey(start = 0.8, end = 0.2) +
  ylab("1 = highly unacceptable, 7 = highly acceptable") + 
  xlab("") + 
  theme_bw() +
  #theme(legend.position="bottom")+ 
  scale_x_discrete(breaks=c("none", "low", "moderatelow", "moderate", "greatest"),
                   labels=c("Control", "Treat 1", "Treat 2", "slkdj", "adkljf")) + 
  labs(fill="reg")

Thank you so much to those of you who commented! Your help led me to the answer.

ETA - okay here I am, back again. It was pointed out to me by @Gregor Thomas that my scale limits are set incorrectly, along with the unnecessary nature of some of my code. This feedback is much appreciated. Using the guidance of commentors, I was able to resolve the labeling issue that existed.

But, now I cannot figure out how to adjust the limits of the axis in the new code format. Given the following, how can I set the scale from 1-7 to reflect the nature of the likert scale people responded to? See code below.

ggplot(mydata, aes(y = item, x = mean, fill = reg)) + 
  geom_col(position = "dodge") + 
  scale_fill_manual(values = cbPalette) +
  scale_y_discrete(breaks=c("none", "low", "moderatelow", "moderate", "greatest"),
                   labels=c("No wolves", "Very low numbers of wolves", "Moderately low numbers of wolves", "Moderate numbers of wolves", "Greatest numbers of wolves that can be sustained")
  ) + 
  scale_x_continuous(expand = expansion(mult = c(0, .05))) +
  labs(
    title = "How acceptable are each of the following scenarios to you?",
    x = "1 = highly unacceptable, 7 = highly acceptable",
    y = "",
    fill = "population"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom",
    panel.grid.major.y = element_blank()
  )
Meg
  • 39
  • 5
  • 1
    Instead of `scale_x_discrete(breaks = NULL)` use `scale_x_discrete(labels = paste("Scenario", 1:5))` (or whatever labels you want. – Gregor Thomas Jan 07 '22 at 18:11
  • 1
    Or, if you add a column to your data frame with the labels, use `x = that_column` instead of `x = item`, remove `scale_x_discrete()` entirely and the default settings will show your labels. – Gregor Thomas Jan 07 '22 at 18:13
  • I can use ```scale_x_discrete(waiver())``` which is fine, but what I actually want to do is replace the labels with the associated statements e.g., "no animals" -> "greatest numbers of animals." I could do so by changing the data itself (e.g., changing "none" to "no animals"), but that seems silly relative to somehow assigning labels somehow. And, in some cases in the future, I would want to enter sentences to reflect a survey item, etc. – Meg Jan 07 '22 at 18:20
  • I tried using ```scale_x_discrete(breaks = 1:5, labels=c("foo","bar","baz","phi","fum")) +``` but for some reason in my figure this just causes all of the text label to disappear. – Meg Jan 07 '22 at 18:36
  • 1
    You might need to put that *before* the `coord_flip`... `coord_flip` can do weird things and is best left for the end. (Or you could not use coord flip at all, just map the discrete variable to `y` and the continuous variable to `x`. `coord_flip` hasn't been necessary for a few versions). – Gregor Thomas Jan 07 '22 at 19:00
  • If you do specify `breaks`, they need to match the values in your data frame. But you don't have to. As I said in the first comment, you can **just** specify labels. – Gregor Thomas Jan 07 '22 at 19:01

2 Answers2

2

Here's how I'd clean up your code. I skip the coord_flip, just mapping the x and y variables as desired. I consolidate all the labels into labs(), and I use scale_y_discrete(labels = ) for the labels.

my_labels = rev(paste("Scenario", LETTERS[1:5]))

ggplot(mydata, aes(y = item, x = mean, fill = reg)) + 
  geom_col(position = "dodge") + 
  scale_fill_manual(values = cbPalette) +
  scale_y_discrete(
    labels = my_labels
  ) + 
  labs(
    title = "How acceptable are each of the following scenarios to you?",
    x = "1 = highly unacceptable, 7 = highly acceptable",
    y = "",
    fill = "population"
  ) +
  theme_bw() +
  theme(legend.position = "bottom")

enter image description here

If this were my plot, I'd adjust the x-scale to remove the padding below 0, and I'd remove the y gridlines, like this:

ggplot(mydata, aes(y = item, x = mean, fill = reg)) + 
  geom_col(position = "dodge") + 
  scale_fill_manual(values = cbPalette) +
  scale_y_discrete(
    labels = my_labels
  ) + 
  scale_x_continuous(expand = expansion(mult = c(0, .05))) +
  labs(
    title = "How acceptable are each of the following scenarios to you?",
    x = "1 = highly unacceptable, 7 = highly acceptable",
    y = "",
    fill = "population"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom",
    panel.grid.major.y = element_blank()
  )

enter image description here

Though if 1 is "highly unacceptable", I don't know how to interpret 0... the whole x scale is seems confusing. Maybe you should set the x limits to be from 1 to 7, not 0 to max of data (which is 5)? If so, use scale_x_continuous(expand = expansion(mult = c(0, .05)), limits = c(1, 7)).

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
  • 1
    omg i am an idiot lol. I did not notice that the scale starts at 0 FML. Currently fixing that in every graph I've made for this presentation... I also did not realize that you could avoid use of ```coord_flip``` which is great, and I see how you've made that change. I am learning a lot from your feedback so thank you! – Meg Jan 07 '22 at 19:20
  • Glad to hear! Yes, ggplot version 3.3.0 let geoms like `geom_bar` work in either direction so you don't need `coord_flip`, and it's really helped to simplify code. Also note I used `geom_col`, which is basically the same as `geom_bar` but it has `stat = "identity"` by default. I'd also strongly recommend grouping similar plot elements together - all the `scale_*` calls together, all the labels in a single `labs()` call, all the `theme` stuff at the end. It's a nice style to help keep things organized and clear. – Gregor Thomas Jan 07 '22 at 19:26
  • ugh. I last used ggplot in 2015 so I am very rusty and things have changed. – Meg Jan 07 '22 at 19:30
  • I am having trouble with the scale limits. Heh. I have taken your code and adjusted it for my purposes, which works great with the exception of the ```scale_x_continuous```. Do you know why? See edit above, if you have the time and energy. Thank you again for your help with this. – Meg Jan 07 '22 at 19:49
  • So, I guess you can't really have bar plots with the 0 chopped off. What you can do is fake it by making your scale go from 0 to 6, but label it 1 to 7. Do that with `scale_x_continuous(expand = expansion(mult = c(0, .05)), limits = c(0, 6), labels = \(x) x + 1)` (you'll need R version 4.1 or higher to use that `\(x)` syntax). – Gregor Thomas Jan 08 '22 at 03:03
  • 1
    **However**, I think a bar plot starting at 1 is a pretty poor way to see a likert scale. Really poor ratings are minimized visually. A better (and fairly common) way to visualize it is to have the bars start in the middle of the scale and go up/right for above average rating and down/left for bad ratings. that way average ratings are minimized visually (shortest bars) but extreme ratings show up well. You can do that use `x = mean - 4` inside your `aes()` and `scale_x_continuous(limits = c(-3, 3), labels = \(x) x + 4, breaks = -3:3, expand = expansion(0, 0))` – Gregor Thomas Jan 08 '22 at 03:14
1

We could add facet_grid(item ~ ., scales="free_y", space="free_y", switch="y")

ggplot(mydata, aes(x = item, y = mean, fill = reg)) + 
  ggtitle("How acceptable are each of the following scenarios to you?")+
  coord_flip() + 
  geom_bar(position = "dodge", stat = "identity") + 
  # facet_wrap(~item, scales = "free_x") +   # changed
  scale_fill_manual(values=cbPalette) +
  # scale_fill_grey(start = 0.8, end = 0.2) +
  ylab("1 = highly unacceptable, 7 = highly acceptable") + 
  xlab("") + 
  theme_bw() +
  theme(legend.position="bottom")+ 
  scale_x_discrete(breaks = NULL) + 
  labs(fill="population") +
  facet_grid(item ~ ., scales="free_y", space="free_y", switch="y") +
  guides(fill=FALSE)

enter image description here

TarJae
  • 72,363
  • 6
  • 19
  • 66