Executive Summary

raw <- read.csv(
  csv_file,
  stringsAsFactors = FALSE,
  check.names = FALSE
)

# The source contains one aggregate row. It is excluded because this
# analysis is explicitly precinct-level.
precinct_data <- raw %>%
  filter(!is.na(Precinct), Precinct != "Total People")

# Convert percentage strings to proportions.
pct_cols <- names(precinct_data)[grepl("%", names(precinct_data), fixed = TRUE)]
precinct_data <- precinct_data %>%
  mutate(across(all_of(pct_cols), pct_to_num),
         MOV = pct_to_num(MOV))

# Signed percentage-point versions for presentation.
precinct_data <- precinct_data %>%
  mutate(
    Jones_pp = `Jones %` * 100,
    MOV_pp = MOV * 100,
    Turnout_pp = `Dem Turnout %` * 100,
    Majority_White = `White Dem RV %` > .50,
    Majority_Black = `Black Dem RV %` > .50
  )

n_precincts <- nrow(precinct_data)
jones_wins <- sum(precinct_data$`Jones Win?` == "Y", na.rm = TRUE)
jones_losses <- sum(precinct_data$`Jones Win?` == "N", na.rm = TRUE)
ties <- sum(precinct_data$`Jones Win?` == "Tie", na.rm = TRUE)

# Strongest demographic associations. MOV is deliberately excluded because
# it is mathematically derived from candidate vote shares.
cor_vars <- c(
  "White Dem PV %", "Black Dem PV%", "White Dem RV %", "Black Dem RV %",
  "Dem RV %", "Male Dem PV %", "Female Dem PV %", "Dem Turnout %",
  "18-24 Dem PV %", "25-34 Dem PV %", "35-49 Dem PV %",
  "50-64 Dem PV %", "65+ Dem PV %", "Doorknock Attempts", "Phonebank Attempts"
)

cor_results <- map_dfr(cor_vars, function(v) {
  x <- precinct_data[[v]]
  y <- precinct_data$`Jones %`
  keep <- complete.cases(x, y)
  tibble(
    Variable = v,
    N = sum(keep),
    r = cor(x[keep], y[keep], method = "pearson")
  )
}) %>%
  arrange(desc(abs(r)))

# Group-level summaries directly answer the two race-composition questions.
white_summary <- precinct_data %>%
  summarise(
    n = sum(Majority_White),
    wins = sum(`Jones Win?`[Majority_White] == "Y", na.rm = TRUE),
    mean_jones = mean(`Jones %`[Majority_White], na.rm = TRUE),
    mean_mov = mean(MOV[Majority_White], na.rm = TRUE)
  )

black_summary <- precinct_data %>%
  summarise(
    n = sum(Majority_Black),
    wins = sum(`Jones Win?`[Majority_Black] == "Y", na.rm = TRUE),
    mean_jones = mean(`Jones %`[Majority_Black], na.rm = TRUE),
    mean_mov = mean(MOV[Majority_Black], na.rm = TRUE)
  )

# Highest and lowest Jones precincts.
highest <- precinct_data %>% slice_max(`Jones %`, n = 1, with_ties = FALSE)
lowest <- precinct_data %>% slice_min(`Jones %`, n = 1, with_ties = FALSE)

summary_table <- tibble(
  Metric = c(
    "Precincts analyzed",
    "Jones wins / losses / ties",
    "Mean Jones vote share",
    "Median Jones vote share",
    "Mean precinct MOV",
    "Majority-Black precincts: wins / total",
    "Majority-White precincts: wins / total",
    "Strongest demographic association with Jones %",
    "Weakest of the tested turnout/contact relationships",
    "Highest Jones precinct",
    "Lowest Jones precinct"
  ),
  Result = c(
    n_precincts,
    paste(jones_wins, "/", jones_losses, "/", ties),
    percent(mean(precinct_data$`Jones %`), accuracy = .1),
    percent(median(precinct_data$`Jones %`), accuracy = .1),
    paste0(round(mean(precinct_data$MOV_pp), 1), " pp"),
    paste0(black_summary$wins, " / ", black_summary$n),
    paste0(white_summary$wins, " / ", white_summary$n),
    cor_results$Variable[1],
    cor_results %>%
      filter(Variable %in% c("Dem Turnout %", "Doorknock Attempts", "Phonebank Attempts")) %>%
      slice_min(abs(r), n = 1) %>%
      pull(Variable),
    paste0(highest$Precinct, " (", percent(highest$`Jones %`), ")"),
    paste0(lowest$Precinct, " (", percent(lowest$`Jones %`), ")")
  )
)

