Created
July 24, 2025 13:16
-
-
Save JEFworks/1f0815a23f4ac16aac6bd826821df213 to your computer and use it in GitHub Desktop.
Visualizing temporal trends in country of citizenship for people arrested by ICE from Sep. 1, 2023 to Jun. 26, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############# | |
## Dataset on ICE enforcement from Sep. 1, 2023 to Jun. 26, 2025 | |
## Source: https://deportationdata.org/data/ice.html | |
############# | |
############# | |
## Read in data | |
############# | |
## Arrest data | |
arrests <- read_excel('2025-ICLI-00019_2024-ICFO-39357_ERO Admin Arrests_raw.xlsx', skip=6, sheet=1) | |
head(arrests) | |
############# | |
## Clean data | |
############# | |
library(tidyverse) | |
library(dplyr) | |
arrests_df <- data.frame( | |
date = arrests$`Apprehension Date`, | |
citizenship = arrests$`Citizenship Country`) | |
# Aggregate results by month | |
arrests_df <- arrests_df %>% | |
filter(year(date) <= 2025) %>% # <-- clean up typo date of 2115 in some sheets | |
mutate(month = floor_date(date, unit = "month")) | |
# Count methods per month | |
arrests_df_summary <- arrests_df %>% | |
group_by(month, citizenship) %>% | |
summarise(count = n(), .groups = 'drop') | |
head(arrests_df_summary) | |
range(arrests_df_summary$count) | |
# Cherry pick few countries to highlight | |
arrests_df_summary %>% filter(year(arrests_df_summary$month) == 2025) %>% | |
filter(count > quantile(count, 0.9, na.rm=TRUE)) %>% | |
summarize(sum(count), .by=citizenship) | |
# 1 BRAZIL 1693 | |
# 2 COLOMBIA 3704 | |
# 3 CUBA 2933 | |
# 4 DOMINICAN REPUBLIC 1919 | |
# 5 ECUADOR 3086 | |
# 6 EL SALVADOR 5579 | |
# 7 GUATEMALA 16191 | |
# 8 HONDURAS 13473 | |
# 9 MEXICO 43861 | |
# 10 NICARAGUA 3992 | |
# 11 PERU 1407 | |
# 12 VENEZUELA 8197 | |
# 13 CHINA, PEOPLES REPUBLIC OF 861 | |
# 14 INDIA 547 | |
# 15 HAITI 341 | |
# 16 IRAN 181 | |
# 17 VIETNAM 142 | |
# Clean up for display later | |
arrests_df_summary$citizenship[arrests_df_summary$citizenship == "CHINA, PEOPLES REPUBLIC OF"] <- 'CHINA' | |
# Cherry pick a few countries for plotting | |
topcitizens <- rev(c('MEXICO', 'GUATEMALA', 'HONDURAS', 'VENEZUELA', 'EL SALVADOR', | |
'CHINA', 'INDIA', 'HAITI', 'RUSSIA')) | |
arrests_df_summary_sub <- arrests_df_summary[arrests_df_summary$citizenship %in% topcitizens,] | |
############# | |
## Plot | |
############# | |
library(ggplot2) | |
library(gganimate) | |
library(dplyr) | |
library(tidyr) | |
library(lubridate) | |
library(ggrepel) | |
# Prepare data | |
df_long <- arrests_df_summary_sub %>% | |
pivot_longer(cols = count, | |
names_to = "metric", | |
values_to = "count") %>% | |
filter(metric == "count") %>% | |
mutate( | |
metric = recode(metric, count = "Arrests"), | |
month = as.Date(month), | |
citizenship = factor(citizenship, levels = topcitizens) | |
) %>% | |
arrange(citizenship, month) | |
# Create a numeric time index for animation reveal | |
df_long <- df_long %>% | |
mutate(reveal_order = match(citizenship, topcitizens)) %>% | |
group_by(citizenship) %>% | |
arrange(month) %>% | |
mutate(ind = row_number() + (reveal_order - 1) * n()) %>% | |
ungroup() | |
# Plot | |
p <- ggplot(df_long, aes(x = month, y = count, group = citizenship, color = citizenship)) + | |
geom_line() + | |
geom_point() + | |
# Label the moving point | |
geom_text_repel(aes(label = citizenship), hjust = -0.2, vjust = -0.5, size = 5, show.legend = FALSE) + | |
scale_x_date(date_breaks = "3 months", date_labels = "%b %Y") + | |
labs( | |
title = "People arrested by ICE: What countries are they from?", | |
subtitle = "Number of ICE arrests from Sep. 1, 2023 to Jun. 26, 2025 per month highlights | |
recent increased number of arrests for immigrants from many countries.", | |
caption = "'Arrest' = An event where an ICE officer apprehended someone, | |
whether or not that person has a criminal record. | |
Select countries of citizenship shown. | |
Data source: https://deportationdata.org/data/ice.html", | |
x = "", | |
y = "Number of People Arrested by ICE" | |
) + | |
theme_bw(base_size = 15) + | |
theme( | |
plot.title = element_text(size = 18, lineheight = 1.9, vjust = 3), | |
axis.title = element_text(size = 13), | |
axis.text = element_text(size = 10), | |
plot.subtitle = element_text(size = 13, lineheight = 0.9), | |
plot.caption = element_text(size = 10, vjust = -35, hjust=0), | |
legend.position = "none", | |
axis.text.x = element_text(angle = 45, hjust = 1), | |
plot.margin = margin(t = 30, r = 20, b = 90, l = 20) | |
) | |
# Animate | |
panim <- p + | |
transition_reveal(along = ind) + # Moves point and label along | |
enter_fade() + | |
exit_fade() + | |
view_follow(fixed_y = FALSE) | |
# Render | |
animate(panim, | |
width = 600, | |
height = 600, | |
fps = 20, | |
duration = 20, | |
end_pause = 100) | |
Author
JEFworks
commented
Jul 24, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment