The divide between urban and rural voters has become an increasingly observable pattern in U.S. elections. Many Democratic voters pack into areas with higher population densities. Choropleth maps—where regions are shaded by a variable—often hide this reality because geographic area has little to do with the vote count.
Area cartograms can address this issue by distorting the geography to match the population. Furthermore, cartograms on different variables can present some insights. Below are three different maps of the 2018 midterm U.S. House election results by populations: total population, population of Democratic voters, and population of GOP voters.
How I Made This
I processed the data in R. The House results came from a spreadsheet maintained by David Wasserman & Ally Flinn of Cook Political Report. I also used a table from the U.S. Census to map the Congressional District shapefiles to the results.
library(maps) all_content = readLines("https://docs.google.com/spreadsheets/d/1WxDaxD5az6kdOjJncmGph37z0BPNhV1fNAH_g7IkpC0/gviz/tq?tqx=out:csv&sheet=Sheet1") all_content = all_content[-2] all_content = all_content[-2] results <- read.csv(textConnection(all_content), header = TRUE, stringsAsFactors = FALSE) results$CD.[is.na(results$CD.)]<-0 fips <- read.csv("https://www2.census.gov/geo/docs/reference/state.txt", sep="|") results_fips <- merge(results, fips, by.x="State", by.y="STATE_NAME") results_fips$GEOID <- sprintf("%02d%02d", results_fips$STATE, results_fips$CD.) tail(results_fips[,c("State", "CD.", "Party", "GEOID")])
To visualize this data, I need to use my trusty congressional shape files from the U.S. Census Bureau.
library(cartogram) library(maptools) shape <- sf::st_read(shapefile) shape$STATEFP = as.numeric(shape$STATEFP) shape_data <- merge(shape, results_fips, by="GEOID") shape_data <- shape_data[!is.na(shape_data$State) & shape_data$State != "Alaska" & shape_data$State != "Hawaii",] shape_data$GOP.Votes <- as.numeric(gsub(",", "", shape_data$GOP.Votes)) shape_data$Dem.Votes <- as.numeric(gsub(",", "", shape_data$Dem.Votes))
Sorry, Alaska and Hawaii. Some things are easier without you.
library(topogram) top <- topogram(shape=shape_data, value="Dem.Votes") hpop <- read.csv(popfile) hpop$GEOID <- sprintf("%04d", hpop$GEO.id2) data <- merge(shape_data, hpop, by="GEOID") d <- data[,c("STUSAB", "CD115FP", "Party", "HC01_EST_VC01", "Dem.Votes", "GOP.Votes")] top2 <- topogram(shape=d, value="HC01_EST_VC01") write(top2$x$shape, "images/test.json")
That is all there is to it. The end results look a bit strange (and a bit like Russia according to some observers), but I think they do a good job at showing where each respective party’s voters are located.