When visualizing data using a Bar Plot, there are situations where displaying percentage labels for all categories becomes unfeasible due to overlapping text. This often occurs when the proportion of one or more categories is significantly small and disproportionate compared to others. To handle this issue, there are two possible solutions:
Below are the R codes for implementing these two solutions.
#================================
# Draft Report for Survey Data
#================================
# Clear R environment:
rm(list = ls())
# Load some R packages:
library(readr)
library(dplyr)
library(stringi)
library(stringr)
library(showtext)
library(ggplot2)
library(tidyr)
questions <- c("Q1: Encouraging and motivating me to participate in learning activities",
"Q2: Having an effective teaching style",
"Q3: Being well prepared",
"Q4: Explaining clearly the lectures and tutorials",
"Q5: Being approachable and responsive?",
"Q6: Providing a variety of perspectives and evidence",
"Q7: Providing feedback that supported my learning",
"Q8: I was satisfied with the quality of this lecturer's teaching")
responses <- c("Very Satisfied", "Satisfied", "Neutral", "Dissatisfied", "Very Dissatisfied")
# Function recodes Likert scales:
convert_to_meaning <- function(x) {case_when(x == 1 ~ "Very Dissatisfied",
x == 2 ~ "Dissatisfied",
x == 3 ~ "Neutral",
x == 4 ~ "Satisfied",
x == 5 ~ "Very Satisfied")}
# Prepare colors for ploting:
col_Very_Dissatisfied <- "#e36c33"
col_Dissatisfied <- "#edad88"
col_neutral <- "#c7cdd1"
col_Satisfied <- "#829cb2"
col_Very_Satisfied <- "#3e6487"
# Select Font for the graph:
my_font <- "Lato"
font_add_google(name = my_font, family = my_font)
showtext_auto()
# Load data:
ausData <- read_csv("tqs_for Aus team.csv")
# Prepare data for ploting:
ausData %>%
select(Question1:Question8) %>%
mutate_all(convert_to_meaning) -> df1
names(df1) <- questions
df1 %>%
bind_cols(ausData %>% select(CourseName)) -> dataCourseNamePloting
nrow(dataCourseNamePloting) # Number of responses
dataCourseNamePloting %>%
pivot_longer(cols = questions) %>%
group_by(name, value) %>%
count() %>%
mutate(value = factor(value, levels = responses[5:1])) %>%
mutate(name = factor(name, levels = questions[8:1])) -> dataForPlotingBar
#------------------------------------------
# Design a Graph Template (Version 1)
#------------------------------------------
# Prepare data for ploting percent text on bar plot:
dataForPlotingBar %>%
pivot_wider(names_from = "value", values_from = "n") %>%
mutate(rate = `Very Satisfied` / (`Very Satisfied` + `Very Dissatisfied` + Dissatisfied + Neutral + Satisfied)) %>%
mutate(rate = round(100*rate, 1)) %>%
mutate(rate = as.character(rate)) %>%
mutate(ratetext = case_when(str_count(rate) != 4 ~ str_c(rate, ".0"), TRUE ~ rate)) %>%
mutate(ratetext = str_c(ratetext, "%")) -> dfText
dataForPlotingBar %>%
pivot_wider(names_from = "value", values_from = "n") %>%
mutate(rateVerySat = `Very Satisfied` / (`Very Satisfied` + `Very Dissatisfied` + Dissatisfied + Neutral + Satisfied)) %>%
mutate(rateSat = Satisfied / (`Very Satisfied` + `Very Dissatisfied` + Dissatisfied + Neutral + Satisfied)) -> dfText2
dfText2 %>% pull(rateVerySat) -> percent1
round(100*percent1, 1) %>% as.character() -> percent1Text
case_when(str_count(percent1Text) != 4 ~ str_c(percent1Text, ".0"), TRUE ~ percent1Text) -> percent1Text
str_c(percent1Text, "%") -> percent1Text
percent1 / 2 -> position1
dfText2 %>% mutate(posi2 = rateVerySat + 0.5*rateSat) -> dfText2
dfText2 %>% pull(rateSat) -> percent2
round(100*percent2, 1) %>% as.character() -> percent2Text
case_when(str_count(percent2Text) != 4 ~ str_c(percent2Text, ".0"), TRUE ~ percent2Text) -> percent2Text
str_c(percent2Text, "%") -> percent2Text
dfText2 %>% pull(posi2) -> position2
ggplot() +
geom_col(data = dataForPlotingBar, aes(y = name, x = n, fill = value), position = "fill") +
theme(axis.title = element_blank()) +
labs(title = "Students' Satisfaction Level with The Quality of Teaching",
subtitle = "The total number of responses for the 20 courses is 932. The positive feedback rate from students\nacross 8 metrics is high. Teaching style (Q2) is the aspect with the highest dissatisfaction rate.",
caption = "Source: Student Feedback on Teaching Survey | Swinburne University of Technology, Vietnam Campus") -> draftBarPlot
draftBarPlot +
scale_x_continuous(breaks = seq(0, 1, 0.25), expand = c(0, 0), labels = scales::percent) +
scale_y_discrete(expand = c(0, 0)) +
scale_fill_manual(values = c(`Very Satisfied` = col_Very_Satisfied,
`Satisfied` = col_Satisfied,
`Neutral` = col_neutral,
`Dissatisfied` = col_Dissatisfied,
`Very Dissatisfied` = col_Very_Dissatisfied)) +
theme(strip.text = element_text(color = "grey20", family = my_font, hjust = 0, size = 10)) +
theme(legend.position = "top") +
theme(axis.title = element_blank()) +
theme(text = element_text(family = my_font)) +
theme(plot.margin = unit(rep(0.7, 4), "cm")) +
theme(legend.text = element_text(size = 9, family = my_font, color = "grey30")) +
theme(legend.key.height = unit(0.4, "cm")) +
theme(legend.key.width = unit(0.27*1, "cm")) +
theme(legend.title = element_blank()) +
theme(panel.grid.minor = element_blank()) +
theme(panel.grid.major.x = element_line(size = 0.5, color = "grey40")) +
theme(axis.text.x = element_text(color = "grey30", size = 11, family = my_font)) +
theme(axis.text.y = element_text(color = "grey30", size = 10.1, family = my_font)) +
theme(plot.title = element_text(size = 14, face = "plain", color = "grey20")) +
theme(plot.subtitle = element_text(size = 10, color = "grey30")) +
theme(plot.caption = element_text(size = 8, color = "grey40", vjust = -1.5, hjust = 1)) +
theme(plot.title.position = "plot") +
theme(plot.caption.position = "plot") +
guides(fill = guide_legend(reverse = TRUE)) +
theme(axis.ticks.y = element_blank()) -> draftBarPlot
draftBarPlot +
geom_text(data = dfText2,
aes(y = name, x = 0.06, label = percent1Text),
size = 3.5, color = "white", family = my_font) -> barVersion1
barVersion1
#------------------------------------------
# Design a Graph Template (Version 2)
#------------------------------------------
draftBarPlot +
theme(panel.grid.major.x = element_blank()) +
geom_text(data = dfText2,
aes(y = name, x = position1, label = percent1Text),
size = 3.5, color = "white", family = my_font) +
geom_text(data = dfText2,
aes(y = name, x = position2, label = percent2Text),
size = 3.5, color = "white", family = my_font) -> barVersion2
barVersion2
For an alternative approach to this problem, you can read this post on Stack Overflow.