The politics of New Mexico: a brief historical-visual account

[This article was first published on Jason Timm, 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.

In this post, we piece together a brief political history of New Mexico using a host of data sources, including Wikipedia, the US Census, the New Mexico State Legislature (NMSL), VoteView, and the National Conference of State Legislatures. A bit of a show/tell post, and one that piggybacks some on a guide I have developed for working with US political data using R.

Here, we focus on the political leanings of voters in New Mexico as attested by who they have backed historically in presidential elections, who they have sent to the US Congress, and who they have elected as representation in the state legislature & governorship. As we will see, a curious history since statehood in 1912.

In the process, we demonstrate some methods for accessing/cleaning online data sets made available in a variety formats. Many of these data (especially those made available by the NMSL) have not really seen the light of day; so, we let these data breathe some. Fully reproducible. Open methods. Open government.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(tidyverse, tigris, nmelectiondatr) 

#devtools::install_github("jaytimm/nmelectiondatr")
options(tigris_use_cache = TRUE, tigris_class = "sf")

New Mexico demographics via tidycensus

We first take a quick look at some socio-demographic indicators in New Mexico (relative to other states in the Union) using the tidycensus package. The violin plots below summarize percentages of the population that are Hispanic (Per_Hispanic), White (Per_White), living below the poverty line (Per_BPL), have a Bachelor’s degree or higher (Per_Bachelors_Plus), and have a high school degree or higher (Per_HS_Plus). Also included are median household incomes (Median_HH_Income).

vars <- c(Per_Hispanic = 'DP05_0071P',
          Per_Bachelors_Plus = 'DP02_0067P', 
          Per_BPL = 'DP03_0128P', 
          Per_White = 'DP05_0077P',
          Per_HS_Plus = 'DP02_0066P',
          Median_HH_Income = 'DP03_0062')

m90 <- tidycensus::get_acs(geography = "state", 
                           variables = vars, 
                           year = 2017,
                           output = "tidy", 
                           survey = 'acs1') %>%
  filter(!GEOID %in% c('11', '72'))

So, New Mexico is browner, less financially heeled, and less educated relative to other states in the USA. A very simple overview of a fairly complicated state.

labs <- m90 %>%
  filter(NAME == 'New Mexico')

m90 %>%
  ggplot(aes(x =1, y = estimate)) +
  geom_violin()+
  geom_point() +
  ggrepel::geom_label_repel(data = labs, 
                           aes(x = 1, y = estimate, 
                               label = 'NM'),
            color = 'steelblue',
            nudge_x = .01)+
  theme_minimal()+
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        legend.position = "none") +
  facet_wrap(~variable, scales = 'free') +
  labs(title="Some socio-demographics", 
       caption = 'American Community Survey, 1-Year Estimates, 2017.') 

2016 Presidential Election

To get our bearings, we briefly consider how New Mexico voted in the 2016 presidential election. While Hillary Clinton carried the state by roughly eight points, here we investigate election results at the precinct level. My nmelectiondatr package (available on GitHub) makes available election returns in New Mexico for state & federal elections from 2014 – 2018. These data live on the New Mexico Legislature website as fairly inaccessible spreadsheets. I have cleaned things up some, and collated returns as simple data tables. For non-R users, data also live as simple csv/excel files.

Here, we access precinct-level returns for the 2016 presidential election.

precincts_raw <- #nmelectiondatr::nmel_pol_geos$nm_precincts %>%
  nmelectiondatr::nmel_results_precinct %>%
               filter(Type == 'President and Vice President of the United States' ) %>% #& 
  group_by(County_Name, Precinct_Num) %>%
  mutate(per = round(Votes/sum(Votes), 3)) %>%
  select(County_Name, Precinct_Num, Party, per) %>%
  filter(Party %in% c('DEM', 'REP')) %>%
  spread(Party, per) %>%
  mutate(Trump_Margin = REP - DEM)
