Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Likert plot with ggplot2 in R with different ranking levels and display percentages

Tags:

r

ggplot2

likert

i have a data frame in R called df that looks like this :

  df
# A tibble: 90 × 3
# Groups:   item [18]
   item  Response          Percentage
   <chr> <fct>                  <dbl>
 1 A     Very Dissatisfied        0  
 2 A     Dissatisfied             0  
 3 A     Average                 33.3
 4 A     Satisfied               11.1
 5 A     Very Satisfied          55.6
 6 B     Very Dissatisfied        0  
 7 B     Dissatisfied             0  
 8 B     Average                 44.4
 9 B     Satisfied                0  
10 B     Very Satisfied          55.6
# ℹ 80 more rows
# ℹ Use `print(n = ...)` to see more rows

the response column is Likert scale column with 5 levels. Plotting it with ggplot2



# Creating a mapping of responses to numeric values
response_mapping <- c("Very Dissatisfied" = 1,
                      "Dissatisfied" = 2,
                      "Average" = 3,
                      "Satisfied" = 4,
                      "Very Satisfied" = 5)

# Applying the mapping and calculate the sign
data_f_sum <- df %>% 
  ungroup() %>% 
  mutate(res.sgn = sign(response_mapping[as.character(Response)] - 3)) %>% 
  summarise(sum.prcnt = sum(Percentage),
            .by = c(item, res.sgn))
likert_levels =  c("Very Dissatisfied", 
                   "Dissatisfied" ,
                   "Average" ,
                   "Satisfied", 
                   "Very Satisfied")

df = df%>%
  mutate(Response = factor(Response , levels = likert_levels))

ggplot(data = df, 
       aes(Percentage, item, fill = Response)) +
  geom_col(position = position_likert()) +
  scale_x_continuous(breaks = seq(-1, 1, 0.5),
                     labels = ggstats::label_percent_abs()) +
  geom_label(data = data_f_sum,
             aes(label = sprintf("%.1f", sum.prcnt), y = item, x = res.sgn * 0.5),
             alpha = 0.3, inherit.aes = FALSE) +
  scale_fill_brewer(type = "div", palette = "RdYlGn") +
  theme_bw()

i receive the following plot (picture)

enter image description here

i want to solve some issues that i have here and i need some help:

  1. I want to fix the average level in the middle and horizontically to start from 0%.

  2. i want in the middle of each item in the average level to display the percentage of the average level. at the left of each item to display the percentage sum of the two lower levels (i.e very dissatisfied and satisfied) and at the right to display the percentage sum of the two upper levels (i.e satisfied and very satisfied) .Something like this :

enter image description here

Can someone help me solve these issues ?

data are here :

 structure(list(item = c("A", "A", "A", "A", "A", "B", "B", "B", 
"B", "B", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D", "E", 
"E", "E", "E", "E", "F", "F", "F", "F", "F", "G", "G", "G", "G", 
"G", "H", "H", "H", "H", "H", "I", "I", "I", "I", "I", "J", "J", 
"J", "J", "J", "K", "K", "K", "K", "K", "L", "L", "L", "L", "L", 
"M", "M", "M", "M", "M", "N", "N", "N", "N", "N", "O", "O", "O", 
"O", "O", "P", "P", "P", "P", "P", "Q", "Q", "Q", "Q", "Q", "R", 
"R", "R", "R", "R"), Response = structure(c(4L, 2L, 1L, 3L, 5L, 
4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 
2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 
1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 
3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 
5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 4L, 2L, 1L, 3L, 5L, 
4L, 2L, 1L, 3L, 5L), levels = c("Average", "Dissatisfied", "Satisfied", 
"Very Dissatisfied", "Very Satisfied"), class = "factor"), Percentage = c(0, 
0, 33.3, 11.1, 55.6, 0, 0, 44.4, 0, 55.6, 0, 0, 22.2, 33.3, 44.4, 
0, 0, 33.3, 11.1, 55.6, 0, 22.2, 11.1, 11.1, 55.6, 0, 0, 44.4, 
11.1, 44.4, 0, 0, 11.1, 33.3, 55.6, 0, 0, 33.3, 22.2, 44.4, 0, 
0, 11.1, 33.3, 55.6, 0, 0, 22.2, 22.2, 55.6, 0, 0, 11.1, 11.1, 
77.8, 0, 0, 11.1, 33.3, 55.6, 0, 0, 33.3, 0, 66.7, 0, 0, 33.3, 
11.1, 55.6, 0, 11.1, 0, 33.3, 55.6, 0, 0, 22.2, 22.2, 55.6, 0, 
22.2, 22.2, 11.1, 44.4, 0, 11.1, 22.2, 11.1, 55.6)), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -90L), groups = structure(list(
    item = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", 
    "K", "L", "M", "N", "O", "P", "Q", "R"), .rows = structure(list(
        1:5, 6:10, 11:15, 16:20, 21:25, 26:30, 31:35, 36:40, 
        41:45, 46:50, 51:55, 56:60, 61:65, 66:70, 71:75, 76:80, 
        81:85, 86:90), ptype = integer(0), class = c("vctrs_list_of", 
    "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -18L), .drop = TRUE))

like image 446
Homer Jay Simpson Avatar asked Dec 06 '25 16:12

Homer Jay Simpson


1 Answers

You can center the likert scale by hard-setting limits for the x axis, using coord_cartesian, so that your plot actually goes from -100% to 100% instead of adjusting to the data ranges, and you can place the labels on the edges of the plot just by using -1 or 1 as your x value:

library(ggplot2)
library(ggstats)

ggplot(data = df, 
       aes(Percentage, item, fill = Response)) +
  geom_col(position = position_likert()) +
  scale_x_continuous(#breaks = seq(-1, 1, 0.5),
                      
                     labels = ggstats::label_percent_abs()) +
  geom_label(data = data_f_sum,
             aes(label = sprintf("%.1f", sum.prcnt), y = item, x = res.sgn),
             alpha = 0.3, inherit.aes = FALSE) +
  coord_cartesian(xlim = c(-1, 1)) +
  scale_fill_brewer(type = "div", palette = "RdYlGn") +
  theme_bw()

enter image description here

like image 96
Bastián Olea Herrera Avatar answered Dec 09 '25 15:12

Bastián Olea Herrera



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!