Complete Code Listing
Below is the complete R code used in this chapter as a single script. You can copy this code into a Quarto document (.qmd file), render it, and reproduce all of the visualizations from this chapter.
Complete Code
# -------------------------------------------
# 1. Load packages and data
# -------------------------------------------
if(!require("pacman")) install.packages("pacman")
pacman::p_load("tidyverse", "psych", "knitr", "kableExtra", "gridExtra")
work <- read_delim(
"https://ljkelly3141.github.io/datasets/bi-book/Absenteeism_at_work.csv",
delim = ";"
)
# -------------------------------------------
# 2. Seasonality by month
# -------------------------------------------
absences_by_month <- work |>
filter(`Month of absence` > 0) |>
group_by(`Month of absence`) |>
summarise(
n_absences = n(),
total_hours = sum(`Absenteeism time in hours`)
) |>
mutate(
Month = factor(month.abb[`Month of absence`], levels = month.abb)
) |>
select(Month, n_absences, total_hours)
absences_by_month |>
pivot_longer(cols = c(n_absences, total_hours),
names_to = "metric", values_to = "value") |>
ggplot(aes(x = Month, y = value, fill = metric)) +
geom_col() +
facet_wrap(~ metric, ncol = 1, scales = "free_y",
labeller = as_labeller(c(
n_absences = "Number of absences",
total_hours = "Total hours absent"
))) +
labs(x = NULL, y = NULL) +
theme_minimal() +
theme(legend.position = "none")
# -------------------------------------------
# 3. Day-of-week pattern
# -------------------------------------------
weekday_labels <- c("Mon", "Tue", "Wed", "Thu", "Fri")
absences_by_weekday <- work |>
filter(`Month of absence` > 0) |>
group_by(`Day of the week`) |>
summarise(
n_absences = n(),
total_hours = sum(`Absenteeism time in hours`)
) |>
mutate(
Weekday = factor(weekday_labels[`Day of the week` - 1],
levels = weekday_labels)
) |>
select(Weekday, n_absences, total_hours)
absences_by_weekday |>
pivot_longer(cols = c(n_absences, total_hours),
names_to = "metric", values_to = "value") |>
ggplot(aes(x = Weekday, y = value, fill = metric)) +
geom_col() +
facet_wrap(~ metric, ncol = 1, scales = "free_y",
labeller = as_labeller(c(
n_absences = "Number of absences",
total_hours = "Total hours absent"
))) +
labs(x = NULL, y = NULL) +
theme_minimal() +
theme(legend.position = "none")
# -------------------------------------------
# 4. Distribution of absence duration
# -------------------------------------------
work |>
filter(`Absenteeism time in hours` > 0) |>
ggplot(aes(x = `Absenteeism time in hours`)) +
geom_histogram(binwidth = 4, fill = "steelblue", color = "white") +
labs(
title = "Distribution of absence duration",
x = "Hours absent (per event)",
y = "Number of events"
) +
theme_minimal()
# -------------------------------------------
# 5. Duration by reason group (box plot)
# -------------------------------------------
absence_with_reason <- work |>
filter(`Absenteeism time in hours` > 0,
`Reason for absence` > 0) |>
mutate(
reason_group = case_when(
`Reason for absence` %in% 1:6 ~ "Serious illness",
`Reason for absence` %in% 7:14 ~ "Other illness",
`Reason for absence` %in% 15:17 ~ "Pregnancy / perinatal",
`Reason for absence` %in% 18:21 ~ "Injury / external",
`Reason for absence` %in% 22:28 ~ "Routine / administrative",
TRUE ~ "Other"
),
reason_group = factor(reason_group, levels = c(
"Routine / administrative",
"Other illness",
"Serious illness",
"Injury / external",
"Pregnancy / perinatal"
))
)
absence_with_reason |>
ggplot(aes(x = reason_group, y = `Absenteeism time in hours`)) +
geom_boxplot(fill = "steelblue", alpha = 0.6, outlier.color = "grey40") +
scale_y_continuous(trans = "log10") +
labs(
title = "Absence duration by reason group",
x = NULL,
y = "Hours absent (log scale)"
) +
theme_minimal()
# -------------------------------------------
# 6. Heat map: month x weekday
# -------------------------------------------
work |>
filter(`Month of absence` > 0) |>
mutate(
Month = factor(month.abb[`Month of absence`], levels = month.abb),
Weekday = factor(c("Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat")[`Day of the week`],
levels = c("Mon", "Tue", "Wed", "Thu", "Fri"))
) |>
group_by(Month, Weekday) |>
summarise(total_hours = sum(`Absenteeism time in hours`),
.groups = "drop") |>
ggplot(aes(x = Weekday, y = Month, fill = total_hours)) +
geom_tile(color = "white") +
scale_y_discrete(limits = rev(month.abb)) +
scale_fill_viridis_c(option = "magma", direction = -1) +
labs(
title = "Total hours absent by month and weekday",
x = NULL, y = NULL, fill = "Hours"
) +
theme_minimal()The transition from data preparation to modeling marks a shift from “what does the data look like?” to “what can the data tell us?”