Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I center geom_label_repel labels so that they are in the middle of each bar?

I’m using geom_label_repel to place percentages in a faceted horizontal bar chart, which mostly works great, except that I can’t seem to find a way to shift the labels to the middle (or as close as possible) of each bar. Instead, they are justified right, which visually is a bit confusing as some bars are close to one another.

I’ve tried using h_just and v_just, but these just center the text within its box rather than the box within the plot. If I drop position_identity() in favour of nudge_x the bars all drop out, so that doesn’t get me there. Assuming there is some simple way to do this, but I’m not seeing it!

Here’s the function I’ve made to do the plot:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

plot_single_result_with_facets <- function(x) {
  x %>% 
    # 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) %>%
    # remove nas
    filter(!is.na(response)) %>%
    # add grouping by bins
    group_by(bin_name,b) %>%
    # calculate percentages
    mutate(perc=paste0(round(n*100/sum(n),0),"%")) %>% 
    # run ggplot
    ggplot(aes(x = n, y = "", fill = response, label = perc)) +
    # reversing order here using forcats::fct_rev() note - needs to be changed under geom_label_repel as well
    geom_col(position=position_fill(), aes(fill=forcats::fct_rev(response))) +
    coord_cartesian(clip = "off") +
    geom_vline(xintercept = x_limits, linetype = 3) +
    geom_label_repel(
      # important to make sure grouping of data matches grouping of labels so they aren't backwards
      # reversing order here using forcats::fct_rev() note - needs to be changed above as well
      aes(group = forcats::fct_rev(response), label = perc),
      # justify text using center = 0.5, left = 0 and right = 1
      hjust = 0.5,
      vjust = 0.5,
      direction = "y",
      force = 1.5,
      fill = "white", 
      # font size in the text labels
      size = 1.5,
      # allow labels to overlap
      max.overlaps = Inf,
      # make sure that bars are included
      position = position_fill(),
      # hide points
      segment.size = 0.2,
      point.size = NA,
      # reduce padding around each text label
      box.padding = 0.001
    ) +
    scale_fill_brewer(palette="YlOrBr", direction = -1) +
    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(title = title, caption = caption, x = "", y = "") + 
    guides(fill = guide_legend(title = NULL)) +
    theme_classic()
}

And the code that uses it:

caption <- NULL
df <- select(data, Q51_bin, Q52_bin, Q57_bin, Q53_bin, Q4)
df <- as_factor(df)
names(df) <- c("Q51_bin", "Q52_bin", "Q57_bin", "Q53_bin", "response")
facet_names <- c(`Q51_bin` = "Nature Relatedness", `Q52_bin` = "Spirituality", `Q57_bin` = "Religiosity", `Q53_bin` = "Politics L/R", `low`="low", `medium`="medium", `high`="high")
facet_labeller <- function(variable,value){return(facet_names[value])}
x_limits <- c(.50, NA)
facet_grid(~fct_relevel(df,'Nature Relatedness','Spirituality','Religiosity','Politics L/R'))
plot_single_result_with_facets(df)
ggsave("figures/q5_faceted.png", width = 20, height = 10, units = "cm")

Here’s the plot as it currently stands:

enter image description here

And a bit of data to make it reproducible:

df <- structure(list(Q51_bin = structure(c(2L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q52_bin = structure(c(3L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q57_bin = structure(c(2L, 2L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q53_bin = structure(c(2L, 3L, 2L, 2L), levels = c("low", "medium", "high"), class = "factor"), Q4 = structure(c(2, 3, 3, 5), label = "How much have you thought about climate change before today?", format.spss = "F40.0", display_width = 5L, labels = c(`Not at all` = 1, `A little` = 2, Some = 3, `A lot` = 4, `A great deal` = 5, `Don't know` = 99), class = c("haven_labelled", "vctrs_vctr", "double"))), class = c("rowwise_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -4L), groups = structure(list(.rows = structure(list(1L, 2L, 3L, 4L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame")))

>Solution :

You need to put vjust = 0.5 inside position_fill:

x %>% 
  pivot_longer(!response, names_to = "bin_name", values_to = "b") %>% 
  count(response, bin_name, b) %>%
  filter(!is.na(response)) %>%
  group_by(bin_name,b) %>%
  mutate(perc=paste0(round(n*100/sum(n),0),"%")) %>% 
  ggplot(aes(x = n, y = "", fill = response, label = perc)) +
  
  geom_col(position=position_fill(), aes(fill=forcats::fct_rev(response))) +
  coord_cartesian(clip = "off") +
  geom_vline(xintercept = x_limits, linetype = 3) +
  geom_label_repel(
    aes(group = forcats::fct_rev(response), label = perc),
    hjust = 0.5,
    vjust = 0.5,
    direction = "y",
    force = 1.5,
    fill = "white", 
    size = 1.5,
    max.overlaps = Inf,
    position = position_fill(vjust = 0.5),
    segment.size = 0.2,
    point.size = NA,
    box.padding = 0.001
  ) +
  scale_fill_brewer(palette="YlOrBr", direction = -1) +
  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(title = 'title', caption = caption, x = "", y = "") + 
  guides(fill = guide_legend(title = NULL)) +
  theme_classic()

enter image description here

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading