How to use stat="count" to label a bar chart with counts or percentages in ggplot2?
Asked Answered
W

2

8

I'm trying to produce a stacked column chart with data labels.

I'm able to produce the chart, but was unable to find a way to input data labels. I have tried geom_text() but it keeps asking me to input a y label (which if you see the ggplot code is not there). I have also tried adding geom_text(stat = "count") but that also gives me an error saying

"Error: geom_text requires the following missing aesthetics: y and label".

PS - i'm aware I need to rename the y axis as percentage. I'm also trying to figure out how to have more contrasting colours

ggplot(property,
       aes(x=Bedrooms.New, fill=Property.Type.)) + 
  geom_bar(position = "fill") + 
  scale_x_discrete(name = "Number of Bedrooms", 
                   limits = sort(factor(unique(property$Bedrooms.New))))

I have added an image below to see what my output is right now!

enter image description here

Worrisome answered 30/8, 2020 at 2:11 Comment(1)
What kind of data labels are you trying to produce? Category label? Number of observations? Proportion of observations?Attenuant
C
13

As the error message is telling you, geom_text requires the label aes. In your case you want to label the bars with a variable which is not part of your dataset but instead computed by stat="count", i.e. stat_count.

The computed variable can be accessed via ..NAME_OF_COMPUTED_VARIABLE... , e.g. to get the counts use ..count.. as variable name. BTW: A list of the computed variables can be found on the help package of the stat or geom, e.g. ?stat_count

UPDATE: The dot-dot notation was deprecated in ggplot2 3.4.0. Instead we could or should use after_stat, i.e. use e.g. after_stat(count) instead of ..count..

Using mtcars as an example dataset you can label a geom_bar like so:

library(ggplot2)

ggplot(mtcars, aes(cyl, fill = factor(gear))) +
  geom_bar(position = "fill") +
  geom_text(aes(label = after_stat(count)),
    stat = "count", position = "fill"
  )

Two more notes:

  1. To get the position of the labels right you have to set the position argument to match the one used in geom_bar, e.g. position="fill" in your case.

  2. While counts are pretty easy, labelling with percentages is a different issue. By default stat_count computes percentages by group, e.g. by the groups set via the fill aes. These can be accessed via after_stat(prop). If you want the percentages to be computed differently, you have to do it manually.

As an example if you want the percentages to sum to 100% per bar this could be achieved using after_stat and ave (to compute the percentage per group) like so:

library(ggplot2)

ggplot(mtcars, aes(cyl, fill = factor(gear))) +
  geom_bar(position = "fill") +
  geom_text(
    aes(label = after_stat(
      scales::percent(
        ave(count, x, FUN = function(x) x / sum(x))
      )
    )),
    stat = "count", position = "fill"
  )

Chevrette answered 30/8, 2020 at 9:40 Comment(2)
I've been looking for this comment all day! a way to calculate percentages without modifying the dataframe! THANK YOU!Raising
This answer (which I love because it worked for me too!) illustrates exactly why I was NEVER going to be able to figure out on my own how to simply add percents to my stacked barchart without doing the dplyr summarising work myself before the ggplot code.Capacious
L
0

Adding a follow-up to the answer above, since this answer usually gets me 90% of the way, but I can never remember how to also:

  • center the labels (position_fill(vjust = 0.5))
  • make the percent labels pretty (scales::percent())
library(ggplot)

ggplot(mtcars, aes(cyl, fill = factor(gear)))+
  geom_bar(position = "fill") +
  geom_text(aes(label = scales::percent(..count.. / tapply(..count.., ..x.., sum)[as.character(..x..)])), stat = "count", position = position_fill(vjust = 0.5))

enter image description here

And here's an alternative with pre-calculations in advance:

mtcars %>% 
  count(gear, cyl) %>% 
  group_by(cyl) %>% 
  mutate(perc = n / sum(n)) %>% 
  ggplot(aes(cyl, perc, fill = factor(gear)))+
  geom_col(position = "fill") +
  geom_text(aes(label = scales::percent(perc)), position = position_fill(vjust = 0.5))
Leigh answered 31/1, 2023 at 7:48 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.