base <- nmelectiondatr::nmel_pol_geos$nm_precincts %>%
  inner_join(precincts_raw) %>%
  ggplot() + 
  geom_sf(aes(fill = cut_width(Trump_Margin, 0.2)),
           color = 'darkgray') +
  scale_fill_brewer(palette = 'RdBu', direction = -1, name = 'Margin')

The map below summarizes Trump vote margins by precinct in New Mexico. So, an airplane-red state, much like the country as a whole, with larger, more rural precincts dominating the map.

base +
  ggsflabel::geom_sf_text_repel(data = nmel_pol_geos$nm_places %>% 
                                filter (LSAD == '25'),
                                aes(label = NAME), size = 2.5)  +
  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 = 'right') +
  labs(title = "2016 Trump margins by precinct")

When we zoom in some to New Mexico’s four largest metro areas, the state becomes a bit blue-er. Rio Rancho is the friendliest to 45 – where Trump held a rally in mid-September.

Presidential elections in New Mexico historically

A blue state in 2016, next we consider how New Mexico has voted in presidential elections since its statehood in 1912. We first grab a simple list of US presidents and their party affiliations via Git Hub.

url1 <- 'https://gist.githubusercontent.com/namuol/2657233/raw/74135b2637e624848c163759be9cd14ae33f5153/presidents.csv'

us_pres <- read.csv(url(url1)) %>%
  #select(Year, Party) %>%
  mutate(Party = trimws(Party),
         Party = gsub('/.*$', '', Party),
         year = as.numeric(gsub('^.*/', '', Took.office))-1,
         President = gsub(' \\(.*$', '', President)) %>%
  select(year, President, Party) %>%
  mutate(Party = gsub('Democratic', 'Democrat', Party)) %>%
  bind_rows(data.frame(year = 2016,
                       President = 'Donald Trump',
                       Party = 'Republican'))

Then we access New Mexico’s presidential election voting history via Wikipedia.

url <- 'https://en.wikipedia.org/wiki/United_States_presidential_elections_in_New_Mexico'

nm_returns <- url %>%
  xml2::read_html() %>%
  rvest::html_node(xpath = '//*[@id="mw-content-text"]/div/table[2]') %>%
  rvest::html_table(fill = TRUE)

nm_returns <- nm_returns[,c(1:2, 4:5, 7)]
colnames(nm_returns) <- c('year', 'winner', 'winner_per', 'loser', 'loser_per')

nm_returns1 <- nm_returns %>%
  mutate(state_winner = ifelse(winner_per < loser_per, loser, winner))%>%
  rowwise() %>%
  mutate(other = round(100 - sum(winner_per, loser_per), 2)) %>%
  left_join(us_pres %>% select(-year), by = c('winner' = 'President')) 

wins <- nm_returns1 %>%
  select(year, state_winner, Party, winner_per)%>%
  rename(per = winner_per)

loss <- nm_returns1 %>%
  select(year, state_winner, Party, loser_per)%>%
  mutate(Party = ifelse(Party == 'Democrat', 'Republican', 'Democrat')) %>%
  rename(per = loser_per)

others <- nm_returns1 %>%
  select(year, state_winner, Party, other)%>%
  rename(per = other)%>%
  mutate(Party = 'Other')

new <- bind_rows(wins, loss, others)
new$Party <- factor(new$Party, levels = c('Other', 'Democrat', 'Republican')) 

Based on these data, the plot below summarizes historical election results by party affiliation. Labeled are the candidates that won New Mexico. The gray portions of the plot reflect vote shares for “other”/ non-predominant political parties.

flip_dets <- c('Other', 'Democrat', 'Republican')
flip_pal <- c('#b0bcc1', '#395f81', '#9e5055')
names(flip_pal) <- flip_dets

dems <- new %>%
  group_by(year) %>%
  filter(per == max(per)) %>%
  filter(Party == 'Democrat')

