Adding percentage labels to a bar chart in ggplot2

It’s easiest to calculate the quantities you need beforehand, outside of ggplot, as it’s hard to track what ggplot calculates and where those quantities are stored and available.

First, summarize your data:

library(dplyr)
library(ggplot2)

mtcars %>% 
    count(cyl = factor(cyl), gear = factor(gear)) %>% 
    mutate(pct = prop.table(n))
#> # A tibble: 8 x 4
#>   cyl   gear      n    pct
#>   <fct> <fct> <int>  <dbl>
#> 1 4     3         1 0.0312
#> 2 4     4         8 0.25  
#> 3 4     5         2 0.0625
#> 4 6     3         2 0.0625
#> 5 6     4         4 0.125 
#> 6 6     5         1 0.0312
#> 7 8     3        12 0.375 
#> 8 8     5         2 0.0625

Save that if you like, or pipe directly into ggplot:

mtcars %>% 
    count(cyl = factor(cyl), gear = factor(gear)) %>% 
    mutate(pct = prop.table(n)) %>% 
    ggplot(aes(x = cyl, y = pct, fill = gear, label = scales::percent(pct))) + 
    geom_col(position = 'dodge') + 
    geom_text(position = position_dodge(width = .9),    # move to center of bars
              vjust = -0.5,    # nudge above top of bar
              size = 3) + 
    scale_y_continuous(labels = scales::percent)

If you really want to keep it all internal to ggplot, you can use geom_text with stat="count" (or stat_count with geom = "text", if you prefer):

ggplot(data = mtcars, aes(x = factor(cyl), 
                          y = prop.table(stat(count)), 
                          fill = factor(gear), 
                          label = scales::percent(prop.table(stat(count))))) +
    geom_bar(position = "dodge") + 
    geom_text(stat="count",
              position = position_dodge(.9), 
              vjust = -0.5, 
              size = 3) + 
    scale_y_continuous(labels = scales::percent) + 
    labs(x = 'cyl', y = 'pct', fill="gear")

which plots exactly the same thing.

Leave a Comment