Search code examples
rggplot2geom-textstacked-bar-chart

How to align geom_text() in a facetted and flipped stacked bar chart


Here is my fake data:

dat1 <- structure(list(A_B = c("A", "B", "A", "B", "A", "B", "A", "B", 
"A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", 
"B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", 
"A", "B", "A", "B", "A", "B"), percentage = c(49.2629769158808, 
50.7370230841193, 64.4899165250794, 35.5100834749206, 59.3111128788481, 
40.6888871211519, 85.3899516501997, 14.6100483498003, 80.5933773112794, 
19.4066226887206, 84.3750256827502, 15.6249743172498, 62.8269574848769, 
37.1730425151231, 69.5582018265724, 30.4417981734276, 56.5840999142665, 
43.4159000857335, 100, 0, 67.4302746354917, 32.5697253645083, 
27.3784794349813, 72.6215205650187, 58.0885854897603, 41.9114145102397, 
100, 0, 89.005829015544, 10.994170984456, 57.1694599627561, 42.8305400372439, 
100, 0, 54.2251244585246, 45.7748755414754, 74.4838466287125, 
25.5161533712875, 74.6413724456935, 25.3586275543065), facet = c("Facet 1", 
"Facet 1", "Facet 1", "Facet 1", "Facet 1", "Facet 1", "Facet 1", 
"Facet 1", "Facet 1", "Facet 1", "Facet 2", "Facet 2", "Facet 2", 
"Facet 2", "Facet 2", "Facet 2", "Facet 2", "Facet 2", "Facet 2", 
"Facet 2", "Facet 2", "Facet 2", "Facet 1", "Facet 1", "Facet 2", 
"Facet 2", "Facet 2", "Facet 2", "Facet 1", "Facet 1", "Facet 1", 
"Facet 1", "Facet 2", "Facet 2", "Facet 1", "Facet 1", "Facet 1", 
"Facet 1", "Facet 1", "Facet 1"), id = c(1L, 1L, 2L, 2L, 3L, 
3L, 4L, 4L, 5L, 5L, 6L, 6L, 7L, 7L, 8L, 8L, 9L, 9L, 10L, 10L, 
11L, 11L, 12L, 12L, 13L, 13L, 14L, 14L, 15L, 15L, 16L, 16L, 17L, 
17L, 18L, 18L, 19L, 19L, 20L, 20L)), row.names = c(NA, -40L), class = c("tbl_df", 
"tbl", "data.frame"))

Here is the chart:

library(ggplot2)

ggplot(dat1, aes(id, percentage, fill = factor(A_B))) + 
  geom_col(position = position_fill()) +
  scale_fill_manual(values = c("coral", "lightblue"),
                    breaks=c('A', 'B'))+
  coord_flip() +
  facet_wrap(. ~ facet, scale= "free")+
  geom_text(data = subset(dat1, percentage != 0), aes(label = sprintf("%.1f%%", percentage), group = A_B), position = position_fill(vjust=0.5), size = 5) 

I get this: enter image description here

When attempting to vertically align text within a single line for both A and B by adjusting the vjust argument in position_fill(), I encounter difficulties achieving a uniform alignment. Using vjust=1 brings me closest to the desired outcome; however, this results in the text exceeding the boundaries. Using any other number in vjust results in a skewed formation of the percent numbers.


Solution

  • Using the vjust parameter you can only position both labels at the bottom, top or center. Instead, for what you are trying to achieve I think you are better off using position="idenity" and setting the y position and the horizontal alignment using an ifelse. Also note that I switched to geom_label to add some padding (I'm not a big fan of using h/vjust for this task (: ).

    library(ggplot2)
    
    ggplot(dat1, aes(id, percentage, fill = factor(A_B))) +
      geom_col(position = position_fill()) +
      scale_fill_manual(
        values = c("coral", "lightblue"),
        breaks = c("A", "B")
      ) +
      coord_flip() +
      facet_wrap(. ~ facet, scale = "free") +
      geom_label(
        data = subset(dat1, percentage != 0),
        aes(
          y = ifelse(A_B == "A", 1, 0),
          hjust = ifelse(A_B == "A", 1, 0),
          label = sprintf("%.1f%%", percentage)
        ), size = 3, fill = NA, label.size = 0
      )
    

    enter image description here