Skip to content

Instantly share code, notes, and snippets.

@tor-gu
Created November 11, 2025 21:09
Show Gist options
  • Select an option

  • Save tor-gu/26ece04c44cc8e84532664f079c53dbf to your computer and use it in GitHub Desktop.

Select an option

Save tor-gu/26ece04c44cc8e84532664f079c53dbf to your computer and use it in GitHub Desktop.
Generate a map comparing the performance of Kim vs Harris in NJ in 2024
library(tigris)
library(njmunicipalities)
library(njelections)
library(purrr)
library(dplyr)
library(ggplot2)
library(tidyr)
library(glue)
library(stringr)
# Get the county names
county_names <- njmunicipalities::counties |> pull(county)
# Get the map of NJ
options(tigris_use_cache = TRUE)
nj_municipality_map <- county_names %>%
map_df( ~ county_subdivisions("NJ", county = ., class = "sf")) |>
filter(ALAND > 0)
# Get a map of congressional district NJ-03
nj_03_map <- congressional_districts(state="NJ", year=2024) |> filter(GEOID==3403)
# Calculate the statewide Kim - Harris vote share delta
kim_harris_delta <- election_statewide |>
filter(year==2024) |>
filter(party %in% c("Democratic", "Republican")) |>
select(office, party, vote) |>
group_by(office) |>
mutate(vote_share=vote/sum(vote)) |>
filter(party=="Democratic") |>
select(office, vote_share) |>
arrange(office) |> # This will put President before Senate
pull(vote_share) |> diff()
# Calculate the relative delta of Kim over Harris by municipality
delta_by_geoid <- election_by_municipality |>
filter(year==2024) |>
filter(party %in% c("Democratic", "Republican")) |>
select(GEOID, office, party, vote) |>
group_by(GEOID, office) |>
mutate(vote_share=vote/sum(vote)) |>
ungroup() |>
filter(party=="Democratic") |>
select(GEOID, office, vote_share) |>
pivot_wider(names_from=office, values_from = vote_share) |>
mutate(delta=Senate-President-kim_harris_delta) |>
select(GEOID, delta)
# Now glue our election data to the map
map_with_values <- nj_municipality_map |>
left_join(delta_by_geoid, by="GEOID")
# Plot our map
plot_kim_harris <- map_with_values |>
ggplot() +
geom_sf(aes(fill=delta, geometry=geometry, size=.2)) +
geom_sf(data=nj_03_map, color="darkgreen", fill=NA, aes(geometry=geometry, size=.6)) +
ggrepel::geom_label_repel(
data=nj_03_map,
color="darkgreen",
mapping = aes(label = "NJ-03", geometry = geometry),
stat = "sf_coordinates",
nudge_x = -.5,
nudge_y = .2,
) +
scale_size_identity() +
scale_fill_gradient2(
na.value = "lightgrey",
low = "purple",
high = "green",
mid = "lightgrey",
midpoint = kim_harris_delta,
limits = c(-.05,.05),
oob = scales::squish,
breaks = c(-.05, 0, .05),
labels=c("More Harris", glue("State Average (Kim +{round(kim_harris_delta, 3)})"), "More Kim"),
name="Margin"
) +
theme(
axis.ticks = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.background = element_rect(fill = "lightblue"),
panel.grid.major = element_line(color = "lightblue"),
legend.position = "bottom",
legend.key.width = unit(2, "cm"),
legend.title = element_blank(),
plot.title = element_text(hjust=0.5),
plot.subtitle = element_text(hjust=0.5),
plot.title.position = "panel",
) +
labs(title=str_wrap("Relative overperformance of Andy Kim vs. Kamala Harris", width=45),
subtitle = str_wrap("Two-party vote share in elections for Senate and President 2024", width=50),
caption="Source: NJ Division of Elections")
# Save plot
# ggsave("kim_harris.png", plot = plot_kim_harris )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment