rdataframeggplot2

Add totals in likert plot in ggplot2 in R


I have this data frame in R called df :

df
# A tibble: 5 × 7
  var       n `Very \n Dissatisfied` Dissatisfied Neutral Satisfied `Very \n Satisfied`
  <chr> <int>                  <dbl>        <dbl>   <dbl>     <dbl>               <dbl>
1 A       106                   18.9         14.5    23.0      22.0                21.7
2 B       106                   19.2         16.0    25.5      18.9                20.4
3 C        87                   22.2         25.3    15.7      17.2                19.5
4 D       102                   19.0         19.0    21.2      22.9                18.0
5 E        99                   22.2         20.5    20.9      17.5                18.9



from this data frame I have create two plots, one likert plot and one bar plot :


df_long <- df %>%
  select(-n)%>%
  pivot_longer(!var, names_to = "Likert", values_to = "Percentage")
# Likert plot
likert_plot <- ggplot(df_long, aes(x = var, y = Percentage, fill = Likert)) +
  geom_col(position = position_likert(reverse = FALSE)) +
  geom_text(
    aes(
      label = label_percent_abs(hide_below = .01, accuracy = 1)(Percentage),
      color = after_scale(hex_bw(.data$fill))
    ),
    position = position_likert(vjust = 0.5, reverse = FALSE),
    size = 3.5
  ) +
  scale_y_continuous(labels = scales::percent) +
  labs(
    title = "Likert Responses by Category",
    x = "Category",
    y = "Percentage",
    fill = "Likert Scale"
  ) +
  coord_flip()+
  theme_minimal()+scale_fill_manual(values = custom_colors) +
  labs(x = NULL, y = NULL, fill = NULL)

# Horizontal bar plot
bar_plot <- ggplot(df, aes(x = n, y = var)) +
  geom_bar(stat = "identity", fill = "lightgrey") +
  geom_label(
    aes(
      label = label_number_abs(hide_below = .05, accuracy = 2)(n)
    ),
    size = 3.5,
    position = position_stack(vjust = 0.5), 
    hjust = 1,
    fill = NA,
    label.size = 0,
    color = "black"
  ) +
  scale_y_discrete(labels = \(x) gsub("\\..*$", "", x)) +
  scale_x_continuous(
    labels = label_percent_abs(),
    expand = c(0, .15)
  ) +
  theme_light() +
  theme(
    legend.position = "bottom",
    panel.grid.major.y = element_blank(),
    axis.text.x = element_blank()  # Hides x-axis numbers
  ) +
  labs(x = NULL, y = NULL, fill = NULL)

# Print plots

(likert_plot) +(bar_plot)+
  plot_layout(
   width = c(4,1)
  ) &
  theme(legend.position = "bottom")

that look like this :

enter image description here

I want :

  1. the percentages inside each bar level to round them to 0 decimal places
  2. make them bold
  3. add the totals at right and left .(ie totals at left is the sum of very dissatisfied and satisfied and the totals at right the sum of satisfied and ver satisfied)

How can I achieve these in R ?

Data

dput(df)
structure(list(var = c("A", "B", "C", "D", "E"), n = c(106L, 
106L, 87L, 102L, 99L), `Very 
 Dissatisfied` = c(18.8679245283019, 
19.1823899371069, 22.2222222222222, 18.9542483660131, 22.2222222222222
), Dissatisfied = c(14.4654088050314, 16.0377358490566, 25.2873563218391, 
18.9542483660131, 20.5387205387205), Neutral = c(22.9559748427673, 
25.4716981132075, 15.7088122605364, 21.2418300653595, 20.8754208754209
), Satisfied = c(22.0125786163522, 18.8679245283019, 17.2413793103448, 
22.8758169934641, 17.5084175084175), `Very 
 Satisfied` = c(21.6981132075472, 
20.440251572327, 19.5402298850575, 17.9738562091503, 18.8552188552189
)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"
))


