0
votes

I am trying to add percentage labels to a stacked AND faceted barplot (position='fill'). I want the percentages displayed to add up for each bar.

I'm using a data set like this:

## recreate dataset
Village<-c(rep('Vil1',10),rep('Vil2',10))
livestock<-c('p','p','p','c','c','s','s','s','g','g',
             'p','p','c','c','s','s','s','s','g','g')
dose<-c(3,2,1,2,1,3,2,1,2,1,
        2,1,2,1,4,3,2,1,2,1)
Freq<-c(4,5,5,2,3,4,1,1,6,8,
      1,3,2,2,1,1,3,2,1,1)
df<-data.frame(Village,livestock,dose,Freq)

I sucessfully plotted it and added labels that add up to 100% for each X variable (livestock):

## create dose categories (factors)
df$dose<-as.character(df$dose)
df$dose[as.numeric(df$dose)>3]<-'>3'
df$dose<-factor(df$dose,levels=c('1','2','3','>3'))
## percentage barplot
ggplot(data = df, aes(x=livestock, y=Freq, fill=dose)) +
  geom_bar(position='fill', stat='identity') +
  labs(title="Given doses of different drugs in last 6months (livestock)", 
       subtitle='n=89',x="Livestock",y="Percentage",
       fill = "Nr. of\ndoses") +
  theme(axis.text.x = element_text(angle = 45, hjust=1))+ 
  scale_y_continuous(labels=percent)+
  facet_wrap(~Village)+
  geom_text(aes(label = percent(..y../tapply(..y..,..x..,sum)[..x..])),
            stat = "identity",position = position_fill(vjust=0.5))

barplot 100% for each Xvariable

Does anyone know how I can change the label code within ggplot so the percentages add up to 100% for each bar? Maybe something to do with ..group..?

I tried something similar to this: Label percentage in faceted filled barplot in ggplot2 put I can't make it work for my data.

2

2 Answers

0
votes

The easiest way would be to transform your data beforehand so that the fractions can be used directly.

library(tidyverse)
library(scales)

# Assume df is as in example code
df <- df %>% group_by(Village, livestock) %>%
  mutate(frac = Freq / sum(Freq))

ggplot(df, aes(livestock, frac, fill = dose)) +
  geom_col() +
  geom_text(
    aes(label = percent(frac)),
    position = position_fill(0.5)
  ) +
  facet_wrap(~ Village)

If you insist on not pre-transforming the data, you can write yourself a little helper function.

bygroup <- function(x, group, fun = sum, ...) {
  splitted <- split(x, group)
  funned   <- lapply(splitted, fun, ...)
  funned   <- mapply(function(x, y) {
    rep(x, length(y))
  }, x = funned, y = splitted)
  unsplit(funned, group)
}

Which you can then use by setting the group to x and the (undocumented) PANEL column.

library(ggplot2)
library(scales)

# Assume df is as in example code
ggplot(df, aes(livestock, Freq, fill = dose)) +
  geom_col(position = "fill") +
  geom_text(
    aes(
      label = percent(after_stat(y / bygroup(y, interaction(x, PANEL))))
    ),
    position = position_fill(0.5)
  ) +
  facet_wrap(~ Village)

0
votes

Just to add to the solution of @teunbrand: I calculated the fractions as @teunbrand suggested and it worked perfectly. However, I started to get very weird and persistent warning messages:

Warning messages:
1: Unknown or uninitialised column: `times`. 
2: Unknown or uninitialised column: `times`. 
3: Unknown or uninitialised column: `times`. 
4: Unknown or uninitialised column: `times`. 
5: Unknown or uninitialised column: `Var1`. 

I read up on this problem here which seems to be a known bug: Persistent "Unknown or uninitialised column" warnings

I could get rid of the warnings by ungrouping and reconverting the tibble into a dataframe.

df <- as.data.frame(df %>% group_by(Village, livestock) %>%
  mutate(frac = Freq / sum(Freq)) %>% ungroup())