kable(summary_table, caption = "What the precinct data says at a glance") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
What the precinct data says at a glance
Metric Result
Precincts analyzed 258
Jones wins / losses / ties 131 / 122 / 5
Mean Jones vote share 34.7%
Median Jones vote share 31.2%
Mean precinct MOV 2.5 pp
Majority-Black precincts: wins / total 65 / 68
Majority-White precincts: wins / total 47 / 166
Strongest demographic association with Jones % White Dem PV %
Weakest of the tested turnout/contact relationships Dem Turnout %
Highest Jones precinct 02-031 (75%)
Lowest Jones precinct 01-022 (0%)

What this report can and cannot answer

It can answer: where Jones performed strongly or weakly, how precinct results relate to demographic composition and turnout, which precincts are unusual relative to the overall pattern, and where campaign-contact activity was associated with the result.

It cannot answer by itself: why an individual voter supported a candidate, whether a campaign contact caused a vote, or whether a precinct-level relationship would hold for individual voters. Those questions require voter-level or survey data.

1. The Central Finding: Race Composition and Jones Vote Share

The strongest relationships in this dataset are the racial composition measures. Black Democratic primary-voter share and White Democratic primary-voter share have correlations with Jones vote share of opposite signs and very similar magnitude.

central_data <- precinct_data %>%
  select(Precinct, `Jones %`, `Black Dem RV %`, `White Dem RV %`, `Dem Primary Voters`) %>%
  pivot_longer(
    cols = c(`Black Dem RV %`, `White Dem RV %`),
    names_to = "Group",
    values_to = "Composition"
  )

ggplot(central_data, aes(Composition, `Jones %`)) +
  geom_point(aes(size = `Dem Primary Voters`), alpha = .55) +
  geom_smooth(method = "lm", se = TRUE, color = "black") +
  facet_wrap(~ Group) +
  scale_x_continuous(labels = percent_format(accuracy = 1)) +
  scale_y_continuous(labels = percent_format(accuracy = 1)) +
  scale_size_continuous(name = "Democratic\nprimary voters", range = c(1.5, 6)) +
  labs(
    title = "Jones Vote Share Tracks Closely With Precinct Racial Composition",
    subtitle = "Point size reflects the number of Democratic primary voters in the precinct",
    x = "Democratic voter composition",
    y = "Jones vote share"
  )

Majority-group comparison

majority_table <- tibble(
  Group = c("Majority Black", "Majority White"),
  `Precincts` = c(black_summary$n, white_summary$n),
  `Jones Wins` = c(black_summary$wins, white_summary$wins),
  `Win Rate` = c(black_summary$wins / black_summary$n,
                 white_summary$wins / white_summary$n),
  `Mean Jones %` = c(black_summary$mean_jones, white_summary$mean_jones),
  `Mean MOV` = c(black_summary$mean_mov, white_summary$mean_mov)
) %>%
  mutate(
    `Win Rate` = percent(`Win Rate`, accuracy = .1),
    `Mean Jones %` = percent(`Mean Jones %`, accuracy = .1),
    `Mean MOV` = paste0(round(`Mean MOV` * 100, 1), " pp")
  )

kable(majority_table, caption = "Jones results in majority-Black vs. majority-White Democratic precincts") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
Jones results in majority-Black vs. majority-White Democratic precincts
Group Precincts Jones Wins Win Rate Mean Jones % Mean MOV
Majority Black 68 65 95.6% 56.8% 35.1 pp
Majority White 166 47 28.3% 25.2% -11.8 pp

2. Overall Margin of Victory Map

This is the geographic overview. Blue indicates a Jones win, red a Jones loss, and white a near-tie. The value is the percentage-point margin separating Jones from the next-highest candidate.

precinct_shapes <- st_read(geojson_file, quiet = TRUE)

precinct_map <- precinct_shapes %>%
  mutate(Precinct = VOTINGDISTRICTS) %>%
  left_join(precinct_data, by = "Precinct")

