rggplot2geom-textstacked-bar-chart

Set labels on top of horizontal stacked barplot in ggplot2


I have a dummy dataset:

tmp <- structure(list(order = c("John", "John", "John", "Ross", "Ross", 
"Ross", "John", "John", "John", "Ross", "Ross", "Ross"), value = c(39, 
40, 13, 19, 13, 15, -12, -3, 0, -4, -1, 0), Category = c("VU", 
"EN", "CR", "VU", "EN", "CR", "VU", "EN", "CR", "VU", "EN", "CR"
), perc = c(NA, NA, NA, NA, NA, NA, NA, "14%", NA, NA, "9.6%", 
NA)), row.names = c(1L, 2L, 3L, 4L, 5L, 6L, 25L, 26L, 27L, 28L, 
29L, 30L), class = "data.frame")

I would like to display the % on the left of the plot (negative values) on the top of each bar. This is my code:

ggplot(tmp, 
             aes(fill = Category, y = value, x = reorder(order, value))) + 
  
  # add light grey vertical lines in correspondance of x axis thicks
  geom_hline(yintercept = c(seq(-20, 0, 5), seq(0, 100, 20)), colour = "grey92") + 
  
  # add stacked bars with grey contouring
  geom_bar(position = "stack", stat = "identity", alpha = .8, color = "grey50") + 
  
  # add zero vertical line
  geom_hline(yintercept = 0, colour = "grey50", linewidth = 1.5, linetype = "dashed") +  
  
  # add % for negative values
  geom_text(aes(label = perc), 
            hjust = 2,
            size = 10) + 
  
  # add y axis breaks and limits
  scale_y_continuous(breaks = c(seq(-20, 0, 10), seq(0, 100, 20)), 
                     limits = c(-36, 100),
                     expand = c(0, 0)) + 
  
  # add discrete colour scale
  scale_fill_manual(values = c("#E55C30FF","#FBB91FFF","#FCFFA4FF")) +
  
  # flip horizontally
  coord_flip() 

And this is the resulting plot: enter image description here

As some bars are longer than others, if I play around with hjust I can just move all the labels at the same time. Is there a way to have all the labels displayed on top (in this case, on the side, as it's a horizontal barplot) of the bars?


Solution

  • You can achieve your desired result by using position=position_stack() for the labels too, setting vjust=0 to place the labels inside the bars and finally use hjust = 1. Also note that I switched to geom_label as it allows to add some padding around the label, which however requires setting fill=NA and label.size=0 to get rid of the box around the label. Additionally we have to map on the group aes.

    library(ggplot2)
    
    ggplot(
      tmp,
      aes(fill = Category, y = value, x = reorder(order, value))
    ) +
      # add light grey vertical lines in correspondance of x axis thicks
      geom_vline(xintercept = c(seq(-20, 0, 5), seq(0, 100, 20)), colour = "grey92") +
      # add stacked bars with grey contouring
      geom_col(alpha = .8, color = "grey50") +
      # add zero vertical line
      geom_hline(yintercept = 0, colour = "grey50", linewidth = 1.5, linetype = "dashed") +
      # add % for negative values
      geom_label(
        aes(label = perc, group = Category),
        hjust = 1,
        size = 6,
        position = position_stack(vjust = 0),
        fill = NA,
        label.size = 0
      ) +
      # add y axis breaks and limits
      scale_y_continuous(
        breaks = c(seq(-20, 0, 10), seq(0, 100, 20)),
        limits = c(-36, 100),
        expand = c(0, 0)
      ) +
      # add discrete colour scale
      scale_fill_manual(values = c("#E55C30FF", "#FBB91FFF", "#FCFFA4FF")) +
      # flip horizontally
      coord_flip()
    #> Warning: Removed 10 rows containing missing values or values outside the scale range
    #> (`geom_label()`).