Search code examples
rggplot2geom-text

R ggplot labels on stacked bar chart


I have data that I need to put into a stack bar chart but when I add the labels of the counts some of the labels are above the category and some are under the category. I tried modifying position arguments of the geom_text function to no avail.

Below is a reproducible example showing the labels for the 'Under' category seating above that category and the labels of the 'Over' category seating inside the bar.

library(tidyverse)

data.frame(AgeGroup = sample(c(rep("Over",10),"Under"), 6000, replace = TRUE),
DueDate = sample( 
         seq( as.Date("2015-01-01"), 
              as.Date("2015-06-30"), by="1 month") ,  
         6000,replace = TRUE),
             stringsAsFactors = TRUE) %>%
group_by(AgeGroup,DueDate) %>%
  tally() %>% ungroup %>% 
  ggplot() +
  geom_bar(aes(x=DueDate, y=n, fill = AgeGroup),stat = "identity") +
  geom_text(aes(x=DueDate, y=n
            ,label = prettyNum(n,big.mark = ","))
        , vjust = 0,  size = 2) +
  scale_y_continuous(labels = scales::comma) +
  theme_bw() +
  labs(title="Where are the labels")

Below is the output chart. enter image description here


Solution

  • Just use n/2 as the y position for geom_text(), and it will always fall "inside" the bar:

    library(tidyverse)
    
    data.frame(AgeGroup = sample(c(rep("Over",10),"Under"), 6000, replace = TRUE),
               DueDate = sample( 
                   seq( as.Date("2015-01-01"), 
                        as.Date("2015-06-30"), by="1 month") ,  
                   6000,replace = TRUE),
               stringsAsFactors = TRUE) %>%
        group_by(AgeGroup,DueDate) %>%
        tally() %>% ungroup %>% 
        ggplot() +
        geom_bar(aes(x=DueDate, y=n, fill = AgeGroup),stat = "identity") +
        geom_text(aes(x=DueDate, y=n/2
                      ,label = prettyNum(n,big.mark = ","))
                  , vjust = 0,  size = 2) +
        scale_y_continuous(labels = scales::comma) +
        theme_bw() +
        labs(title="Where are the labels")
    

    enter image description here

    EDIT: That quick solution will only work for your specific example. If you have more than two categories per bar, or if the values are more evenly distributed, it will not fly. i.e.:

    set.seed(999)
    data.frame(Direction = sample(rep(c("South", "West", "East", "North")), 6000, replace = TRUE),
               DueDate = sample( 
                   seq( as.Date("2015-01-01"), 
                        as.Date("2015-06-30"), by="1 month") ,  
                   6000,replace = TRUE),
               stringsAsFactors = TRUE) %>%
        group_by(Direction, DueDate) %>%
        tally() %>% 
        ungroup %>%
        arrange(desc(Direction)) %>% 
        group_by(DueDate) %>% 
        mutate(pos = cumsum(n) - n/2) %>% 
        ggplot() +
        geom_bar(aes(x=DueDate, y=n, fill = Direction),stat = "identity") +
        geom_text(aes(x=DueDate, y=pos, label = prettyNum(n,big.mark = ","))
                  , vjust = 0,  size = 2) +
        scale_y_continuous(labels = scales::comma) +
        theme_bw() +
        labs(title="Where are the labels")
    

    enter image description here

    So here's a general solution, that adds a "position" column to the dataframe (arrange(desc(Direction)) %>% group_by(DueDate) %>% mutate(pos = cumsum(n) - n/2)), to use with geom_text() and place the labels exactly where they belong:

    set.seed(999)
    data.frame(Direction = sample(rep(c("South", "West", "East", "North")), 6000, replace = TRUE),
               DueDate = sample( 
                   seq( as.Date("2015-01-01"), 
                        as.Date("2015-06-30"), by="1 month") ,  
                   6000,replace = TRUE),
               stringsAsFactors = TRUE) %>%
        group_by(Direction, DueDate) %>%
        tally() %>% 
        ungroup %>%
        arrange(desc(Direction)) %>% 
        group_by(DueDate) %>% 
        mutate(pos = cumsum(n) - n/2) %>% 
        ggplot() +
        geom_bar(aes(x=DueDate, y=n, fill = Direction),stat = "identity") +
        geom_text(aes(x=DueDate, y=pos, label = prettyNum(n,big.mark = ","))
                  , vjust = 0,  size = 2) +
        scale_y_continuous(labels = scales::comma) +
        theme_bw() +
        labs(title="Where are the labels")
    

    enter image description here