Skip to content

Instantly share code, notes, and snippets.

@JEFworks
Created July 24, 2025 13:16
Show Gist options
  • Save JEFworks/1f0815a23f4ac16aac6bd826821df213 to your computer and use it in GitHub Desktop.
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
#############
## 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)
@JEFworks
Copy link
Author

file135354a64f2e4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment