Search code examples
rggplot2geom-bargeom-text

Why are geom_labels backwards?


I'm using the excellent package ggrepel() to position text labels on bars with a bit of jitter. The only trouble is that they are appearing in reverse order. Here's some reproducible code:

library(tidyverse)
require(scales)       # Used for adding percentages to bar charts
library(ggrepel)

# ingest some sample data
structure(list(Q52_bin = structure(c(3L, 2L, 2L, 2L, 2L, 2L, 
2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 1L, 2L, 
2L, 1L, 3L, 2L, 3L, 3L, 1L), .Label = c("low", "medium", "high"
), class = "factor"), Q53_bin = structure(c(2L, 3L, 2L, 2L, 2L, 
2L, 2L, 3L, 2L, 3L, 2L, 1L, 2L, 2L, 1L, 2L, 3L, 2L, 2L, 1L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 3L, 1L, 
2L, 2L, 2L, 1L, 2L, 2L, 3L, 2L), .Label = c("low", "medium", 
"high"), class = "factor"), Q57_bin = structure(c(2L, 2L, 2L, 
2L, 2L, 2L, 2L, 1L, 1L, 1L, 3L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 1L, 
1L, 2L, 2L, 1L, 2L, 2L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 2L, 1L, 3L, 
3L, 1L, 2L, 2L, 2L, 2L, 1L, 3L, 2L, 2L), .Label = c("low", "medium", 
"high"), class = "factor"), Q4 = c(2, 3, 3, 5, 4, 3, 4, 5, 2, 
4, 2, 3, 5, 4, 3, 3, 5, 5, 4, 5, 3, 2, 4, 1, 5, 4, 4, 4, 4, 4, 
5, 3, 5, 1, 5, 5, 4, 5, 4, 1, 4, 2, 1, 5, 4)), row.names = c(NA, 
-45L), class = c("tbl_df", "tbl", "data.frame"))

df <- select(climate_experience_data, Q52_bin, Q53_bin, Q57_bin, Q4)
names(df) <- c("Q52_bin", "Q53_bin", "Q57_bin", "response")
facet_names <- c(`Q52_bin` = "Spirituality", `Q53_bin` = "Politics L/R", `Q57_bin` = "Religiosity", `low`="low", `medium`="medium", `high`="high")
facet_labeller <- function(variable,value){return(facet_names[value])}
q4_levels = c("Not at all", "A little", "Some", "A lot", "A great deal")
df$response <- factor(df$response, ordered = TRUE, levels = c("5", "4", "3", "2", "1"))
df$response <- fct_recode(df$response, "Not at all" = "1", "A little" = "2", "Some" = "3", "A lot" = "4", "A great deal" = "5")
caption <- "How much have you thought about climate change before today?"
df %>% 
  # we need to get the data including facet info in long format, so we use pivot_longer()
  pivot_longer(!response, names_to = "bin_name", values_to = "b") %>% 
  # add counts for plot below
  count(response, bin_name, b) %>%
  group_by(bin_name,b) %>%
  mutate(perc=paste0(round(n*100/sum(n),1),"%")) %>% 
  # run ggplot
  ggplot(aes(x = n, y = "", fill = response, label = perc)) +
  geom_col(position=position_fill(), aes(fill=response)) +
  coord_cartesian(clip = "off") +
  geom_label_repel(
    fill = "white", 
    size = 3,
    min.segment.length = 0,
    max.overlaps = Inf,
    position = position_fill()
    ) +
  scale_fill_brewer(palette="YlOrBr") +
  scale_x_continuous(labels = scales::percent_format(), expand = c(0.05, 0.05)) +
  facet_grid(vars(b), vars(bin_name), labeller=as_labeller(facet_names)) + 
  labs(caption = caption, x = "", y = "") + 
  guides(fill = guide_legend(title = NULL))
ggsave("figures/q4_faceted.png", width = 30, height = 10, units = "cm")

Here's the visual that I'm getting: bar chart

So what's going on here in terms of labels reversing? I'm concerned that there may be something about the plot that is inaccurate!


Solution

  • The issue is that the grouping variable used for your labels is different from the one used for the columns. To fix that you have to explicitly tell ggrepel to group the labels by response using the group aes:

    library(tidyverse)
    library(ggrepel)
    
    df_long <- df %>%
      pivot_longer(!response, names_to = "bin_name", values_to = "b") %>%
      count(response, bin_name, b) %>%
      group_by(bin_name, b) %>%
      mutate(perc = paste0(round(n * 100 / sum(n), 1), "%"))
    
    ggplot(df_long, aes(x = n, y = "")) +
      geom_col(position = position_fill(), aes(fill = response)) +
      coord_cartesian(clip = "off") +
      geom_label_repel(
        aes(group = response, label = perc),
        fill = "white",
        size = 3,
        min.segment.length = 0,
        max.overlaps = Inf,
        position = position_fill()
      ) +
      scale_fill_brewer(palette = "YlOrBr") +
      scale_x_continuous(labels = scales::percent_format(), expand = c(0.05, 0.05)) +
      facet_grid(vars(b), vars(bin_name), labeller = as_labeller(facet_names)) +
      labs(caption = caption, x = "", y = "") +
      guides(fill = guide_legend(title = NULL))