Spatial Interpolation

From precincts to districts

Last Time

  1. Gerrymandering packs and cracks voters.
  2. The mean-median score detects skew.
  3. The efficiency gap compares wasted votes.
  4. An ensemble shows what fair maps look like.

Key Ideas for Today

  1. Interpolation splits by area.
  2. Votes are extensive.
  3. SR precincts have shapes.
  4. Two approaches estimate new-map votes.

Your Turn

Question: What is the total value of the pink polygon on the right?

01:30

Agenda

  1. Spatial Interpolation
  2. Project: Gerrymandering
    1. Old map analysis
    2. New map analysis

Spatial Interpolation

Area-weighted Interpolation

Estimates a value for each target polygon by giving it, from each source polygon it overlaps, the share of the source’s value equal to the share of the source’s area that falls inside the target. Assumes each value is spread evenly over its source.

st_interpolate_aw(x, to, extensive)

A toy map

library(sf)
precincts_sf <- st_sf(votes = c(40, 30, 60),
  geometry = st_sfc(
    st_polygon(list(rbind(c(0,1), c(1,1), c(.5,.5), c(0,1)))),
    st_polygon(list(rbind(c(0,0), c(.5,0), c(.5,.5), c(0,1), c(0,0)))),
    st_polygon(list(rbind(c(.5,0), c(1,0), c(1,1), c(.5,.5), c(.5,0))))),
  crs = 3310)
districts_sf <- st_sf(district = c("North", "South"),
  geometry = st_sfc(
    st_polygon(list(rbind(c(0,.5), c(1,.5), c(1,1), c(0,1), c(0,.5)))),
    st_polygon(list(rbind(c(0,0), c(1,0), c(1,.5), c(0,.5), c(0,0))))),
  crs = 3310)

A toy map

plot(st_geometry(precincts_sf), lwd = 3)
plot(st_geometry(districts_sf), border = c("steelblue", "deeppink"),
     lwd = 5, lty = 2, add = TRUE)
text(st_coordinates(st_centroid(st_geometry(precincts_sf))),
     labels = precincts_sf$votes, cex = 2)

By hand

library(dplyr)
pieces <- precincts_sf |>
  mutate(precinct_area = st_area(geometry)) |>
  st_intersection(districts_sf) |>
  mutate(piece_area = st_area(geometry),
         piece_votes = votes * as.numeric(piece_area / precinct_area))

st_intersection() cuts one layer by another: each piece keeps the columns of both.

By hand

pieces |>
  group_by(district) |>
  summarize(votes = sum(piece_votes))

Or, in one step

interp <- st_interpolate_aw(precincts_sf["votes"], districts_sf, extensive = TRUE)
interp

Votes are counts: they’re spatially extensive (split them with the area). A rate or a share is intensive.

The result keeps the target geometries but drops district; add it back with interp$district <- districts_sf$district (rows come back in the same order).

Guess the Metaphor

Spatial interpolation is like a tea valley: when a new path cuts through the bushes (a new district line through the precincts), the harvest on each side is its share of every bush it crosses, as long as the leaves grow evenly over each bush (votes are spread evenly over each precinct).

Project: Gerrymandering

Project: Gerrymandering

Research question:

Will the adoption of the new district map lead to an increase in partisan advantage relative to 2024? If so, by how much?

Your answer will take the form of a dashboard.

Prop 50 Dashboard

Your final deliverable is a dashboard with six pages:

  1. Results (opens first): both maps, and seats, mean-median score, and efficiency gap for 2024 and the re-run
  2. About the Data
  3. Data Cleaning
  4. Exploratory Data Analysis
  5. Measuring Partisan Advantage
  6. Re-running the 2024 Election

Old Map Analysis

Question: Sketch the sf data frame needed to make this dashboard. Be sure to note:

  1. Number of rows
  2. Names and types of columns
03:00

A tale of two precinct types

SV Precincts

  1. Created by counties each election
  2. Two counties might use the same precinct name
  3. Unique ID: SVPREC x FIPS
  4. May not correspond to geographic area Example:
    • SVPREC: 30010 holds the vote by mail
    • SVPREC: 30010A holds the in-person votes

    This is why we won’t have shapefiles for every precinct

A tale of two precinct types

SR Precincts

  1. Created by the statewide data base to have a stable precinct unit from election to election.
  2. One SR precinct is generally an aggregation of smaller SV precincts.

We can get both votes and shapefiles for SR precincts.

How to construct the old map df

Ingredients:

  1. Statement of Vote, by SR precinct
  2. 2024 district shapefile

How to construct the old map df

Ingredients:

  1. Statement of Vote, by SR precinct
  2. 2024 district shapefile
  • California has a primary system where the two candidates with the most votes advance to the general election regardless of party.
  • How to deal with this effect in your analysis is up to you.

Old Map New Map

Approach A: Area-weighted Interpolation

Assume voters are spread evenly across each SR precinct, then use st_interpolate_aw() with extensive = TRUE to move each precinct’s votes into the new districts by area.

How to construct the new map df

Approach A

Ingredients:

  1. Statement of Vote, by SR precinct
  2. SR precinct shapefiles
  3. Proposed map shapefiles

But wait… we have more data

A tale of two precinct types and a block

Census Block

  1. Defined by the census bureau as the smallest geographic unit on which they gather data.
  2. Often a city block but always geographical.
  3. We don’t have voting data but we do have # registered voters.

How can this be used?

Approach B: Proportional allocation

How to construct the new map df

Approach A

Ingredients:

  1. Statement of Vote, by SR precinct
  2. SR precinct shapefiles
  3. Proposed map shapefiles

Approach B

Ingredients:

  1. Statement of Vote, by SR precinct
  2. SR precinct to 2020 Block proportional mapping
  3. Block to new district mapping

Both: the proposed map shapefile, to draw the Results map.

Key Ideas for Today

  1. Interpolation splits by area.
  2. Votes are extensive.
  3. SR precincts have shapes.
  4. Two approaches estimate new-map votes.

For Next Time

  1. Sign up for Quiz 5 (11/2-11/3).
  2. Finish PS 5, due 10/31.
  3. Project Studio 4 is the week of 11/2.