Search code examples
rggplot2centeringtext-alignmentgeom-text

Center alignment of text over each bar on faceted, dodged bar plot using ggplot2 in R


I would like my text to be center-aligned above each bar in a faceted dodged bar plot.

# setup
library(ggplot2); library(tidyverse)
data("iris")

# graph
iris %>% group_by(Species) %>% 
mutate(Petal_Group = ifelse(Petal.Width < median(Petal.Width), "Short", "Long")) %>% 
mutate(Sepal_Group = ifelse(Sepal.Width < median(Sepal.Width), "Short", "Long")) %>% 
group_by(Petal_Group, Sepal_Group, Species) %>% 
summarise(mean.Sepal.Length = mean(Sepal.Length)) %>% 
mutate(asterisks = "***") %>% 
ggplot(aes(x = Petal_Group, y = mean.Sepal.Length)) + 
    geom_col(aes(fill = Species), position = "dodge") + 
    geom_text(aes(label=asterisks, group = Species), position = position_dodge(width = 1)) + 
    facet_grid(~Sepal_Group, labeller = label_parsed) + 
    theme(legend.position = "bottom", panel.background = element_blank(), panel.border = element_rect(colour = "black", fill = NA, size = 0.2), strip.background = element_blank())

Example of a faceted dodged bar plot that needs text center-alignment

In the example (sorry if it's overcomplicated but I'm working with something like this), the triple asterisks ("***") are not centered over each bar. I.e., the asterisks over red bars appear on the left and the asterisks over the blue bars appear on the right.

I tried adjusting the hjust in geom_text but I couldn't get it right. (And isn't there a more universal way of center alignment, something like position = "center"?)


Solution

  • The default dodge width for geom_bar and geom_col is 0.9 I think, not 1, so your text is being dodged by a different amount to your bars. If you create a dodge object that you can apply to both the columns and the text they will align nicely:

    # graph
    dodger = position_dodge(width = 0.9)
    iris %>% group_by(Species) %>% 
        mutate(Petal_Group = ifelse(Petal.Width < median(Petal.Width), "Short", "Long")) %>% 
        mutate(Sepal_Group = ifelse(Sepal.Width < median(Sepal.Width), "Short", "Long")) %>% 
        group_by(Petal_Group, Sepal_Group, Species) %>% 
        summarise(mean.Sepal.Length = mean(Sepal.Length)) %>% 
        mutate(asterisks = "***") %>% 
        ggplot(aes(x = Petal_Group, y = mean.Sepal.Length)) + 
        geom_col(aes(fill = Species), position = dodger) + 
        geom_text(aes(label=asterisks, group = Species), position = dodger) + 
        facet_grid(~Sepal_Group, labeller = label_parsed) + 
        theme(legend.position = "bottom", panel.background = element_blank(), 
              panel.border = element_rect(colour = "black", fill = NA, 
              size = 0.2), strip.background = element_blank())
    

    Result:

    enter image description here