ggplot(precinct_map) +
  geom_sf(aes(fill = MOV_pp), color = "white", linewidth = .15) +
  scale_fill_gradient2(
    low = "#B2182B", mid = "white", high = "#2166AC", midpoint = 0,
    name = "Jones MOV (pp)"
  ) +
  labs(
    title = "Julian Jones Margin of Victory by Precinct",
    subtitle = "Positive = Jones win; negative = Jones loss"
  ) +
  theme_void()

3. Majority-Black Precincts

ggplot(precinct_map) +
  geom_sf(fill = "grey85", color = "white", linewidth = .15) +
  geom_sf(
    data = precinct_map %>% filter(Majority_Black),
    aes(fill = MOV_pp), color = "white", linewidth = .15
  ) +
  scale_fill_gradient2(
    low = "#B2182B", mid = "white", high = "#2166AC", midpoint = 0,
    name = "Jones MOV (pp)"
  ) +
  labs(
    title = "Jones Margin of Victory in Majority-Black Precincts",
    subtitle = "Black Democratic RV share > 50%; all other precincts are gray"
  ) +
  theme_void()

4. Majority-White Precincts

ggplot(precinct_map) +
  geom_sf(fill = "grey85", color = "white", linewidth = .15) +
  geom_sf(
    data = precinct_map %>% filter(Majority_White),
    aes(fill = MOV_pp), color = "white", linewidth = .15
  ) +
  scale_fill_gradient2(
    low = "#B2182B", mid = "white", high = "#2166AC", midpoint = 0,
    name = "Jones MOV (pp)"
  ) +
  labs(
    title = "Jones Margin of Victory in Majority-White Precincts",
    subtitle = "White Democratic RV share > 50%; all other precincts are gray"
  ) +
  theme_void()

5. Turnout: What It Explains and What It Does Not

Turnout has a weaker relationship with Jones vote share than racial composition. That distinction matters: a precinct can have high turnout and still produce a very different Jones result depending on who makes up the Democratic electorate.

turnout_model <- lm(`Jones %` ~ `Dem Turnout %`, data = precinct_data)
turnout_plot_data <- precinct_data %>%
  mutate(
    std_residual = rstandard(turnout_model),
    outlier = abs(std_residual) > 2
  )

ggplot(turnout_plot_data,
       aes(`Dem Turnout %`, `Jones %`, color = `Jones Win?`)) +
  geom_point(aes(size = `Dem Primary Voters`), alpha = .7) +
  geom_smooth(method = "lm", se = TRUE, color = "black") +
  geom_text_repel(
    data = turnout_plot_data %>% filter(outlier),
    aes(label = Precinct), color = "black", size = 3, max.overlaps = Inf
  ) +
  scale_x_continuous(labels = percent_format(accuracy = 1)) +
  scale_y_continuous(labels = percent_format(accuracy = 1)) +
  scale_color_manual(values = outcome_colors, labels = outcome_labels, name = NULL) +
  scale_size_continuous(name = "Democratic\nprimary voters", range = c(1.5, 6)) +
  labs(
    title = "Jones Vote Share vs. Democratic Primary Turnout",
    subtitle = "Labels identify unusually high/low results relative to the fitted relationship",
    x = "Democratic primary turnout",
    y = "Jones vote share"
  )

6. Age: Is There a Clear Relationship?

age_data <- precinct_data %>%
  select(
    Precinct, `Jones %`,
    `18-24 Dem PV %`, `25-34 Dem PV %`, `35-49 Dem PV %`,
    `50-64 Dem PV %`, `65+ Dem PV %`
  ) %>%
  pivot_longer(
    cols = -c(Precinct, `Jones %`),
    names_to = "Age Group",
    values_to = "Age Share"
  )

ggplot(age_data, aes(`Age Share`, `Jones %`)) +
  geom_point(alpha = .55, size = 2) +
  geom_smooth(method = "lm", se = TRUE, color = "black") +
  facet_wrap(~ `Age Group`, scales = "free_x") +
  scale_x_continuous(labels = percent_format(accuracy = 1)) +
  scale_y_continuous(labels = percent_format(accuracy = 1)) +
  labs(
    title = "Jones Vote Share vs. Age Composition",
    subtitle = "Age-group relationships are substantially weaker than the racial-composition relationships",
    x = "Share of Democratic primary voters",
    y = "Jones vote share"
  )