pres_labels <- new %>%
  group_by(year) %>%
  filter(Party == 'Democrat') %>%
  mutate(percent1 = ifelse(state_winner %in% dems$state_winner,
                           per + 7, per - 7),
         state_winner = toupper(sub('^.* ', '', state_winner)))

new %>%
  ggplot(aes(x=year, y=per, fill = Party))+
  geom_bar(alpha = 0.85, color = 'white', stat = 'identity') +
  annotate(geom="text", 
           x = pres_labels$year, 
           y = pres_labels$percent1, 
           label = pres_labels$state_winner,
           size = 3, angle = 90, color = 'white')+
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  #geom_hline(yintercept = 50, color = 'white', linetype = 2) +
  theme(legend.position = "none")+
  #guides(fill = guide_legend(reverse=TRUE))+
  scale_fill_manual(values = flip_pal) +
  #ggthemes::scale_fill_stata()+
  scale_x_continuous(breaks=seq(1912,2016,4)) + xlab('') +
  ggtitle('Presidential election results in New Mexico')

Margins historically

For a slightly different perspective, we consider Republican-Democrat vote margins historically. As the plot below attests, a state that swings quite a bit. However, more recently having settled some as blue post-Bush v2.

new %>%
  select(-state_winner) %>%
  spread(Party, per) %>%
  mutate(margin = Republican - Democrat,
         Party = ifelse(margin > 0, 'Republican', 'Democrat')) %>%
  
  ggplot(aes(x=year, y=margin, fill = Party))+
  geom_bar(alpha = 0.85, color = 'white', stat = 'identity') +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  theme(legend.position = "none")+
  ggthemes::scale_fill_stata()+
  scale_x_continuous(breaks=seq(1912,2016,4)) + xlab('') +
  ggtitle('Presidential vote margins in New Mexico since 1912')

New Mexico as bellwether?

While New Mexico got Clinton wrong in 2016, the state seems a fairly consistent bellwether of presidential winners historically, supporting JKF, Ronald Reagan, and Barack Obama alike. Here, then, we consider how New Mexico stacks up against other states in the Union in terms of voting for the winning presidential candidate.

Below, we extract voting histories for all US states from Wikipedia. Tables are mostly uniform across states (eg, New Mexico). California & Pennsylvania tables, eg, are structured a bit differently, and require some individual tweaking.

base_url <- 'https://en.wikipedia.org/wiki/United_States_presidential_elections_in_'

states <- uspoliticalextras::uspol_csusa_senate_bios %>%
  filter(congress == 116) %>%
  select(state_fips:state_abbrev)%>%
  distinct() %>%
  filter(!state %in%  c('Pennsylvania',  'California'))%>%
  mutate(which_table = ifelse(state %in% c('New York', 'Missouri'), 3, 2))

states_correct <- list()
for (i in 1:nrow(states)) {
  states_correct[[i]] <- 
    paste0(base_url, states$state[i]) %>%
    xml2::read_html() %>%
    rvest::html_node(xpath = paste0('//*[@id="mw-content-text"]/div/table[', 
                                    states$which_table[i],']')) %>%
    rvest::html_table(fill = TRUE) 
  
  states_correct[[i]] <- states_correct[[i]][,c(1:2, 4:5, 7)] 
  colnames(states_correct[[i]]) <- c('year', 'winner', 'winner_per', 'loser', 'loser_per')

  states_correct[[i]] <- states_correct[[i]] %>%
    mutate(winner_per = as.numeric(gsub('^$|-|%', 0, winner_per)),
           loser_per = as.numeric(gsub('^$|-|%', 0, loser_per)),
           state_winner = ifelse(winner_per < loser_per, loser, winner),
           correct = ifelse(state_winner == winner, 'correct', 'incorrect'),
           correct = ifelse(winner_per == 'n\a', NA, correct),
           year = as.integer(gsub("\\D+", "", year)),
           year = substr(year, 1,4))}

