I am looking to ultimately render a map in Leaflet that shows the roll call voting results for a specific vote from the Senate. This obviously involves coloring a state polygon based on the unique combination of the Senator's party affiliation and how they voted (2 senators per state). The problem I have is developing a workflow to color code a state (here I am using a simple sf dataframe of the US states) in this manner. The idea would be to "stripe" the state in two different colors based on each of the Senator's party affiliation and vote type.
Below is a workflow that has already been created for viewing roll call voting results by congressional districts (not what I want, I want to do this for Senate voting), but I figured this would be a starting point or baseline for hoping to create a similar map for a roll call vote from the Senate. This code can be found at https://www.r-bloggers.com/2020/09/mapping-congressional-roll-calls/. The only thing different is that I provided a function that I found on another website to directly read a congressional district shapefile from the website where they are housed courtesy of the UCLA Political Science Department:
# Workflow for mapping congressional district roll call voting results
library(Rvoteview)
library(tidyverse)
devtools::install_github("jaytimm/wnomadds")
library(wnomadds)
library(sf)
library(tigris)
# Function to download a shapefile for any congressional district of your choice.
get_congress_map <- function(cong=113) {
tmp_file <- tempfile()
tmp_dir <- tempdir()
zp <- sprintf("http://cdmaps.polisci.ucla.edu/shp/districts%03i.zip",cong)
download.file(zp, tmp_file)
unzip(zipfile = tmp_file, exdir = tmp_dir)
fpath <- paste(tmp_dir, sprintf("districtShapes/districts%03i.shp",cong), sep = "/")
st_read(fpath)
}
# Get the shapefile for the 89th congress
cd89 <- get_congress_map(cong = 89)
options(tigris_use_cache = TRUE, tigris_class = "sf")
# List the FIPS for US territories (and Alaska and Hawaii) that we won't include in maps.
nonx <- c('78', '69', '66', '72', '60', '15', '02')
# Create a simple states dataframe
states <- tigris::states(cb = TRUE) %>%
data.frame() %>%
select(STATEFP, STUSPS) %>%
rename(state_abbrev = STUSPS)
# Join the congressional districts shapefile with the simple states dataframe we
# created above.
cd_sf <- cd89 %>%
mutate(STATEFP = substr(ID, 2, 3),
district_code = as.numeric(substr(ID, 11, 12))) %>%
left_join(states, by = "STATEFP") %>%
filter(!STATEFP %in% nonx) %>%
select(STATEFP, state_abbrev, district_code)
# Download rollcall data from the Voteview database. Here for the Voting
# Rights Act of 1965
vra <- Rvoteview::voteview_search('("VOTING RIGHTS ACT OF 1965") AND (congress:89)
AND (chamber:house)') %>%
filter( date == '1965-07-09') %>%
janitor::clean_names()
votes <- Rvoteview::voteview_download(vra$id)
names(votes) <- gsub('\\.', '_', names(votes))
# Restructure the roll call voting data stored in votes
big_votes <- votes$legis_long_dynamic %>%
left_join(votes$votes_long, by = c("id", "icpsr")) %>%
filter(!grepl('POTUS', cqlabel)) %>%
group_by(state_abbrev) %>%
mutate(n = length(district_code)) %>%
ungroup() %>%
mutate(avote = case_when(vote %in% c(1:3) ~ 'Yea',
vote %in% c(4:6) ~ 'Nay',
vote %in% c(7:9) ~ 'Not Voting'),
party_code = case_when(party_code == 100 ~ 'Dem',
party_code == 200 ~ 'Rep' ),
Party_Member_Vote = paste0(party_code, ': ', avote),
## fix at-large --
district_code = ifelse(district_code %in% c(98, 99), 0, district_code),
district_code = ifelse(n == 1 & district_code == 1, 0, district_code),
district_code = as.integer(district_code)) %>%
select(-n)
#Members who represent historical “at-large” districts are
##assigned 99, 98, or 1 in various circumstances. Per VoteView.
# Make the Party_Member_Vote variable a factor and change the order of its levels.
big_votes$Party_Member_Vote <- factor(big_votes$Party_Member_Vote)
big_votes$Party_Member_Vote <-
factor(big_votes$Party_Member_Vote,
levels(big_votes$Party_Member_Vote)[c(3,6,1,4,2,5)])
# Join the roll call voting data with the shapefile and plot.
cd_sf_w_rolls <- cd_sf %>%
left_join(big_votes, by = c("state_abbrev", "district_code"))
main1 <- cd_sf_w_rolls %>%
ggplot() +
geom_sf(aes(fill = Party_Member_Vote),
color = 'white',
size = .25) +
wnomadds::scale_fill_rollcall() +
theme_minimal() +
theme(axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.title.y=element_blank(),
axis.text.y=element_blank(),
legend.position = 'none') # +
main1 + ggtitle(vra$short_description)
This is fine for mapping congressional districts on roll call votes by the house. I am trying to figure out a way to reproduce a similar map for senate roll call votes. So, I started with the same workflow and am not sure how to proceed further or if it is even possible:
# Now I want to make a similar map for the senators of each state, not
# the representatives.
# I want to include Hawaii and Alaska in my Senate maps, so remove those FIPS
# from the vector.
non_states <- c('78', '69', '66', '72', '60', '11')
# No congressional district shapefile is therefore needed. So, here I just make
# a simple sf dataframe for the US States. Set the coordinate reference system to
# 4326 (World Geodetic System 1984) because I want to render the map in Leaflet
# and that's the reference system Leaflet uses.
states_Senate <- tigris::states(cb = TRUE) %>%
st_as_sf(crs = 4326) %>%
select(STATEFP, STUSPS, geometry) %>%
filter(!STATEFP %in% non_states) %>%
rename(state_abbrev = STUSPS)
# Query a roll call vote in the Voteview database. Any vote will work, here
# a vote related to marketing of non-prescription drugs in the 116th congress in the
# Senate now, not the House.
vra2 <- Rvoteview::voteview_search('("A bill to amend the Federal Food, Drug, and Cosmetic Act")
AND (congress:116) AND (chamber:senate)') %>%
janitor::clean_names()
votes2 <- Rvoteview::voteview_download(vra2$id)
names(votes2) <- gsub('\\.', '_', names(votes2))
# Restructure the roll call voting data stored in votes2
big_votes2 <- votes2$legis_long_dynamic %>%
left_join(votes2$votes_long, by = c("id", "icpsr")) %>%
filter(!grepl('POTUS', cqlabel)) %>%
mutate(avote = case_when(vote %in% c(1:3) ~ 'Yea',
vote %in% c(4:6) ~ 'Nay',
vote %in% c(7:9) ~ 'Not Voting'),
party_code = case_when(party_code == 100 ~ 'Dem',
party_code == 200 ~ 'Rep' ),
Party_Member_Vote = paste0(party_code, ': ', avote))
# Now I have a dataframe, big_votes2 that has 2 rows for each state. I need to figure
# out how to color the polygons for each state based on the unique combination of
# party affiliation and vote cast.
# Make Party_Member_Vote a factor like the congressional district workflow above,
# join big_votes2 with states_Senate sf dataframe, and plot........finishing this
# workflow and making a Senate map is essentially my question.
My hope is make a final map that looks similiar to the following (found at https://voteview.com/rollcall/RS1160389), which is the resulting roll call vote for the example query I provide in the script of my workflow for creating a senate map directly above (the roll call vote about non-prescription drugs). This is probably done in Javascript, maybe D3, but I am working on an R Shiny app looking at roll call voting, so I am strictly looking to do this in R.
Here the state polygons are "striped" by the senator's party and how they voted. If the senators in a state are both one party and vote in unison, the state is a solid color reflecting this. The color palette is based off the voteview_pal
provided in the wnomadds
package. The colors in this palette don't include senators that consider themselves independent, but I can update the palette if there is a solution to creating the striping pattern within the state polygons. In my use of R I can't think of a way to accomplish this, since color fills are based on unique levels of a factor variable and here we have to rows per state, as the dataframe is being created in this workflow. Additionally, I've never seen a pattern or stripe fill in ggplot
that could accomplish this even if the dataframe was arranged in a way that there were only 1 row/observation per state. If this is even possible, I would want to render this in Leaflet, but if the basic concepts can be accomplished by plotting the sf object in ggplot
I would gladly start there. Any help would appreciated.