7. What Is Actually Most Closely Associated With Jones Vote Share?

Rather than calling these variables “influential,” this table identifies the strongest statistical associations in the precinct data. That is the more defensible interpretation of a precinct-level correlation.

MOV is intentionally excluded because it is mathematically constructed from candidate vote shares and therefore does not provide an independent explanation of Jones’s result.

correlation_results <- cor_results %>%
  mutate(
    Direction = if_else(r >= 0, "Positive", "Negative"),
    Strength = case_when(
      abs(r) >= .70 ~ "Very strong",
      abs(r) >= .50 ~ "Strong",
      abs(r) >= .30 ~ "Moderate",
      TRUE ~ "Weak"
    )
  )

kable(
  correlation_results %>%
    select(Variable, N, r, Strength, Direction),
  col.names = c("Variable", "N", "Pearson r", "Strength", "Direction"),
  digits = 3,
  caption = "Strongest associations with Jones vote share"
) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
Strongest associations with Jones vote share
Variable N Pearson r Strength Direction
White Dem PV % 258 -0.922 Very strong Negative
Black Dem PV% 258 0.920 Very strong Positive
White Dem RV % 258 -0.901 Very strong Negative
Black Dem RV % 258 0.896 Very strong Positive
Dem RV % 258 0.683 Strong Positive
Male Dem PV % 258 -0.414 Moderate Negative
Female Dem PV % 258 0.413 Moderate Positive
Doorknock Attempts 258 0.322 Moderate Positive
Phonebank Attempts 258 0.287 Weak Positive
Dem Turnout % 258 -0.278 Weak Negative
50-64 Dem PV % 258 0.158 Weak Positive
35-49 Dem PV % 258 -0.154 Weak Negative
25-34 Dem PV % 258 0.141 Weak Positive
65+ Dem PV % 258 -0.085 Weak Negative
18-24 Dem PV % 258 0.080 Weak Positive
correlation_results %>%
  slice_head(n = 10) %>%
  mutate(Variable = fct_reorder(Variable, r)) %>%
  ggplot(aes(r, Variable)) +
  geom_col(fill = "grey35") +
  geom_vline(xintercept = 0, linewidth = .8) +
  scale_x_continuous(limits = c(-1, 1)) +
  labs(
    title = "Top 10 Precinct-Level Associations With Jones Vote Share",
    subtitle = "Pearson correlation; this is association, not causation",
    x = "Pearson r",
    y = NULL
  )

8. Campaign Contact: A Separate Question

Campaign-contact variables deserve their own section because they are different from demographics. They can tell us whether where the campaign invested contact was related to the final result, but they cannot establish that the contact caused the result.

contact_results <- tibble(
  Variable = c("Doorknock Attempts", "Phonebank Attempts"),
  Correlation = c(
    cor(precinct_data$`Doorknock Attempts`, precinct_data$`Jones %`, use = "complete.obs"),
    cor(precinct_data$`Phonebank Attempts`, precinct_data$`Jones %`, use = "complete.obs")
  )
) %>%
  mutate(
    Correlation = round(Correlation, 3)
  )

kable(contact_results, caption = "Association between campaign contact volume and Jones vote share") %>%
  kable_styling(full_width = FALSE)
Association between campaign contact volume and Jones vote share
Variable Correlation
Doorknock Attempts 0.322
Phonebank Attempts 0.287
contact_data <- precinct_data %>%
  select(Precinct, `Jones %`, `Doorknock Attempts`, `Phonebank Attempts`, `Jones Win?`) %>%
  pivot_longer(
    cols = c(`Doorknock Attempts`, `Phonebank Attempts`),
    names_to = "Contact Type",
    values_to = "Attempts"
  )

ggplot(contact_data, aes(Attempts, `Jones %`, color = `Jones Win?`)) +
  geom_point(alpha = .65, size = 2) +
  geom_smooth(method = "lm", se = TRUE, color = "black") +
  facet_wrap(~ `Contact Type`, scales = "free_x") +
  scale_y_continuous(labels = percent_format(accuracy = 1)) +
  scale_color_manual(values = outcome_colors, labels = outcome_labels, name = NULL) +
  labs(
    title = "Campaign Contact Volume vs. Jones Vote Share",
    subtitle = "Descriptive relationship; contact was not randomly assigned",
    x = "Contact attempts",
    y = "Jones vote share"
  )

