Mapping Election Results with R and Choroplethr

[This article was first published on R – AriLamstein.com, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Today’s guest post is by R. Duncan McIntosh. Last week Duncan tweeted about using choroplethr to map the 2016 Florida primary election results. I’ve been wanting to analyze election results in R for some time, and asked Duncan to share with my readers how he did his analysis. This is his reply. 

Election season is providing plenty of data to explore. Today I will demonstrate how to make a choropleth map of recent presidential primary election results in R. The final map we will produce compares the democratic candidates’ percent of total votes by county:

30

The Data

The election results for Florida are made available by Florida Election Watch. Using read.delim(), you can read directly from the tab delimited file online, which allows for a completely reproducible analysis from start to finish, though you might want to also download the file for offline use. Setting the argument strip.white = TRUE removes the problematic white spaces in the CountyNames column.

# Load required packages
library(ggplot2)
library(dplyr)
library(reshape2)
library(choroplethr)
library(choroplethrMaps)
library(gridExtra)
library(knitr)

# Read election results file from the web, and strip the white spaces
fl <- read.delim("http://fldoselectionfiles.elections.myflorida.com/enightfilespublic/20160315_ElecResultsFL.txt", strip.white = T)

Using the dplyr package, I filtered the data frame leaving only one party and selected only the columns I’m interested in. Using the reshape2 package’s dcast function, I then cast the data frame from long to wide format (i.e., with each candidate’s vote counts in a separate column). I also changed the datatype of the CountyName column to facilitate joining it with the county.regions data frame in a later step.

# Filter leaving only one party, and select desired columns
dem <- filter(fl, PartyCode == "DEM") %>% select(CountyName, CanNameLast, CanVotes)

# Cast dem dataframe from long to wide using dcast 
dem_cast <- dcast(dem, CountyName ~ CanNameLast, sum)  # Now we can see each candidate's votes per county
colnames(dem_cast)[3] <- "OMalley" # Remove apostrophe from O'Malley

# Change CountyName column from Factor to lowercase Character  
dem_cast$CountyName <- tolower(as.character(dem_cast$CountyName))  

Then, I created a new column for each county’s total vote count and columns for each candidate’s percentage of those totals.

# Create columns for total votes in each county
dem_cast <- mutate(dem_cast, total = Clinton + OMalley + Sanders)

# Create columns for percentage variables
dem_cast <- mutate(dem_cast, hc = (Clinton/total)*100, bs = (Sanders/total)*100, mo = (OMalley/total)*100)
dem_cast[,6:8] <- round(dem_cast[,6:8], digits = 1)  # Round new variables to 1 decimal place

In order to map these county-level data with the choroplethr package, our data frame needs a column containing each county’s FIPS code. We can get this vector from the county.regions data frame supplied with the choroplethrMaps package. I  filtered the county.regions data frame leaving only Florida counties, then selected the region column and the county.name column while renaming the latter to CountyName to match the analogous column in the  dem_cast data frame. After joining these FIPS codes to our election results dataframe with a left_join(), our data frame is now ready for mapping.

# Read county.regions dataframe supplied by choroplethrMaps package
data("county.regions")

# Filter leaving only florida counties, and select only the 2 needed columns
fl.regions <- filter(county.regions, state.name == "florida") %>% select(region, "CountyName" = county.name)

# Join regions column from fl.regions dataframe to election results dataframe
df <- left_join(dem_cast, fl.regions)  

A table view of counties won by Sanders:

bs.counties <- filter(df, Sanders > Clinton & Sanders > OMalley)
kable(bs.counties, caption = "Counties won by Sanders")
Counties won by Sanders
CountyNameClintonOMalleySanderstotalhcbsmoregion
baker654240805169938.547.414.112003
calhoun437225545120736.245.218.612013
dixie409150459101840.245.114.712029
gilchrist428134578114037.550.711.812041
holmes339239619119728.351.720.012059
lafayette20413636370329.051.619.312067
liberty31612439283238.047.114.912077
suwannee14754751551350142.144.313.612121
union33610747291536.751.611.712125

A table view of counties won by Clinton:

hc.counties <- filter(df, Clinton > Sanders & Clinton > OMalley)
kable(hc.counties, caption = "Counties won by Clinton")
Counties won by Clinton
CountyNameClintonOMalleySanderstotalhcbsmoregion
alachua17777708177303621549.149.02.012001
bay52185714134992352.641.75.812005
bradford1056206908217048.741.89.512007
brevard318621392201005335459.737.72.612009
broward13432819014905418528372.526.51.012011
charlotte812632146361308362.135.42.512015
citrus686555547861220656.239.24.512017
clay53463233699936857.139.53.412019
collier1271939061341924366.131.92.012021
columbia23043721676435252.938.58.512023
desoto988165728188152.538.78.812027
duval595111982272328872567.130.72.212031
escambia1677085393262694962.234.63.212033
flagler61602152980935565.831.92.312035
franklin666104647141747.045.77.312037
gadsden74493541945974876.420.03.612039
glades3877631377649.940.39.812043
gulf568111520119947.443.49.312045
hamilton758148479138554.734.610.712047
hardee53082393100552.739.18.212049
hendry1157104647190860.633.95.512051
hernando894651055491500559.637.03.412053
highlands37152762056604761.434.04.612055
hillsborough6906024023859011005262.835.12.212057
indian river690122839281105762.435.52.112061
jackson28055511842519854.035.410.612063
jefferson1671152762258564.629.55.912065
lake1593269684822511063.433.82.812069
lee279931029156734469562.635.12.312071
leon274011150199304848156.541.12.412073
levy15702151356314150.043.26.812075
madison1548188743247962.430.07.612079
manatee18129696101812900662.535.12.412081
marion1822493498962905462.734.13.212083
martin652627841051090959.837.62.512085
miami-dade12954617564205217335474.724.31.012086
monroe48461723755877355.242.82.012087
nassau29122052062517956.239.84.012089
okaloosa45634283788877952.043.14.912091
okeechobee1152149787208855.237.77.112093
orange6667711483666410448963.835.11.112095
osceola1653343172852424968.230.01.812097
palm beach10379219573953314528271.427.21.312099
pasco217721052145053732958.338.92.812101
pinellas6371621603976710564360.337.62.012103
polk293451715154924655263.033.33.712105
putnam31835112747644149.442.67.912107
santa rosa39414603612801349.245.15.712113
sarasota25896681157934237061.137.31.612115
seminole22089688151123788958.339.91.812117
st. johns973740569561709856.940.72.412109
st. lucie1755959580982625266.930.82.312111
sumter702327230221031768.129.32.612119
taylor987251908214646.042.311.712123
volusia263101174161824366660.337.12.712127
wakulla16593091424339248.942.09.112129
walton15151581365303849.944.95.212131
washington858182781182147.142.910.012133

 

O’Malley did not win any counties.

Mapping with Choroplethr

To create choropleth maps, choroplethr requires:

A data.frame with a column named “region” and a column named “value”. Elements in the “region” column must exactly match how regions are named in the “region” column in ?country.map.

We have joined the regions directly from the county.map data frame, now we just need to add a column named value and assign it to equal the column we want to map. I do this with one line of base R immediately preceding each call of the county_choropleth() function. Below, I mapped each candidate’s percent of total vote by county in three separate maps, then all three in a row.

# For each candidate, map the percent of each counties' total vote using choroplethr package 
df$value = df$bs  # Set the desired 'value' column for choroplethr
choro_bs = county_choropleth(df, state_zoom="florida", legend = "%", num_colors=1) + 
  ggtitle("Bernie Sanders") +
  coord_map()  # Adds a Mercator projection
choro_bs

df$value = df$hc  # Set the desired 'value' column for choroplethr
choro_hc = county_choropleth(df, state_zoom="florida", legend = "%", num_colors=1) + 
  ggtitle("Hillary Clinton") +
  coord_map()
choro_hc

hc

df$value = df$mo  # Set the desired 'value' column for choroplethr
choro_mo = county_choropleth(df, state_zoom="florida", legend = "%", num_colors=1) + 
  ggtitle("Martin O'Malley") +
  coord_map()
choro_mo

mo

# Plot all three maps in a grid
grid.arrange(choro_hc, choro_bs, choro_mo, ncol=3, top = "Florida Democratic Primary 2016n Percent of Total Votes by Countyn ")

Highlight Counties

In this post, Ari shared a function for highlighting a county. Here, it’s applied to our first map:

 
# Function for highlighting a county
highlight_county = function(county_fips)
{
 library(choroplethrMaps)
 data(county.map, package="choroplethrMaps", envir=environment())
 df = county.map[county.map$region %in% county_fips, ]
 geom_polygon(data=df, aes(long, lat, group = group), color = "yellow", fill = NA, size = 0.5)
}
# Filter counties won by Sanders
bs.counties <- filter(df, Sanders > Clinton & Sanders > OMalley)

# Create list of counties won
bs.fips <- bs.counties[[9]]

# Map using the highlight_county() function after calling county_choropleth()
df$value = df$bs  # Set the desired 'value' column for choroplethr
choro_bs = county_choropleth(df, state_zoom="florida", legend = "%", num_colors=1) + 
  highlight_county(bs.fips) +  # Highlight counties won
  ggtitle("Bernie Sanders") +
  coord_map()  # Adds a Mercator projection
choro_bs

bs

 

Update

Ari asked if I’d add a map showing who won each county:

# Add a new column to show each county's winner
df$winner <- as.factor(ifelse(df$hc > df$bs, "Clinton", "Sanders"))

# Plot of winner by county
df$value = df$winner  # Set the desired 'value' column for choroplethr choro_winner = county_choropleth(df, state_zoom="florida", legend = "Winner", num_colors=2) +   ggtitle("Florida Presidential Primaryn 15 March 2016") +   coord_map() choro_winner
winner

The post Mapping Election Results with R and Choroplethr appeared first on AriLamstein.com.

To leave a comment for the author, please follow the link and comment on their blog: R – AriLamstein.com.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)