# Load necessary libraries
pacman::p_load(readr, ggplot2, dplyr, tidyr, viridis, ggthemes, RColorBrewer, countrycode, gridExtra)
# Load the dataset
data <- read.csv("Age-of-Sexual-Consent.csv")
# Clean column names for easier use
colnames(data) <- c("country", "age_consent")
# Add a column to identify countries where marriage is required
data$requires_marriage <- ifelse(data$age_consent == "Must be married", TRUE, FALSE)
# Prepare the data for analysis
data$age_consent <- ifelse(data$age_consent %in% c("Must be married"), NA, data$age_consent)
data$age_consent <- as.numeric(as.character(data$age_consent)) # Convert age of consent to numeric
# Handle countries requiring marriage
marriage_countries <- data %>%
filter(requires_marriage) %>%
mutate(age_consent = NA) # No bars for these countries
# Split the data into two halves: higher and lower ages of consent
data_high <- data %>%
filter(!is.na(age_consent)) %>%
arrange(desc(age_consent)) %>%
slice(1:floor(n() / 2)) # Top half by age of consent
data_low <- data %>%
filter(!is.na(age_consent)) %>%
arrange(desc(age_consent)) %>%
slice((floor(n() / 2) + 1):n()) # Bottom half by age of consent
# Add marriage countries to the lower chart
data_low <- bind_rows(data_low, marriage_countries) %>%
arrange(desc(is.na(age_consent)), desc(age_consent))
# Caption
Caption <- "Data Visualisation: Patrick Ford"
# Create the bar chart for the top half
bar_chart_high <- ggplot(data_high, aes(x = reorder(country, age_consent), y = age_consent, fill = age_consent)) +
geom_bar(stat = "identity") +
coord_flip() +
scale_fill_viridis_c(option = "viridis", name = "Age of Consent") +
labs(title = "Higher Ages of Sexual Consent by Country 2025",
subtitle = "Source: AgeOfConsent.net 2025",
caption = Caption,
x = "Country", y = "Age of Consent") +
theme_minimal() +
theme(axis.text.y = element_text(size = 8))
# Create the bar chart for the bottom half
bar_chart_low <- ggplot(data_low, aes(x = reorder(country, age_consent), y = age_consent, fill = age_consent)) +
geom_bar(stat = "identity", na.rm = TRUE) +
coord_flip() +
scale_fill_viridis_c(option = "viridis", name = "Age of Consent") +
labs(title = "Lower Ages of Sexual Consent by Country 2025",
subtitle = "Source: AgeOfConsent.net 2025\n(Includes Countries Requiring Marriage (without bars); Effectively no Age of Consent)",
caption = Caption,
x = "Country", y = "Age of Consent") +
theme_minimal() +
theme(axis.text.y = element_text(size = 8))
# Arrange the charts side by side
grid.arrange(bar_chart_high, bar_chart_low, ncol = 2)
# Load the dataset
data <- read.csv("Percentage-of-Females-Married-by-15-years-old-2015-2023.csv")
# Clean column names for easier use
colnames(data) <- c("country", "percentage")
# Convert `percentage` to numeric and handle missing values (`-`)
data <- data %>%
mutate(
percentage = as.numeric(percentage) # Convert to numeric
) %>%
filter(!is.na(percentage) & percentage > 0) # Exclude invalid or zero values
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `percentage = as.numeric(percentage)`.
## Caused by warning:
## ! NAs introduced by coercion
# Total number of rows after filtering
n_rows <- nrow(data)
# Split data into two halves for visualisation
data_high <- data %>%
arrange(desc(percentage)) %>%
slice(1:ceiling(n_rows / 2)) # Top half by percentage
data_low <- data %>%
arrange(desc(percentage)) %>%
slice((ceiling(n_rows / 2) + 1):n_rows) # Bottom half by percentage
# Create bar chart for the top half (highest percentages at the top)
bar_chart_high <- ggplot(data_high, aes(x = reorder(country, percentage), y = percentage, fill = percentage)) +
geom_bar(stat = "identity") +
coord_flip() +
scale_fill_viridis_c(option = "viridis", name = "Percentage") +
labs(title = "Countries with Higher Percentages of Females Married by 15yrs Old",
subtitle = "Source: UNICEF 2015-2023",
caption = Caption,
x = "Country", y = "Percentage") +
theme_minimal() +
theme(axis.text.y = element_text(size = 8))
# Create bar chart for the bottom half (highest percentages at the top)
bar_chart_low <- ggplot(data_low, aes(x = reorder(country, percentage), y = percentage, fill = percentage)) +
geom_bar(stat = "identity") +
coord_flip() +
scale_fill_viridis_c(option = "viridis", name = "Percentage") +
labs(title = "Countries with Lower Percentages of Females Married by 15yrs Old",
subtitle = "Source: UNICEF 2015-2023",
caption = Caption,
x = "Country", y = "Percentage") +
theme_minimal() +
theme(axis.text.y = element_text(size = 8))
# Arrange the two charts side by side
grid.arrange(bar_chart_high, bar_chart_low, ncol = 2)
# Load the data
Global_Slavery_Index_2023 <- read_csv("Global_Slavery_Index_2023.csv")
## Rows: 180 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Country, Population, Region
## dbl (1): Estimated prevalence of modern slavery per 1,000 population
## num (1): Estimated number of people in modern slavery
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Clean Population column (remove commas, convert to numeric, handle NAs)
Global_Slavery_Index_2023 <- Global_Slavery_Index_2023 %>%
mutate(Population = as.numeric(gsub(",", "", Population)))
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Population = as.numeric(gsub(",", "", Population))`.
## Caused by warning:
## ! NAs introduced by coercion
# Remove rows with missing data in critical columns
Global_Slavery_Index_2023 <- Global_Slavery_Index_2023 %>%
drop_na(`Estimated prevalence of modern slavery per 1,000 population`,
`Estimated number of people in modern slavery`)
### Prepare data for visualisation
# Top 20 and bottom 20 by prevalence
prevalence_sorted <- Global_Slavery_Index_2023 %>%
arrange(desc(`Estimated prevalence of modern slavery per 1,000 population`))
top_20_prevalence <- prevalence_sorted %>% slice_head(n = 20)
bottom_20_prevalence <- prevalence_sorted %>% slice_tail(n = 20)
# Top 20 and bottom 20 by estimated number of people
number_sorted <- Global_Slavery_Index_2023 %>%
arrange(desc(`Estimated number of people in modern slavery`))
top_20_number <- number_sorted %>% slice_head(n = 20)
bottom_20_number <- number_sorted %>% slice_tail(n = 20)
# Create a fixed colour palette for regions
region_colors <- RColorBrewer::brewer.pal(n = length(unique(Global_Slavery_Index_2023$Region)), "Dark2")
names(region_colors) <- unique(Global_Slavery_Index_2023$Region)
# Data label colour
Data_label <- "#ffffff"
# Subtitle
Subtitle <- "Source: Walk Free 2023, Global Slavery Index 2023"
# Create Plots
# Top 20 by prevalence
p1 <- ggplot(top_20_prevalence, aes(x = reorder(Country, `Estimated prevalence of modern slavery per 1,000 population`),
y = `Estimated prevalence of modern slavery per 1,000 population`, fill = Region)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::comma(`Estimated prevalence of modern slavery per 1,000 population`)),
hjust = 1, color = Data_label, fontface = "bold", size = 3.5) +
coord_flip() +
scale_fill_manual(values = region_colors) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Top 20 Countries by Estimated Prevalence of Modern Slavery",
subtitle = Subtitle,
caption = Caption,
x = "Country", y = "Estimated Prevalence per 1,000 Population") +
theme_minimal() +
theme(legend.position = "bottom")
# Bottom 20 by prevalence
p2 <- ggplot(bottom_20_prevalence, aes(x = reorder(Country, `Estimated prevalence of modern slavery per 1,000 population`),
y = `Estimated prevalence of modern slavery per 1,000 population`, fill = Region)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::comma(`Estimated prevalence of modern slavery per 1,000 population`)),
hjust = 1, color = Data_label, fontface = "bold", size = 3.5) +
coord_flip() +
scale_fill_manual(values = region_colors) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Bottom 20 Countries by Estimated Prevalence of Modern Slavery",
subtitle = Subtitle,
caption = Caption,
x = "Country", y = "Estimated Prevalence per 1,000 Population") +
theme_minimal() +
theme(legend.position = "bottom")
# Top 20 by estimated number of people
p3 <- ggplot(top_20_number, aes(x = reorder(Country, `Estimated number of people in modern slavery`),
y = `Estimated number of people in modern slavery`, fill = Region)) +
geom_bar(stat = "identity") +
geom_text(aes(label = ifelse(`Estimated number of people in modern slavery` > 1e6,
paste0(round(`Estimated number of people in modern slavery` / 1e6, 1), "M"),
scales::comma(`Estimated number of people in modern slavery`))),
hjust = 1, color = Data_label, fontface = "bold", size = 3.5) +
coord_flip() +
scale_fill_manual(values = region_colors) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Top 20 Countries by Estimated Number of People in Modern Slavery",
subtitle = Subtitle,
caption = Caption,
x = "Country", y = "Estimated Number of People") +
theme_minimal() +
theme(legend.position = "bottom")
# Bottom 20 by estimated number of people
p4 <- ggplot(bottom_20_number, aes(x = reorder(Country, `Estimated number of people in modern slavery`),
y = `Estimated number of people in modern slavery`, fill = Region)) +
geom_bar(stat = "identity") +
geom_text(aes(label = scales::comma(`Estimated number of people in modern slavery`)),
hjust = 1, color = Data_label, fontface = "bold", size = 3.5) +
coord_flip() +
scale_fill_manual(values = region_colors) +
scale_y_continuous(labels = scales::comma) +
labs(title = "Bottom 20 Countries by Estimated Number of People in Modern Slavery",
subtitle = Subtitle,
caption = Caption,
x = "Country", y = "Estimated Number of People") +
theme_minimal() +
theme(legend.position = "bottom")
# Arrange Plots Side by Side
grid.arrange(p1, p2, nrow = 2)
grid.arrange(p3, p4, nrow = 2)
grid.arrange(p3, p4, nrow = 2)