9. Precincts That Deserve a Closer Look

Rather than presenting a long list of “best” and “worst” precincts, this section identifies unusual precincts where the observed Jones result differs substantially from what the turnout relationship alone would predict.

These are places for additional investigation, not rankings.

unusual_precincts <- turnout_plot_data %>%
  filter(outlier) %>%
  arrange(desc(abs(std_residual))) %>%
  select(
    Precinct,
    `Jones Win?`,
    `Jones %`,
    `Dem Turnout %`,
    `Black Dem RV %`,
    `White Dem RV %`,
    `Doorknock Attempts`,
    `Phonebank Attempts`,
    std_residual
  ) %>%
  mutate(
    `Jones %` = percent(`Jones %`, accuracy = .1),
    `Dem Turnout %` = percent(`Dem Turnout %`, accuracy = .1),
    `Black Dem RV %` = percent(`Black Dem RV %`, accuracy = .1),
    `White Dem RV %` = percent(`White Dem RV %`, accuracy = .1),
    std_residual = round(std_residual, 2)
  )

kable(
  unusual_precincts,
  col.names = c(
    "Precinct", "Outcome", "Jones %", "Turnout", "Black Dem RV %",
    "White Dem RV %", "Doors", "Phones", "Std. residual"
  ),
  caption = "Precincts with unusually high or low Jones results relative to turnout"
) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
Precincts with unusually high or low Jones results relative to turnout
Precinct Outcome Jones % Turnout Black Dem RV % White Dem RV % Doors Phones Std. residual
03-015 N 0.0% 8.3% 8.3% 75.0% 0 3 -2.71
02-020 Y 72.6% 45.5% 70.8% 26.4% 210 1100 2.71
02-018 Y 69.4% 44.2% 89.7% 7.7% 0 1214 2.48
02-027 Y 70.3% 38.1% 86.8% 9.0% 3 1692 2.37
02-024 Y 71.4% 34.6% 93.5% 3.4% 0 1538 2.35
02-010 Y 70.6% 36.2% 91.7% 3.4% 4 2646 2.34
02-005 Y 70.3% 32.5% 93.5% 2.9% 139 1383 2.22
02-031 Y 75.0% 17.6% 80.2% 14.3% 0 50 2.13
02-014 Y 66.7% 37.4% 89.3% 7.0% 0 2866 2.13
02-003 Y 70.3% 28.9% 82.0% 9.8% 1 223 2.13
02-017 Y 69.4% 30.8% 92.8% 4.2% 3 1942 2.12
04-015 Y 66.7% 35.5% 61.8% 24.6% 0 86 2.08
02-012 Y 67.6% 32.5% 89.2% 7.6% 211 2844 2.06
02-015 Y 67.4% 33.0% 81.6% 12.1% 307 3291 2.06

10. Highest and Lowest Jones Vote-Share Precincts

These are reference tables, not rankings of campaign performance.

reference_table <- precinct_data %>%
  select(
    Precinct, `Jones Win?`, `Jones %`, MOV,
    `Dem Primary Voters`, `Dem Turnout %`,
    `White Dem RV %`, `Black Dem RV %`
  ) %>%
  mutate(
    `Jones %` = percent(`Jones %`, accuracy = .1),
    MOV = paste0(round(MOV * 100, 1), " pp"),
    `Dem Turnout %` = percent(`Dem Turnout %`, accuracy = .1),
    `White Dem RV %` = percent(`White Dem RV %`, accuracy = .1),
    `Black Dem RV %` = percent(`Black Dem RV %`, accuracy = .1)
  )

kable(
  reference_table %>% arrange(desc(`Jones %`)) %>% slice_head(n = 10),
  caption = "10 highest Jones vote-share precincts"
) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
10 highest Jones vote-share precincts
Precinct Jones Win? Jones % MOV Dem Primary Voters Dem Turnout % White Dem RV % Black Dem RV %
02-031 Y 75.0% 56.2 pp 16 17.6% 14.3% 80.2%
02-020 Y 72.6% 60.6 pp 409 45.5% 26.4% 70.8%
02-024 Y 71.4% 58.6 pp 606 34.6% 3.4% 93.5%
02-010 Y 70.6% 58.1 pp 986 36.2% 3.4% 91.7%
02-003 Y 70.3% 58.1 pp 74 28.9% 9.8% 82.0%
02-005 Y 70.3% 54.8 pp 522 32.5% 2.9% 93.5%
02-027 Y 70.3% 60.2 pp 633 38.1% 9.0% 86.8%
02-017 Y 69.4% 56 pp 712 30.8% 4.2% 92.8%
02-018 Y 69.4% 52.3 pp 438 44.2% 7.7% 89.7%
02-012 Y 67.6% 55 pp 1087 32.5% 7.6% 89.2%
kable(
  reference_table %>% arrange(`Jones %`) %>% slice_head(n = 10),
  caption = "10 lowest Jones vote-share precincts"
) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
10 lowest Jones vote-share precincts
Precinct Jones Win? Jones % MOV Dem Primary Voters Dem Turnout % White Dem RV % Black Dem RV %
01-022 N 0.0% -42.9 pp 14 53.8% 100.0% 0.0%
03-015 N 0.0% -100 pp 1 8.3% 75.0% 8.3%
04-011 N 0.0% -33.3 pp 2 100.0% 50.0% 50.0%
15-028 N 0.0% -66.7 pp 3 50.0% 100.0% 0.0%
08-015 N 10.0% -60 pp 10 23.3% 95.3% 0.0%
01-012 N 11.3% -27.4 pp 901 46.0% 92.8% 2.2%
09-002 N 11.7% -40 pp 180 31.1% 94.8% 1.4%
01-014 N 12.1% -37.7 pp 440 49.7% 87.2% 3.2%
09-029 N 12.4% -32.2 pp 460 37.0% 87.1% 6.8%
09-013 N 12.8% -26.3 pp 672 44.3% 93.0% 1.3%

11. Methodology Notes

  • The analysis contains 258 precinct records after removing the aggregate Total People row.
  • The GeoJSON contains the precinct boundaries used for the maps.
  • MOV is the signed percentage-point difference between Jones and the highest vote-getting opposing candidate in each precinct.
  • Majority-Black and majority-White classifications use Democratic registered-voter composition, with a threshold of greater than 50%.
  • Correlations are Pearson correlations across precincts. They measure association, not causation.
  • Precincts are not equal-sized. Some contain very few Democratic primary voters, so extreme percentages in very small precincts should be interpreted cautiously.
  • Campaign-contact volume was not randomly assigned. A positive or negative contact relationship cannot by itself demonstrate a campaign effect.
  • MOV is excluded from the explanatory correlation table because it is mechanically derived from candidate vote shares.
  • The analysis is ecological: relationships observed across precincts should not automatically be interpreted as relationships among individual voters.

Appendix: Data Quality Checks

map_ids <- precinct_shapes %>% transmute(Precinct = VOTINGDISTRICTS)

quality_table <- tibble(
  Check = c(
    "Precinct records in CSV after removing aggregate row",
    "Precinct polygons in GeoJSON",
    "CSV precincts without map geometry",
    "Map polygons without CSV data",
    "Rows with missing Jones vote share",
    "Rows with fewer than 25 Democratic primary voters"
  ),
  Result = c(
    nrow(precinct_data),
    nrow(map_ids),
    nrow(anti_join(precinct_data, map_ids, by = "Precinct")),
    nrow(anti_join(map_ids, precinct_data %>% select(Precinct), by = "Precinct")),
    sum(is.na(precinct_data$`Jones %`)),
    sum(precinct_data$`Dem Primary Voters` < 25, na.rm = TRUE)
  )
)

kable(quality_table, caption = "Data quality checks") %>%
  kable_styling(full_width = FALSE)
Data quality checks
Check Result
Precinct records in CSV after removing aggregate row 258
Precinct polygons in GeoJSON 258
CSV precincts without map geometry 0
Map polygons without CSV data 0
Rows with missing Jones vote share 0
Rows with fewer than 25 Democratic primary voters 11