Display totals and means next to chart in ggplot

Viewed 65

enter image description here

I am trying to get my mean and totals to the left of my bar chart like in the example above. I have my bar charts created in ggplot. Picture and code below. Any advice on how to get the means and totals to display in the right place? Possibly custom_annotations?

Thanks enter image description here

  percentData = stotal %>% #stotal = survey data frame
    group_by(qtext, div, response) %>%  #qtext is my question text
    summarise(N = n()) %>% 
    mutate(prop = N/sum(N))
  percentData$prop = label_percent(accuracy = 1)(percentData$prop) #make percent from decimal
  percentData
  
  
  #colors
  myColors <- c("green4","springgreen2","yellow1","orange1","red1","black", "black", "black")
  
  ggplot(stotal)+
    geom_bar(aes(x = div, fill = response), position = 'fill', width = 0.5)+
    facet_grid(rows = vars(qtext))+
    scale_fill_manual (values = myColors)+
    coord_flip()+
    ylab('')+
    xlab('')+
    scale_y_continuous(labels = percent)+
    ggtitle(i)+
    geom_text(data = percentData, aes(fill = response, y = N, label = prop, x = div),
              position=position_fill(vjust=0.5))+
    theme(strip.text.y = element_text(size = 12, angle = 0, family = "serif"))

  
1 Answers

One approach to achieve this is by making the table via a second ggplot which can be glued to the main plot by e.g. patchwork. Basically the table plot replicates the main plot with only one category, uses facetting to get the colum layout with the mean and the totals and gets rids of axis, grid, background colors, ...

Using some random example data try this:

library(ggplot2)
library(dplyr)
library(scales)
library(patchwork)

# Random example data
set.seed(42)

stotal <- data.frame(
  qtext = rep(c("A", "B"), 50),
  div = sample(c("University", "KSAS-HUM"), 100, replace = TRUE),
  response = sample(c("Poor", "Fair", "Good", "Very good", "Excellent"), 100, replace = TRUE)
)
stotal$response <- factor(stotal$response, levels = c("Poor", "Fair", "Good", "Very good", "Excellent"))

percentData = stotal %>% #stotal = survey data frame
  group_by(qtext, div, response) %>%  #qtext is my question text
  summarise(N = n()) %>% 
  mutate(prop = N/sum(N))
#> `summarise()` regrouping output by 'qtext', 'div' (override with `.groups` argument)
percentData$prop = label_percent(accuracy = 1)(percentData$prop) #make percent from decimal

#colors
myColors <- c("green4","springgreen2","yellow1","orange1","red1","black", "black", "black")

p1 <- ggplot(stotal)+
  geom_bar(aes(x = div, fill = response), position = 'fill', width = 0.5)+
  facet_grid(rows = vars(qtext))+
  scale_fill_manual (values = myColors)+
  coord_flip()+
  ylab('')+
  xlab('')+
  scale_y_continuous(labels = percent)+
  #ggtitle(i)+
  geom_text(data = percentData, aes(fill = response, y = N, label = prop, x = div),
            position=position_fill(vjust=0.5))+
  theme(strip.text.y = element_text(size = 12, angle = 0, family = "serif"))
#> Warning: Ignoring unknown aesthetics: fill


# Table plot

table_data <- stotal %>% 
  mutate(response = as.numeric(response)) %>% 
  group_by(qtext, div) %>%
  summarise(Mean = mean(response), "Total N" = n()) %>% 
  mutate(Mean = round(Mean, 1)) %>% 
  tidyr::pivot_longer(-c(qtext, div), names_to = "var")
#> `summarise()` regrouping output by 'qtext' (override with `.groups` argument)

p2 <- ggplot(table_data, aes(x = div)) +
  geom_bar(color = "white", fill = "white", position = 'fill', width = .5)+
  #geom_vline(color = "grey", xintercept = c(.5, 1.5, 2.5)) +
  geom_text(aes(y = 1, label = value), position=position_fill(vjust=0.5), size = 0.8 * 11 /.pt) + 
  facet_grid(qtext ~ var, switch = "y") +
  coord_flip() +
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(strip.text.y = element_blank()) +
  theme(axis.ticks.y = element_blank(), 
        axis.text.y = element_blank(),
        axis.text.x = element_text(color = "transparent"), 
        axis.ticks.x = element_line(color = "transparent"),
        axis.title = element_blank(),
        panel.grid = element_blank(), panel.spacing.x = unit(0, "pt"))
            

# Glue together

p2 + p1 + plot_layout(widths = c(1, 3))

Related