0

How can I add percentage labels to this stacked barplot, with each component of the stacked bars labeled with it's corresponding percentage?

ggplot(mtcars, aes(cyl, fill = factor(gear))) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) 

Edit: Was able to add counts, but still am unable to convert this to percent

ggplot(mtcars, aes(cyl, fill = factor(gear))) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent)+
  geom_text(aes(label=stat(count)), stat='count', position='fill')
user6883405
  • 393
  • 3
  • 14
  • 1
    The provided code has percent labels on the y axis for me. – TTS Mar 02 '20 at 17:27
  • edited for clarification: How can I add percentage labels to this stacked barplot, with each component of the stacked bars labeled with it's corresponding percentage? – user6883405 Mar 02 '20 at 17:30
  • https://stackoverflow.com/questions/44724580/add-percentage-labels-to-a-stacked-barplot?rq=1 & https://stackoverflow.com/questions/34903368/how-to-center-stacked-percent-barchart-labels?noredirect=1&lq=1 – Tung Mar 02 '20 at 17:42
  • See also this https://github.com/wilkox/ggfittext – Tung Mar 02 '20 at 17:43
  • 1
    [Don't use `$` inside `aes`](https://stackoverflow.com/questions/32543340/issue-when-passing-variable-with-dollar-sign-notation-to-aes-in-combinatio) – camille Mar 02 '20 at 18:09

1 Answers1

3

I would say the easiest way is to do some data preparation, to get the proportions / percentages:

library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
data(mtcars)
dat <- as.data.frame(prop.table(table(mtcars$cyl, mtcars$gear), margin = 1))
colnames(dat) <- c("cyl", "gear", "percent")

dat <- dat %>% 
  group_by(cyl) %>% 
  mutate(cyl_label_y = 1 - (cumsum(percent) - 0.5 * percent)) %>% 
  ungroup()

ggplot(dat, aes(cyl, y = percent, fill = factor(gear))) +
  geom_bar(position = "fill", stat = "identity") +
  scale_y_continuous(labels = scales::percent) +
  geom_text(aes(y = cyl_label_y, label = round(100 * percent, 2)))

An even simpler way is to use the sjPlot-package:

sjPlot::plot_xtab(mtcars$cyl, mtcars$gear, bar.pos = "stack", margin = "row")

Created on 2020-03-02 by the reprex package (v0.3.0)

Daniel
  • 7,252
  • 6
  • 26
  • 38
  • Is there anyway to avoid using data preparations/creating a new df? The actual use case requires it to be more generalized. Also, unfortunately I can't access the sjPlot due to my company's firewall. Only common packages are approved. – user6883405 Mar 02 '20 at 19:05
  • I'm not aware of any solution that avoids calculating the proportions, because at least you need to know the correct positions of the labels. But I haven't followed ggplot2-development in detail the past months, maybe it's possible. – Daniel Mar 03 '20 at 06:50
  • Ok, there _is_ a solution w/o modifying the data: https://rstudio-pubs-static.s3.amazonaws.com/329677_8f579b9e46284caeb9d3a72b7fdb7ac3.html – Daniel Mar 03 '20 at 06:52