names(states_correct) <- states$state

states_correct1 <- states_correct %>%
  bind_rows(.id = 'state') 

The table below summarizes how states have fared in predicting presidential election winners since 1912 – a total of 27 elections. New Mexico, then is tied for second with Missouri. Nevada and Ohio voters have only missed two winners since 1912.

correct <- states_correct1  %>%
  select(state, year, correct) %>%
  mutate(year = as.integer(year)) %>%
  bind_rows(returns1, returns_ca) %>%
  filter(year >  1911) %>%
  group_by(state, correct) %>%
  summarize(n = n()) %>%
  filter(!is.na(correct))%>%
  spread(correct, n) %>%
  ungroup() %>%
  rowwise() %>%
  mutate(per_correct = round(correct/sum(correct, incorrect), 3))

correct %>%
  arrange(desc(per_correct))%>%
  DT::datatable(rownames = FALSE) %>%
  DT::formatStyle('per_correct',
    background = DT::styleColorBar(range(correct[4]), 'lightblue'),
    backgroundSize = '80% 70%',
    backgroundRepeat = 'no-repeat',
    backgroundPosition = 'right')

Elections New Mexico got wrong

The table below summarizes results for elections that New Mexico, Ohio, Missouri, and Nevada got wrong since 1912. So, Ohio has been on spot since 1960; Missouri has (seemingly) gone full red. Also of note: 2/3 presidential nominees that New Mexico got wrong won the popular vote nationally, ie, Al Gore & HR Clinton.

states_correct1 %>%
  filter(state %in% c('Ohio', 'Nevada', 'New Mexico', 'Missouri') &
           correct == 'incorrect' & year > 1911) %>%
  select(state:loser_per) %>%
  knitr::kable()
state year winner winner_per loser loser_per
Missouri 2012 Barack Obama 44.38 Mitt Romney 53.76
Missouri 2008 Barack Obama 49.29 John McCain 49.43
Missouri 1956 Dwight D. Eisenhower 49.89 Adlai Stevenson II 50.11
Nevada 2016 Donald Trump 45.50 Hillary Clinton 47.92
Nevada 1976 Jimmy Carter 45.81 Gerald Ford 50.17
New Mexico 2016 Donald Trump 40.04 Hillary Clinton 48.26
New Mexico 2000 George W. Bush 47.85 Al Gore 47.91
New Mexico 1976 Jimmy Carter 48.28 Gerald Ford 50.75
Ohio 1960 John F. Kennedy (D) 46.72 Richard Nixon (R) 53.28
Ohio 1944 Franklin D. Roosevelt (D) 49.82 Thomas E. Dewey (R) 50.18

The map below illustrates state ranks for voting with the presidential winner since 1912 (based on table above). States in darker blue are better bellwethers. So, the Southwest is generally quite good, while the South & New England less so.

correct_labels <- correct %>%
  ungroup() %>%
  arrange(desc(correct)) %>%
  mutate(rank = dplyr::dense_rank(desc(correct)))

out <- uspoliticalextras::uspol_dvos_equalarea_sf$tile_outer %>%
  left_join(correct_labels) 

inner <- uspoliticalextras::uspol_dvos_equalarea_sf$tile_inner %>%
  left_join(correct_labels) %>%
  mutate(rank_label = paste0(state_abbrev, '\n', rank))

out %>%
  ggplot() +   
    geom_sf(aes(fill = rank),
            color = 'black') + 
  ggsflabel::geom_sf_text(data = inner,
                          aes(label = rank_label), 
                          size = 3.5,
                          color = 'black') +
  theme_minimal() +
  scale_fill_gradient(low="#2c7bb6", high="#ffffbf")+
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        legend.title=element_blank(),
        legend.position = 'none') +

  labs(title = "Voting with winning candidate since 1912 by rank")

Congressional delegation historically

Next, we consider the composition of New Mexico’s congressional delegation historically. Here we access data made available via the VoteView project and the R package Rvoteview.

House of Representatives

The table below details the names & party affiliations of the (3) representatives New Mexico has sent to Washington over the last 15 congresses. District 3, which is comprised of the northern half of the state and includes the Santa Fe metro, has generally gone blue during this time period. District 1 (the ABQ metro area) has flipped from red to blue since the election of Obama in 2008. District 2 (the southern half of the sate) has been a GOP stronghold, with the 111th and 116th congresses being exceptions.

js <- "(/Rep/).test(value) ? '#fcdbc7' : (/Dem/).test(value) ? '#d1e6ef' : ''"

dat <- Rvoteview:: member_search(chamber= 'House', 
                                 state = 'NM', 
                                 congress = 102:116) %>%
  mutate(bioname = gsub('\\(Tom\\)', '', bioname),
         bioname = ifelse(party_name == 'Democratic Party',
                          paste0(bioname, ' (Dem)'),
                          paste0(bioname, ' (Rep)'))) %>%
  select(congress, bioname, district_code) %>%
  group_by(congress, district_code)%>%
  slice(1) %>%
  ungroup() %>%
  spread(district_code, bioname)

dat %>%
  DT::datatable(rownames = FALSE,
                options =  list(pageLength = 15, 
                                  dom = 't')) %>% 
  DT::formatStyle(1:ncol(dat), backgroundColor = htmlwidgets::JS(js))

So, 2018 (the 116th) was only the second time in the last thirty years that New Mexico elected an all-Democrat delegation to the House. See this post for some thoughts on how Torres Small carried New Mexico’s second district in 2018.

US Senate

Next we consider the political affiliations & ideologies of US Senators from New Mexico since 1947. I have discussed VoteView’s political ideology scores in previous posts (eg), and have also demonstrated their derivation using roll call data from New Mexico’s 53rd State Legislature as an example.

Here we utilize Nokken-Poole political ideology scores, which are congress-specific scores. These data are not available via the Rvoteview package; instead, we download these scores directly from the VoteView website.

voteview_nokken_poole <- 
  read.csv(url("https://voteview.com/static/data/out/members/HSall_members.csv"),
           stringsAsFactors = FALSE) 

base1 <- voteview_nokken_poole %>%
  filter(!is.na(nokken_poole_dim1),  
    chamber == 'Senate',
           party_code %in% c('100','200')&
           congress > 79) 

nm <- base1 %>%
  filter(state_abbrev == 'NM') 
  
nm_labels <- nm %>%
  group_by(bioname) %>%
  filter(congress == max(congress)) %>%
  ungroup() %>%
  mutate(bioname =  gsub(',.*$', '', bioname))

Below, the names and political ideology scores (first dimension) of Senators from New Mexico are presented relative to the median ideology for each major party historically. So, a history of fairly moderate representation in the Senate – dominated until more recently by the split delegation of Domenici (R) and Bingaman (D), both of whom voted center of their respective party medians. Udall (D) and Heinrich (D) may be drifting left, but this would reflect the state’s shifting ideology in general.

base2 <- base1 %>%  
  group_by(congress, party_code) %>%
  summarize(med = median(nokken_poole_dim1)) %>%
  ungroup() %>%
  ggplot() +
  
  geom_line(aes(x = congress, y= med, color = as.factor(party_code)),
            size = 1.25) +
  ylim(-.5, .5) +
  theme_minimal()+
  ggthemes::scale_color_stata() +
  theme(legend.position = 'none') +
  labs(title="Median ideologies for major parties: Houses 80 to 116") 

base2 +
  geom_line(data = nm, 
            aes(x = congress, y= nokken_poole_dim1, color = as.factor(bioname)),
            linetype = 2) +
  geom_text(data = nm_labels, 
            aes(label = bioname,
                x = congress, y =nokken_poole_dim1),
            size = 3)

New Mexico State Government

Finally, we consider the composition of New Mexico’s state government historically, namely the governorship and the bicameral house.

State Control in 2019

For a quick look at the current & aggregate composition of New Mexican state leadership relative to other US states, we access data made available by the National Conference of State Legislatures (NCSL).

x <- 'http://www.ncsl.org/Portals/1/Documents/Elections/Legis_Control_2019_August%2026th.pdf'

tmp <- tempfile()
curl::curl_download(x, tmp)

tab <- tabulizer::extract_tables(tmp, 
                          output = "data.frame", 
                          encoding = 'UTF-8')[[1]] %>%
  slice(2:51) %>%
  select(1,4:5, 7:8, 11:13) %>%
  separate(col = `Total.House`, into = c('total_house', 'dem'), sep = ' ') %>%
  select(-total_house) %>%
  filter(X != 'Nebraska')

colnames(tab) <- c('state', 'sen_dem', 'sen_rep', 'house_dem', 'house_rep',
                    'legis_control', 'governor', 'state_control')

tab$state_control <- factor(tab$state_control, levels = c('Divided', 'Dem', 'Rep')) 

The table below presents a sample of the NCSL data set, which includes composition of bicameral state houses by party affiliation, as well as the political affiliation of current governors. The legis_control variable indicates whether the House & Senate are controlled by the same party or not. Eg, both houses in California are controlled by Democrats, while in Minnesota, Republicans control the House and Democrats the Senate, ie, the houses are divided. The state_control variable indicates whether both houses and the governorship are controlled by the same party or not.

set.seed(89)
tab %>% sample_n(5) %>% knitr::kable() %>% 
  kableExtra::kable_styling("striped") 
state sen_dem sen_rep house_dem house_rep legis_control governor state_control
California 29 11 61 18 Dem Dem Dem
Minnesota 32 35 75 59 Divided Dem Divided
New York 40 22 106 43 Dem Dem Dem
Connecticut 22 14 91 60 Dem Dem Dem
Iowa 18 32 46 53 Rep Rep Rep

As far as state legislatures go, very little division exists nationally. The table below summarizes the distribution of legislature control-types (Republican, Democrat, or divided) for the 49 states in the Union with bicameral state houses. Only Minnesota has a divided legislature.

Dem Divided Rep
18 1 30

State control is a bit more divided nationally, with thirteen states having divided gubernatorial and legislative party control. Thirty-six have state government trifectas.

Divided Dem Rep
13 14 22

The map below illustrates government control by state and party affiliation for 2019. New Mexico, then, is one of fourteen states with a Democratic state government trifecta. This is new – during the previous eight years (at least) state control was divided in New Mexico.

flip_dets <- c('Divided', 'Dem', 'Rep')
flip_pal <- c('gray', '#395f81', '#9e5055')
names(flip_pal) <- flip_dets

uspoliticalextras::uspol_dvos_equalarea_sf$tile_outer %>%
  left_join(tab)  %>%
  ggplot() + 
  geom_sf(aes(fill = state_control),
          color = 'black', 
          alpha = .75) + 
  ggsflabel::geom_sf_text(data = uspoliticalextras::uspol_dvos_equalarea_sf$tile_inner,
                          aes(label = state_abbrev), 
                          size = 3.5,
                          color = 'white') +
  theme_minimal()+
  scale_fill_manual(values = flip_pal) +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        legend.title=element_blank(),
        legend.position = 'bottom') +

  labs(title = "State government control in 2019")

Governors historically

Next, we investigate the party affiliation of New Mexico’s governors since its statehood in 1912. These data are made available as a PDF from the New Mexico State Legislature website.

url <- 'https://www.nmlegis.gov/Publications/Handbook/leadership_since_statehood_17.pdf'
tmp <- tempfile()
curl::curl_download(url, tmp)
tab <- tabulizer::extract_tables(tmp, output = "data.frame") 

