Maps

# Initial packages required (we'll be adding more)
library(tidyverse)
library(datasets)
library(fec16)
library(ggplot2)
library(tidycensus)
library(dplyr)
library(sf)
library(ggspatial)
library(mdsr)      # package associated with our MDSR book
library(maps)
us_states <- map_data("state")
head(us_states)
       long      lat group order  region subregion
1 -87.46201 30.38968     1     1 alabama      <NA>
2 -87.48493 30.37249     1     2 alabama      <NA>
3 -87.52503 30.37249     1     3 alabama      <NA>
4 -87.53076 30.33239     1     4 alabama      <NA>
5 -87.57087 30.32665     1     5 alabama      <NA>
6 -87.58806 30.32665     1     6 alabama      <NA>
StatePopulation <- read.csv("https://raw.githubusercontent.com/ds4stats/r-tutorials/master/intro-maps/data/StatePopulation.csv", as.is = TRUE)
us_rent_income |>
  filter(variable == "income") |>
  mutate(name = str_to_lower(NAME)) |>
  select(-NAME) |>
  right_join(us_states, by = c("name" = "region")) |>
  ggplot(mapping = aes(x = long, y = lat,
                          group = group)) + 
  geom_polygon(aes(fill = estimate), color = "black") +
  labs(title = "Average 2017 Income by State", fill = "Yearly Income (in USD)") +
  coord_map() +
  theme_mdsr()

We can see that states like Maryland and Washington had a greater yearly income for 2017 while states like Alabama and West Virginia were not as fortuitous. It’s interesting how states like Maryland and Virginia are doing well while their neighbors are all on the other end of the spectrum. One other thing to note is that the Southeast US tended to earn less than most other regions.

# summary of the 8 congressional Wisconsin districts and the 2016 voting
district_elections <- results_house |>
  mutate(district = parse_number(district_id)) |>
  group_by(state, district) |>
  summarize(
    N = n(), 
    total_votes = sum(general_votes, na.rm = TRUE),
    d_votes = sum(ifelse(party == "DEM", general_votes, 0), na.rm = TRUE),
    #we add together all the votes for democrats in the district
    r_votes = sum(ifelse(party == "REP", general_votes, 0), na.rm = TRUE),
    #same but with only republicans
    .groups = "drop"
  ) |>
  mutate(
    other_votes = total_votes - d_votes - r_votes,
    r_prop = r_votes / total_votes,  
    winner = ifelse(r_votes > d_votes, "Republican", "Democrat")
  )

wi_results <- district_elections |>
  filter(state == "WI")
wi_results |>                  
  select(-state)
# A tibble: 8 × 8
  district     N total_votes d_votes r_votes other_votes r_prop winner    
     <dbl> <int>       <dbl>   <dbl>   <dbl>       <dbl>  <dbl> <chr>     
1        1     7      353990  107003  230072       16915  0.650 Republican
2        2     2      397581  273537  124044           0  0.312 Democrat  
3        3     2      257401  257401       0           0  0     Democrat  
4        4     4      285858  220181       0       65677  0     Democrat  
5        5     3      390507  114477  260706       15324  0.668 Republican
6        6     4      356935  133072  204147       19716  0.572 Republican
7        7     4      362061  138643  223418           0  0.617 Republican
8        8     4      363574  135682  227892           0  0.627 Republican
# Download congressional district shapefiles 
options(timeout = 200)
src <- "http://cdmaps.polisci.ucla.edu/shp/districts113.zip"
lcl_zip <- fs::path(tempdir(), "districts113.zip")
download.file(src, destfile = lcl_zip)
lcl_districts <- fs::path(tempdir(), "districts113")
unzip(lcl_zip, exdir = lcl_districts)
dsn_districts <- fs::path(lcl_districts, "districtShapes")


# read shapefiles into R as an sf object
st_layers(dsn_districts)
Driver: ESRI Shapefile 
Available layers:
    layer_name geometry_type features fields crs_name
1 districts113       Polygon      436     15    NAD83
# be able to read as a data frame as well
districts <- st_read(dsn_districts, layer = "districts113") |>
  mutate(DISTRICT = parse_number(as.character(DISTRICT)))
