NYC 2025 Mayoral Primary

library(targets)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
tar_load(cvr_long_appended)

First round CVR counts

cvr_long_appended |>
  filter(office == "DEM Mayor") |>
  filter(rank == min(rank), .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Zohran Kwame Mamdani 469,642
Andrew M. Cuomo 387,137
Brad Lander 120,634
Adrienne E. Adams 44,192
Scott M. Stringer 17,820
Zellnor Myrie 10,593
Whitney R. Tilson 8,443
overvote 5,500
Michael Blake 4,366
Jessica Ramos 4,273
Write-in 1,581
Paperboy Love Prince 1,560
Selma K. Bartholomew 1,489

This matches the candidate counts reported in the first round exactly!

Second round CVR counts: Removing write-in top choices

cvr_long_appended |>
  filter(office == "DEM Mayor",
         DefaultBallotName != "Write-in") |>
  filter(rank == min(rank), 
         .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Zohran Kwame Mamdani 469,755
Andrew M. Cuomo 387,377
Brad Lander 120,707
Adrienne E. Adams 44,359
Scott M. Stringer 17,894
Zellnor Myrie 10,648
Whitney R. Tilson 8,525
overvote 5,506
Michael Blake 4,389
Jessica Ramos 4,294
Paperboy Love Prince 1,628
Selma K. Bartholomew 1,505

Third round CVR counts

cvr_long_appended |>
  filter(office == "DEM Mayor",
         DefaultBallotName %in% c("Zohran Kwame Mamdani", 
                                  "Andrew M. Cuomo")) |>
  filter(rank == min(rank), 
         .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Zohran Kwame Mamdani 573,635
Andrew M. Cuomo 443,762

Cuomo vs. Lander

cvr_long_appended |>
  filter(office == "DEM Mayor",
         DefaultBallotName %in% c("Brad Lander", 
                                  "Andrew M. Cuomo")) |>
  filter(rank == min(rank), 
         .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Brad Lander 527,418
Andrew M. Cuomo 441,760

Cuomo vs. Adams

cvr_long_appended |>
  filter(office == "DEM Mayor",
         DefaultBallotName %in% c("Adrienne E. Adams", 
                                  "Andrew M. Cuomo")) |>
  filter(rank == min(rank), 
         .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Adrienne E. Adams 457,755
Andrew M. Cuomo 451,439

Adams vs. Mamdani

cvr_long_appended |>
  filter(office == "DEM Mayor",
         DefaultBallotName %in% c("Adrienne E. Adams", 
                                  "Zohran Kwame Mamdani")) |>
  filter(rank == min(rank), 
         .by = c(fn, `Cast Vote Record`)) |>
  #filter(!is.na(candidate)) |>
  count(DefaultBallotName) |> 
  arrange(desc(n)) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::cols_label("DefaultBallotName" = "Candidate",
                 "n" = "Votes") 
Candidate Votes
Zohran Kwame Mamdani 582,746
Adrienne E. Adams 196,728

Common 2nd and 3rd rankings for voters ranking various candidates first

cvr_long_appended |> 
  filter(office == "DEM Mayor",
         rank %in% 1:3) |>
  mutate(cand = str_extract(DefaultBallotName, "\\b(\\w+)$")) |>
  summarize(first_choice = cand[rank==1][1],
            ranking = paste0(sort_by(cand[rank>1],
                                     rank[rank>1]), 
                             collapse = "|"),
            .by = c(fn, `Cast Vote Record`)) |> 
  count(first_choice, ranking) |> 
  mutate(ranking = if_else(ranking == "", "(None ranked)", ranking)) |>
  arrange(first_choice, desc(n)) |> 
  mutate(cumpct = cumsum(n)/sum(n),
         .by = first_choice) |> 
  slice(1:10, .by = first_choice) |> 
  filter(first_choice %in% c("Cuomo", "Mamdani", "Lander", "Adams")) |>
  group_by(first_choice) |> 
  gt::gt() |> 
  gt::fmt_number(decimals = 0) |> 
  gt::fmt_percent(4, decimals = 0) |>
  gt::cols_label("n" = "No. of Ballots",
                 "first_choice" = "First Choice",
                 "ranking" = "Next two choices",
                 "cumpct" = "Cumulative precent") |> 
  gt::tab_style(
      style = gt::cell_text(weight = "bold", color = "darkblue"),
      locations = gt::cells_row_groups(groups = everything())
   )
Next two choices No. of Ballots Cumulative precent
Adams
(None ranked) 6,457 15%
Mamdani|Lander 2,633 21%
Lander|Mamdani 2,568 27%
Lander|Stringer 2,456 32%
Cuomo 2,243 37%
Lander|Myrie 1,445 41%
Cuomo|Stringer 1,294 43%
Mamdani|Myrie 1,239 46%
Stringer|Lander 1,238 49%
Cuomo|Lander 1,235 52%
Cuomo
(None ranked) 163,936 43%
Lander|Stringer 12,435 46%
Adams 11,446 49%
Cuomo|Cuomo 10,753 52%
Stringer|Lander 10,215 54%
Myrie|Adams 9,566 57%
Tilson 9,411 59%
Stringer 9,244 61%
Lander 8,536 64%
Tilson|Stringer 8,262 66%
Lander
Mamdani|Adams 21,643 18%
Mamdani|Myrie 9,333 26%
Adams|Mamdani 6,562 31%
Adams|Stringer 6,487 37%
Mamdani|Stringer 6,346 42%
Adams|Myrie 5,669 46%
Stringer|Adams 5,612 51%
Mamdani 4,412 55%
(None ranked) 4,132 58%
Myrie|Adams 3,771 61%
Mamdani
Lander|Adams 168,064 36%
Lander|Myrie 48,038 46%
(None ranked) 39,101 54%
Lander|Blake 33,764 62%
Adams|Lander 23,599 67%
Lander 18,982 71%
Lander|Stringer 18,217 75%
Lander|Ramos 10,120 77%
Blake|Lander 4,779 78%
Cuomo 4,495 79%