xx <- c('year', 'speaker', 'pro_tem', 'governor', 'president')

tab1 <- lapply(tab, function(x) {
  colnames(x) <- xx
  return(x) }) %>%
  bind_rows() %>%
  mutate(governor = gsub('\\(died\\)|\\(resigned\\)', NA, governor),
         president = gsub('\\(died\\)|\\(resigned\\)', NA, president),
         president = gsub('^$', NA, president)) %>%
  tidyr::fill(governor, .direction = 'up') %>%
  tidyr::fill(president, .direction = 'up') %>%
  filter(!is.na(year)) %>%
  mutate(gov_party = gsub('^.*\\(([A-Z])\\)', '\\1', governor),
         pres_party = gsub('^.*\\(([A-Z])\\)', '\\1', president),
         governor = gsub('\\(.\\)', '', governor),
         president = gsub('\\(.\\)', '', president)) %>%
  select(-speaker, -pro_tem)

#Tabulizer is not perfect.  PDF is not up-to-date.
hand_edits <- data.frame (year = c(1912, 1951:1953, 2000, 2018:2019),
                          governor = c('McDonald', 'Horn', 'Horn', 'Stockton', 
                                       'Sanchez', 'Martinez', 'Lujan Grisham'),
                          president = c('Wilson', 'Truman', 'Truman', 'Eisenhower', 
                                        'Clinton', 'Trump, D.', 'Trump, D.'),
                          gov_party = c('D', 'D', 'D', 'R', 'D', 'R', 'D'),
                          pres_party = c('D', 'D', 'D', 'R', 'D', 'R', 'R'))
tab1 <- tab1 %>% bind_rows(hand_edits) %>% arrange(year)

After some cleaning, a sample of our data set is presented below. Included are the names of sitting US Presidents and their political affiliation.

year governor president gov_party pres_party
1912 McDonald Wilson D D
1913 McDonald Wilson D D
1914 McDonald Wilson D D
1915 McDonald Wilson D D
1916 McDonald Wilson D D
1917 C de Baca Wilson D D

The table below summarizes the total number of years (since 1912) that each party has held the governor’s office, cross-tabbed with the political affiliation of the US President during the same time period. First to note is that Democrats have held gubernatorial control in 70/108 years.

Second to note is that in 59 (39 + 20) of those years the New Mexico governor shared party affiliation with the sitting US President; in 49 (18 + 31) of those years, the two were divided. Roughly a 50-50 split historically, which is pretty interesting.

table(tab1$gov_party, tab1$pres_party) %>% 
  data.frame() %>% 
  rename(Gov_Party = Var1, Pres_Party = Var2) %>%
  spread(Pres_Party, Freq) %>% knitr::kable()%>%
  kableExtra::kable_styling("striped", full_width = F) %>%
  kableExtra::add_header_above(c(" " = 1, "Pres_Party" = 2))
Pres_Party
Gov_Party D R
D 39 31
R 18 20

In rank order by total years, then:

  • [Dem Gov/Dem Pres (39)] > [Dem Gov/Rep Pres (31)] > [Rep Gov/Rep Pres (20)] > [Rep Gov/Dem Pres (18)]

The plot below illustrates the political affiliation of New Mexico governors and US presidents since statehood in 1912. Lots of back and forth for sure. It would seem that New Mexicans hedge their bets when it comes to gubernatorial elections, tempering federal leadership with state leadership from the opposing party. With the exception of the ~FDR years.