Solution

  • To achieve your desired result set the scale= parameter in label_percent_abs to 1 to avoid that the proportions are multiplied by the default 100. To have bold labels use fontface="bold" in geom_text. Finally, to get the labels you can use a separate dataframe where you compute the totals (similar to what I have done in answers on your former questions), then use a second geom_text/label:

    df <- data.frame(
      var = c("A", "B", "C", "D", "E"),
      n = c(106, 106, 87, 102, 99),
      `Very \n Dissatisfied` = c(18.9, 19.2, 22.2, 19.0, 22.2),
      Dissatisfied = c(14.5, 16.0, 25.3, 19.0, 20.5),
      Neutral = c(23.0, 25.5, 15.7, 21.2, 20.9),
      Satisfied = c(22.0, 18.9, 17.2, 22.9, 17.5),
      `Very \n Satisfied` = c(21.7, 20.4, 19.5, 18.0, 18.9),
      check.names = FALSE
    )
    
    library(tidyverse)
    library(ggstats)
    library(patchwork)
    
    levels <- names(df)[-c(1:2)]
    df_long <- df %>%
      select(-n) %>%
      pivot_longer(!var, names_to = "Likert", values_to = "Percentage") |>
      mutate(Likert = factor(Likert, levels))
    
    custom_colors <- c(
      "#D91828", "#D98989", "#71B1D9", "#71F26D", "#17A621"
    )
    names(custom_colors) <- levels
    
    df_tot <- df_long |>
      summarise(
        prop_lower = sum(Percentage[Likert %in% levels[1:2]]),
        prop_higher = sum(Percentage[Likert %in% levels[4:5]]),
        .by = var
      ) |>
      pivot_longer(-var,
        names_prefix = "prop_",
        values_to = "Percentage",
        names_to = "where"
      )
    
    # Likert plot
    likert_plot <- ggplot(df_long, aes(x = Percentage, y = var, fill = Likert)) +
      geom_col(position = position_likert(reverse = FALSE)) +
      geom_text(
        aes(
          label = label_percent_abs(hide_below = .01, accuracy = 1, scale = 1)(Percentage),
          color = after_scale(hex_bw(fill))
        ),
        position = position_likert(vjust = 0.5, reverse = FALSE),
        size = 3.5,
        fontface = "bold"
      ) +
      geom_label(
        data = df_tot,
        aes(
          label = label_percent_abs(hide_below = .01, accuracy = 1, scale = 1)(Percentage),
          x = ifelse(where == "lower", -.6, .6),
          fill = NULL
        ),
        size = 3.5,
        fontface = "bold",
        label.size = 0,
        show.legend = FALSE
      ) +
      scale_x_continuous(
        labels = label_percent_abs()
      ) +
      labs(
        title = "Likert Responses by Category",
        x = "Category",
        y = "Percentage",
        fill = "Likert Scale"
      ) +
      theme_minimal() +
      scale_fill_manual(values = custom_colors) +
      labs(x = NULL, y = NULL, fill = NULL) +
      coord_cartesian(clip = "off")
    
    # Horizontal bar plot
    bar_plot <- ggplot(df, aes(x = n, y = var)) +
      geom_bar(stat = "identity", fill = "lightgrey") +
      geom_label(
        aes(
          label = label_number_abs(hide_below = .05, accuracy = 2)(n)
        ),
        size = 3.5,
        position = position_stack(vjust = 0.5),
        hjust = 1,
        fill = NA,
        label.size = 0,
        color = "black"
      ) +
      scale_y_discrete(labels = \(x) gsub("\\..*$", "", x)) +
      scale_x_continuous(
        labels = label_percent_abs(),
        expand = c(0, .15)
      ) +
      theme_light() +
      theme(
        legend.position = "bottom",
        panel.grid.major.y = element_blank(),
        axis.text.x = element_blank() # Hides x-axis numbers
      ) +
      labs(x = NULL, y = NULL, fill = NULL)
    
    # Print plots
    
    (likert_plot) + (bar_plot) +
      plot_layout(
        width = c(4, 1)
      ) &
      theme(legend.position = "bottom")
    

    enter image description here