Search code examples
rggplot2bar-chartgeom-bar

How to add space between sets of categories in a dodged geom_bar in ggplot2?


library(tidyverse)
library(ggsci)

DF <- tibble(Decision = sample(c("Negative","Positive"), 500, T),
             Category1 = sample(c("X", "Y", "Z"), 500, T),
             Category2 = sample(c("Yellow", "Blue", "Black", "White"), 500, T),
             Category3 = sample(c("Xyz", "Yes", "Zos"), 500, T),
             Category4 = sample(c("O", "F"), 500, T),
             Category5 = sample(c("Xxx", "Yyy", "Zzz", "ooo", "Aha!"), 500, T))

I have a dataset with five different questions, each with a unique set of answers. Each person who answered these five questions ended with either a positive or negative decision (the 6th variable). I created a bar plot which shows the percent of positive decisions separately for each answer to each question using the code below:

DF %>% pivot_longer(cols = 2:6, values_to = "Answer", names_to = "Category") %>% 
  count(Category, Decision, Answer) %>% 
   group_by(Category, Answer) %>% mutate(percent = n / sum(n) * 100) %>% 
    filter(Decision == "Positive") %>% 
  ggplot(aes(Answer %>% fct_reorder2(percent, Category), percent, fill = Category)) +
  geom_bar(stat = "identity", position = position_dodge(), width = 0.95, color = "black", alpha = 0.5) + coord_flip() +
  scale_fill_uchicago() + labs(x = "", y = "", fill = "") + scale_y_continuous(breaks = seq(0, 100, 20), labels = str_c(seq(0, 100, 20), "%")) +
    theme_classic() + theme(legend.position = "top")

This is the product: enter image description here

My question is - is there anyway to bring some space between the sets of answers to each of the questions? I would like the columns of the same color to be next to each other, but at the same time, I would like to add some space between columns of different color, as to make it more clear visually that those are 5 different variables.

Additionally, if it is possible, I would like to present the columns in a descending order separately for each category. Unfortunately, adding width to position_dodge

position_dodge(0.5)

does not work, which I guess makes sense.

I will be grateful for any help. Thank you in advance!


Solution

  • This could be achieved like so:

    1. To add some space between categories you could make use of facet_grid, get rid of the strip texts and set the panel spacing to zero. Additionally I make use of space="free" so that you bars still have the same width.

    2. To reorder your bars in descending order you could make use of tidytext:: reorder_within and tidytext::scale_x_reordered()

    library(tidyverse)
    library(ggsci)
    library(tidytext)
    
    set.seed(42)
    
    DF <- tibble(
      Decision = sample(c("Negative", "Positive"), 500, T),
      Category1 = sample(c("X", "Y", "Z"), 500, T),
      Category2 = sample(c("Yellow", "Blue", "Black", "White"), 500, T),
      Category3 = sample(c("Xyz", "Yes", "Zos"), 500, T),
      Category4 = sample(c("O", "F"), 500, T),
      Category5 = sample(c("Xxx", "Yyy", "Zzz", "ooo", "Aha!"), 500, T)
    )
    
    DF %>%
      pivot_longer(cols = 2:6, values_to = "Answer", names_to = "Category") %>%
      count(Category, Decision, Answer) %>%
      group_by(Category, Answer) %>%
      mutate(percent = n / sum(n) * 100) %>%
      filter(Decision == "Positive") %>%
      ungroup() %>% 
      mutate(Answer = tidytext::reorder_within(Answer, by = percent, within = Category)) %>% 
      ggplot(aes(Answer, percent, fill = Category)) +
      geom_bar(stat = "identity", position = position_dodge(), width = 0.9, color = "black", alpha = 0.5) +
      coord_flip() +
      scale_fill_uchicago() +
      labs(x = "", y = "", fill = "") +
      scale_y_continuous(breaks = seq(0, 100, 20), labels = str_c(seq(0, 100, 20), "%")) +
      tidytext::scale_x_reordered() +
      facet_grid(Category ~ ., scales = "free_y", space = "free") +
      theme_classic() +
      theme(legend.position = "top", strip.text = element_blank(), panel.spacing.y = unit(0, "pt"))