tab1 %>%
  mutate(gov_val = ifelse(gov_party == 'D', .75, -.75),
         pres_val = ifelse(pres_party == 'D', 1, -1)) %>%

  ggplot() +
  geom_line(aes(x = year, y = gov_val), size = 1.25, color = '#b0bcc1') +
  geom_line(aes(x = year, y = pres_val), size = 1.25, color = '#55752f') +
  ylim(-1.25, 1.25) +
  theme_minimal()+
  annotate("text", x = 1920, y = 1.25, label = "DEMOCRAT") +
  annotate("text", x = 1920, y = -1.25, label = "REPUBLICAN") +
  annotate("text", x = 1914, y = 1.05, label = "President") +
  annotate("text", x = 1914, y = .8, label = "Governor") +
  theme(legend.position = 'none', 
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        axis.text.x = element_text(angle = 90, hjust = 1)) +
  scale_x_continuous(breaks=seq(1912,2018,4)) +
  labs(title="Presidential & Gubernatorial Party Affiliation by Year") 

State legislature composition historically

LASTLY, we investigate the party-based composition of the New Mexico state houses historically. Again, we access this data via a PDF made available at the New Mexico State Legislature website.

url_state <- 'https://www.nmlegis.gov/Publications/Handbook/political_control_17.pdf'
tmp <- tempfile()
curl::curl_download(url_state, tmp)
tab <- tabulizer::extract_tables(tmp, 
                          output = "data.frame", 
                          encoding = 'UTF-8')

current <- data.frame(year = c(2019, 2019),
                      count = c(26,16, 46, 24),
                      house = c('senate', 'senate', 'house', 'house'),
                      party = c('dem', 'rep', 'dem', 'rep'))

xx <- c('year', 'house', 'house_dem', 'house_rep', 'house_other', 
        'senate_dem', 'senate_rep', 'senate_other')

tab2 <- lapply(tab, function(x) {
  x <- x[, c(1:4,6, 8:9, 11)]
  colnames(x) <- xx
  x$house_other <- as.numeric(x$house_other)
  x$senate_other <- as.numeric(x$senate_other)
  return(x) }) %>%
  bind_rows() %>%
  filter(year %% 2 == 1 | house == '31st,2nd') %>%
  filter(!grepl('SS', house)) %>%
  mutate(house = gsub(',.*$', '', house)) %>%
  gather(key = 'type', value = 'count', -year, -house) %>%
  separate(type, into = c('house', 'party'), sep = '_') %>%
  mutate(count = ifelse(is.na(count), 0, count)) %>%
  bind_rows(current) %>%
  group_by(year, house) %>%
  mutate(per = round(count/sum(count),2)) %>%
  ungroup()

tab2$party <- factor(tab2$party, levels = c('other', 'dem', 'rep')) 

Per plot below, then, a post-Depression era stronghold for Democrats, with a couple of exceptions – most recently in the 52nd House (which took office in 2015). A bit of a different story relative to the state’s swingy-er tendancies in other offices considered here.

flip_dets <- c('other', 'dem', 'rep')
flip_pal <- c('#b0bcc1', '#395f81', '#9e5055')
names(flip_pal) <- flip_dets

tab2 %>%
  ggplot(aes(x=year, y=per, fill = party))+
  geom_area(alpha = 0.85, color = 'white', stat = 'identity') +
  geom_hline(yintercept = .50, color = 'white', linetype = 2) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  theme(legend.position = "none")+
  scale_fill_manual(values = flip_pal) +
  scale_x_continuous(breaks=seq(1913, 2019, 8)) + xlab('') +
  ggtitle('Composition of New Mexico state houses since statehood') +
  facet_wrap(~house)

Summary

At present, then, New Mexico is a blue state. While Trump rallied in Rio Rancho in September in hopes of capturing the state in 2020, New Mexico has (seemed to have) lost some of the SWING that has defined the state through much of its history. And made it an excellent bellwether for presidential election winners.

The state supported Clinton in 2016, sends two Democrats to the Senate, 3/3 Democrats to the House, and has a Democratic state government trifecta. These things are fluid for sure, but the state’s demographics continue to move the state’s political ideology leftwards. So, we’ll see. Open data. Open government.

To leave a comment for the author, please follow the link and comment on their blog: Jason Timm.

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)