Reading layer `districts113' from data source 
  `C:\Users\colec\AppData\Local\Temp\RtmpS4U6ua\districts113\districtShapes' 
  using driver `ESRI Shapefile'
Simple feature collection with 436 features and 15 fields (with 1 geometry empty)
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -179.1473 ymin: 18.91383 xmax: 179.7785 ymax: 71.35256
Geodetic CRS:  NAD83
head(districts, width = Inf)
Simple feature collection with 6 features and 15 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -91.82307 ymin: 29.41135 xmax: -66.94983 ymax: 47.45969
Geodetic CRS:  NAD83
  STATENAME           ID DISTRICT STARTCONG ENDCONG DISTRICTSI COUNTY PAGE  LAW
1 Louisiana 022113114006        6       113     114       <NA>   <NA> <NA> <NA>
2     Maine 023113114001        1       113     114       <NA>   <NA> <NA> <NA>
3     Maine 023113114002        2       113     114       <NA>   <NA> <NA> <NA>
4  Maryland 024113114001        1       113     114       <NA>   <NA> <NA> <NA>
5  Maryland 024113114002        2       113     114       <NA>   <NA> <NA> <NA>
6  Maryland 024113114003        3       113     114       <NA>   <NA> <NA> <NA>
  NOTE BESTDEC                  FINALNOTE RNOTE                 LASTCHANGE
1 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
2 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
3 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
4 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
5 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
6 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
  FROMCOUNTY                       geometry
1          F MULTIPOLYGON (((-91.82288 3...
2          F MULTIPOLYGON (((-70.98905 4...
3          F MULTIPOLYGON (((-71.08216 4...
4          F MULTIPOLYGON (((-77.31156 3...
5          F MULTIPOLYGON (((-76.8763 39...
6          F MULTIPOLYGON (((-77.15622 3...
class(districts)
[1] "sf"         "data.frame"
#####################################
# create basic plot with Wisconsin congressional districts
wi_shp <- districts |>
  filter(STATENAME == "Wisconsin")
wi_shp |>
  st_geometry() |>
  plot(col = gray.colors(nrow(wi_shp)))

wi_merged <- wi_shp |>
  st_transform(4326) |>
  inner_join(wi_results, by = c("DISTRICT" = "district"))
head(wi_merged, width = Inf)
Simple feature collection with 6 features and 23 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -92.808 ymin: 42.49198 xmax: -87.50719 ymax: 45.20957
Geodetic CRS:  WGS 84
  STATENAME           ID DISTRICT STARTCONG ENDCONG DISTRICTSI COUNTY PAGE  LAW
1 Wisconsin 055113114001        1       113     114       <NA>   <NA> <NA> <NA>
2 Wisconsin 055113114002        2       113     114       <NA>   <NA> <NA> <NA>
3 Wisconsin 055113114003        3       113     114       <NA>   <NA> <NA> <NA>
4 Wisconsin 055113114004        4       113     114       <NA>   <NA> <NA> <NA>
5 Wisconsin 055113114005        5       113     114       <NA>   <NA> <NA> <NA>
6 Wisconsin 055113114006        6       113     114       <NA>   <NA> <NA> <NA>
  NOTE BESTDEC                  FINALNOTE RNOTE                 LASTCHANGE
1 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
2 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
3 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
4 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
5 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
6 <NA>    <NA> {"From US Census website"}  <NA> 2016-05-29 16:44:10.857626
  FROMCOUNTY state N total_votes d_votes r_votes other_votes    r_prop
1          F    WI 7      353990  107003  230072       16915 0.6499393
2          F    WI 2      397581  273537  124044           0 0.3119968
3          F    WI 2      257401  257401       0           0 0.0000000
4          F    WI 4      285858  220181       0       65677 0.0000000
5          F    WI 3      390507  114477  260706       15324 0.6676090
6          F    WI 4      356935  133072  204147       19716 0.5719445
      winner                       geometry
1 Republican MULTIPOLYGON (((-89.08072 4...
2   Democrat MULTIPOLYGON (((-90.43 43.1...
3   Democrat MULTIPOLYGON (((-91.3984 44...
4   Democrat MULTIPOLYGON (((-88.06601 4...
5 Republican MULTIPOLYGON (((-89.01359 4...
6 Republican MULTIPOLYGON (((-89.78555 4...
# Color based on winning party
#   Note that geom_sf is part of ggplot2 package, while st_geometry is
#   part of sf package
wi <- ggplot(data = wi_merged, aes(fill = winner)) +
  annotation_map_tile(zoom = 6, type = "osm", progress = "none") + 
  geom_sf(alpha = 0.5) +
  scale_fill_manual("Winning Party", values = c("blue", "red")) + 
  geom_sf_label(aes(label = DISTRICT), fill = "white") + 
  theme_void() +
  labs(title = "Winners of Wisconsin's Eight Congressional Districts cir. 2016")
wi
Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
give correct results for longitude/latitude data
Loading required namespace: raster

# Color based on proportion Rep.  Be sure to let limits so centered at 0.5.
# This is a choropleth map, where meaningful shading relates to some attribute
wi +
  aes(fill = r_prop) + 
  scale_fill_distiller(
    "Proportion\nRepublican", 
    palette = "RdBu", 
    limits = c(0, 1)
  )
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
give correct results for longitude/latitude data

From this map, we can see that quite a few districts (like 2, 3, and 4) definitely contain a strong democratic population, while the districts that are more in the middle (like 1, 7 and 8) are just ever so slightly right leaning. For a party wanting to gerrymander, you would need to group all your opponent’s voters in a select few borders so that their votes go towards an overwhelming victory in those areas, while yours are spread around so that you use your total voters strategically, just barely pulling off a win. With this map, I can definitely understand the argument that Republicans have lopsided control of the state, since it seems like what few Democratic victories there are have very little Republican pushback.