Search code examples
rggplot2data-visualizationbar-chartaxis-labels

Individual labelling for each group in a clustered stacked bar graph in R


Using help from this answer: Grouped stacked bar graph: issues regarding order of stack, horizontal orientation of graph and label of groups

test  <- data.frame(person=c("Q8.3. Patients presenting to me, who are suitable \n for referral to social prescribing services, \n have decreased.\n GP (n = 72) \n LW (n = 101)",
                             "Q8.5. Patients feel well-supported during COVID-19 \n for their emotional and social needs.\n GP (n = 70) \n LW (n = 125)",
                             "Q9.1. Less patients are interested in being \n referred to social prescribing services.\n GP (n = 55) \n LW (n = 100)",
                             "Q9.4. Online services are available to replace community \n groups and statutory services that are \n not available. \n GP (n = 60) \n LW (n = 133)",
                             "Q9.6. Link workers have great influence on the services \n people are referred to.\n GP (n = 58) \n LW (n = 131)"), 
                    value1=c(22.22, 8.57, 23.63, 31.66, 79.31),  # GP Agree   
                    value2=c(54.16, 77.14, 58.18, 40, 13.79) ,    # GP Disagree
                    value3=c(23.61, 14.28, 18.18, 28.33, 6.89),     # GP Neutral
                    value4=c(19.8, 48, 9, 51.87, 69.46),  # LW Agree
                    value5=c(64.35,34.4,70,28.57,14.5),     # LW Disagree
                    value6=c(15.84,17.6,21,19.54,16.03))     # LW Neutral

library(reshape2) # for melt

melted <- melt(test, "person")

melted$cat <- ''
melted[melted$variable == 'value1' | melted$variable == 'value2' | melted$variable == 'value3',]$cat <- "GP"
melted[melted$variable == 'value4' | melted$variable == 'value5'| melted$variable == 'value6',]$cat <- "LW"
melted$Response <- ''
melted[melted$variable %in% sprintf("value%i",c(1,4)),]$Response <- "Agree"
melted[melted$variable %in% sprintf("value%i",c(2,5)),]$Response <- "Disagree"
melted[melted$variable %in% sprintf("value%i",c(3,6)),]$Response <- "Neutral"


melted$Response <- factor(melted$Response, c("Agree", "Neutral","Disagree"))

p = ggplot(melted, aes(x = cat, y = value, fill = Response))
p + geom_bar(stat = 'identity', position = 'stack') +
  geom_text(aes(label = paste0(value, "%")), position = position_stack(vjust = .5)) +
  ylab("Percentage of responses")+
  xlab("Population")+
  facet_grid(person~.) + 
  scale_fill_manual(values = c("#488f31","#ffeb8a","#de425b")) + 
  theme(panel.background = element_rect(fill = 'white'),
        strip.text.y = element_text(angle = 0)) +
  coord_flip()

I was able to generate the following graph: enter image description here

My question is: how do I get the labels for the groups (GP, LW) to show the sample size next to them? I want to know where in the code can I add such an array that would label the chart like that.

For example, the second bar would have GP (n = 72) and the first bar would have LW (n = 101)written on right, instead of just GP and LW.

Thank you.


Solution

  • This could be achieved like so:

    1. Extract the category labels including the sample sizes from you person variable using e.g. string::str_extract and assign it to cat.
    2. Add scales = "free_y" to facet_grid.

    EDIT The sample sizes could be remove from the question using e.g. gsub:

    
    library(reshape2) # for melt
    library(ggplot2)
    library(stringr)
    
    melted$cat <- ifelse(melted$cat == "GP", 
                         stringr::str_extract(melted$person, "GP \\(n = \\d+\\)"), 
                         stringr::str_extract(melted$person, "LW \\(n = \\d+\\)"))
    
    melted$person <- gsub("(GP|LW) \\(n = \\d+\\)", "", melted$person)
    
    p = ggplot(melted, aes(x = cat, y = value, fill = Response))
    p + geom_bar(stat = 'identity', position = 'stack') +
      geom_text(aes(label = paste0(value, "%")), position = position_stack(vjust = .5)) +
      ylab("Percentage of responses")+
      xlab("Population")+
      facet_grid(person~., scales = "free_y") + 
      scale_fill_manual(values = c("#488f31","#ffeb8a","#de425b")) + 
      theme(panel.background = element_rect(fill = 'white'),
            strip.text.y = element_text(angle = 0)) +
      coord_flip()