Re-districting in Winston-Salem

Introduction

There was a recent bill introduced in the North Carolina General Assembly to reorganise the city counsel by two Republican state members of the General Assembly. The status quo is that there are a total of eight wards, each with a seat on the city counsel. The mayor votes only when there is a tie. As things are, the current city counsel is composed of four Black individuals and four white men, with a white man as mayor. The proposal from the General Assembly is to collapse several of the ward seats into five wards and then create three permanent at-large city counsel positions. Additionally, the mayor would have a vote on all matters, not just in ties.

There is a lot going on here that this post cannot unpack. The biggest issue is truely the political landscape in North Carolina where the Supreme Court of the United States of America is actively hearing a case on gerrymandering where a member of the general assembly admitted to voter packing. Additionally, Winston-Salem has an interesting relationship with racial and political sorting. All of these elements are at play with this new proposed structure. Additionaly, the members whose wards would be collapsed would be the three Black women who are currently on the city counsel. This paired with reducing terms to two hears makes it logistically harder to run campaigns (shorter term lengths mean more campaigning, at-large positions favor people with cash to campaign all over the city, etc). Just a lot happening that can’t be completely unpacked. See here for more details.

Analysis

I am going to take some historical voting records and see given the new ward proposal how the seats would have fallen. Again, this is a quick analysis and more work could defitely be done on this.

library(tidyverse)
library(lubridate)

Current State

proposed_wards <- tribble(
  ~"new_ward", ~"pct_label", ~"current_ward",
  "Ward 1", "032", "",
  "Ward 1", "033", "",
  "Ward 1", "092", "",
  "Ward 1", "101", "",
  "Ward 1", "131", "",
  "Ward 1", "132", "",
  "Ward 1", "201", "",
  "Ward 1", "203", "",
  "Ward 1", "204", "",
  "Ward 1", "205", "",
  "Ward 1", "206", "",
  "Ward 1", "207", "",
  "Ward 1", "801", "",
  "Ward 1", "802", "",
  "Ward 1", "903", "",
  "Ward 1", "904", "",
  "Ward 1", "905", "",
  "Ward 1", "906", "",
  "Ward 1", "907", "",
  "Ward 1", "908", "",
  "Ward 1", "909", "",

  "Ward 2", "031", "",
  "Ward 2", "032", "",
  "Ward 2", "033", "",
  "Ward 2", "034", "",
  "Ward 2", "081", "",
  "Ward 2", "082", "",
  "Ward 2", "083", "",
  "Ward 2", "111", "",
  "Ward 2", "201", "",
  "Ward 2", "203", "",
  "Ward 2", "204", "",
  "Ward 2", "206", "",
  "Ward 2", "207", "",
  "Ward 2", "301", "",
  "Ward 2", "302", "",
  "Ward 2", "303", "",
  "Ward 2", "304", "",
  "Ward 2", "305", "",
  "Ward 2", "306", "",
  "Ward 2", "401", "",
  "Ward 2", "402", "",
  "Ward 2", "403", "",
  "Ward 2", "404", "",

  "Ward 3", "16", "",
  "Ward 3", "011", "",
  "Ward 3", "012", "",
  "Ward 3", "013", "",
  "Ward 3", "015", "",
  "Ward 3", "042", "",
  "Ward 3", "043", "",
  "Ward 3", "063", "",
  "Ward 3", "082", "",
  "Ward 3", "401", "",
  "Ward 3", "405", "",
  "Ward 3", "501", "",
  "Ward 3", "502", "",
  "Ward 3", "503", "",
  "Ward 3", "504", "",
  "Ward 3", "505", "",
  "Ward 3", "507", "",

  "Ward 4", "042", "",
  "Ward 4", "122", "",
  "Ward 4", "506", "",
  "Ward 4", "601", "",
  "Ward 4", "602", "",
  "Ward 4", "603", "",
  "Ward 4", "604", "",
  "Ward 4", "605", "",
  "Ward 4", "606", "",
  "Ward 4", "607", "",
  "Ward 4", "702", "",
  "Ward 4", "703", "",
  "Ward 4", "704", "",
  "Ward 4", "705", "",
  "Ward 4", "709", "",

  "Ward 5", "071", "",
  "Ward 5", "072", "",
  "Ward 5", "074", "",
  "Ward 5", "123", "",
  "Ward 5", "131", "",
  "Ward 5", "133", "",
  "Ward 5", "701", "",
  "Ward 5", "705", "",
  "Ward 5", "706", "",
  "Ward 5", "707", "",
  "Ward 5", "708", "",
  "Ward 5", "803", "",
  "Ward 5", "804", "",
  "Ward 5", "805", "",
  "Ward 5", "806", "",
  "Ward 5", "807", "",
  "Ward 5", "808", "",
  "Ward 5", "809", "",
  "Ward 5", "901", "",
  "Ward 5", "902", "",
)

And now because the records of each primary are made public, I will download them and read them in. Excuse the messiness; I imagine there is an easier way to do this, but the NC Board of Elections elected to change their output format between the years, so it is quicker just to read the files in one at a time as there aren’t too many of them. All of the data are available here if you would like to play along.

my_files <- list.files(file.path(
"data"), full.names = T, pattern = "results_")

Now to read them in, fix a few things and then combine them. I will write a quick helper function though…

clean_ncbe <- function(df){
  df %>%
  janitor::clean_names() %>%
  rename(contest_name = contest,
         choice_party = party) %>%
  select(county, precinct, contest_name, contest_type,
         total_votes, choice, choice_party)
}

Now to read them in.[^1]

results_2010 <- read_csv(my_files[[1]])%>%
  clean_ncbe()
results_2011 <- read_csv(my_files[[2]])%>%
  clean_ncbe()
results_2012 <- read_csv(my_files[[2]])%>%
  clean_ncbe()
z <- lapply( my_files[4:8], function(x) data.table::fread(x, sep = "\t"))

z <- data.table::rbindlist(z, fill =TRUE) %>%
  janitor::clean_names()

We can then put the data together as follows:

all_results <-bind_rows(results_2010, results_2011,
                        results_2012, z)

This data frame includes all precinct level reporting for each election. For the sake of this analysis I want to reduce my scope down to just Forsyth County, the home county of Winston-Salem.

forsyth_results <- all_results %>%
  filter(grepl("FORSYTH",county)) %>%
  mutate(precinct = str_extract(precinct, "\newlined{2,}")) %>%
  left_join(proposed_wards, by = c("precinct" = "pct_label")) %>%
  mutate(my_date = lubridate::mdy(election_date),
         election = lubridate::year(my_date))

Upon inspection of the data it is interesting to note that straight party tickets were once allowed. This practice was outlawed by the North Carolina General Assembly in 2013 (Lynch n.d.).

Further, if I just wanted to look look a city counsel elections, the subject of this analysis I could do the following actions.

city_consel_elections <- forsyth_results %>%
  filter(grepl("WARD", contest_name)) %>%
  filter(!grepl("HIGH POINT", contest_name))

Just as a reminder the last city counsel elections occured in 2016. Previous legislation moved the city counsel elections to presidential election years. Additionaly, citizens have the option of writing in candidates, so that can explain some of the votes for “others.”

Looking at the overall 2016 city counsel elections we can see the following:

city_consel_elections %>%
  mutate(choice_party = ifelse(is.na(choice_party), "Other", choice_party)) %>%
  group_by(election, choice_party) %>%
  summarise(total_vote = sum(total_votes, na.rm = T)) %>%
  spread(choice_party, total_vote, 0) %>%
  mutate(DEM_vote_share = scales::percent(DEM / (DEM + REP + V1))) %>%
  knitr::kable(caption = "Vote Share Overall, 2016 City Counsel Elections")
election V1 DEM REP DEM_vote_share
2016 1656 80697 23660 76%

So all in all Winston-Salem has a strong lean towards the Democratic Party. Digging deeper we can look at the individual wards.

city_consel_elections %>%
  mutate(choice_party = ifelse(is.na(choice_party)|choice_party=="", "Other", choice_party)) %>%
  group_by(election, contest_name, choice_party) %>%
  summarise(total_vote = sum(total_votes, na.rm = T)) %>%
  spread(choice_party, total_vote, 0) %>%
  ungroup() %>%
  mutate(contest_name = str_remove(contest_name, "WINSTON SALEM CITY COUNCIL - ")) %>%
  mutate(DEM_vote_share = scales::percent(DEM / (DEM + REP + Other))) %>%
  knitr::kable(caption = "Vote Share By Ward, 2016 City Counsel Elections")

|election| contest_name| DEM| Other | REP | DEM_vote_share | |2016 |EAST WARD |11673| 204 | 0 | 98.28% | |2016 |NORTH WARD |15454| 209 | 0 | 98.67% | |2016 |NORTHEAST WARD |13276| 397 |0 | 97.10% | |2016 |NORTHWEST WARD |9410| 21 | 5984 | 61.04% | |2016 |SOUTH WARD |9832 |38| 4374 | 69.03% | |2016 |SOUTHEAST WARD| 9044| 184 | 0 98.01% | |2016 |SOUTHWEST WARD| 12008 | 295 | 0| 97.60%| |2016 |WEST WARD| 0 |308 |13302 |0.00%|

We can see pretty easily from this analysis that Winston-Salem is pretty solidly democratic, with the exception of the West ward where not a single vote was cast of a democratic candidate. The only truly competitive districts were the South and Northwest wards which still strongly favoured democratic candidates. However, one of the major take-aways here is that there is evidence of strong population sorting occuring with solidly democratic neighborhoods and solidly republican neighborhoods.

New Proposal

So let’s see how this all plays out under the new proposed wards. If take the 2016 election and group by Wards we see that there still will only be one strong Republican ward, Ward 5. One thing that occludes a stronger take on this is that absentee ballots are not assigned to a precinct.

city_consel_elections %>%
  mutate(choice_party = ifelse(is.na(choice_party)|choice_party=="", "Other", choice_party)) %>%
  group_by(election, new_ward, choice_party) %>%
  summarise(total_vote = sum(total_votes)) %>%
  spread(choice_party, total_vote) %>%
  ungroup() %>%
  mutate(DEM_vote_share = scales::percent(DEM / (DEM + REP + Other))) %>%
  knitr::kable(caption = "Vote Share By Ward, 2016 City Counsel Elections")

And we can see that while many absentee votes are democratic the margins could say some of the above districts to be more competitive. Because of this it is probably worth looking and historical voting.

city_consel_elections %>%
  mutate(choice_party = ifelse(is.na(choice_party)|choice_party=="", "Other", choice_party)) %>%
  group_by(contest_name, choice_party) %>%
  summarise( total_ab = sum(absentee_by_mail)) %>%
  mutate(percent = scales::percent(total_ab/sum(total_ab))) %>%
  mutate(metric = paste0(percent, " n= ", total_ab)) %>%
  select(-total_ab, -percent) %>%
  spread(choice_party, metric) %>%
  knitr::kable(caption = "Breakdown of Absentee Ballots, 2016 City Counsel Elections")

NC Voter History

nc_voter_vistory <- read_tsv("data/ncvhis34.txt")

Additionally, let’s use the 2016 presidential election using the voter history files from the NC Board of Elections here.

nc_voter_vistory %>%
  left_join(proposed_wards) %>%
  filter(!is.na(new_ward)) %>%
  filter(election_desc == "11/08/2016 GENERAL") %>%
  filter(!is.na(voted_party_desc)) %>%
  mutate(voted_party_desc = ifelse(voted_party_desc=="LIBERTARIAN",
                                   "REPUBLICAN", voted_party_desc)) %>%
  group_by(new_ward, voted_party_desc) %>%
  summarise (n = n()) %>%
  mutate(vote_share = round(n/sum(n)*100,0)) %>%
  knitr::kable(caption = "Vote Share By New Ward, 2016 Voters Party Affiliations")

Again, what is interesting here is that all but one of the Wards start with a clearly identified Democratic plurality.

Voter Efficiency

While not the best metric we can look at the efficiency gap. So looking at the current situation we see that there is a:

city_consel_elections %>%
  mutate(choice_party = ifelse(choice_party %in% c("DEM", "REP"), choice_party,
                               "other")) %>%
  group_by(election, contest_name, choice_party) %>%
  summarise(total_vote = sum(total_votes, na.rm = T)) %>%
  spread(choice_party, total_vote, 0) %>%
  mutate(winning = (DEM+other+REP)/2+1) %>%
  rowwise() %>%
  mutate(DEM_wasted_vote = ifelse(DEM - winning<0,DEM,DEM - winning),
         REP_wasted_vote = ifelse(REP - winning<0,REP,REP - winning)) %>%
  ungroup() %>%
  summarise(total_voter= sum(REP+other +DEM),
            wasted_dem = sum(DEM_wasted_vote),
            wasted_rep = sum(REP_wasted_vote)) -> current_eff

scales::percent((current_eff</span>wastedrep<spanclass="keywordoperatorarithmeticr"></span>currenteff<spanclass="keywordaccessordollarr"></span>wasted_rep<span class="keyword operator arithmetic r">-</span>current_eff<span class="keyword accessor dollar r">wasted_dem)/current_eff$total_voter)
"-17%"

And if we move to the proposed organization we see:

forsyth_results %>%
  filter(!is.na(new_ward)) %>%
  filter(grepl(x = contest_name, "PRESIDENT")) %>%
  mutate(choice_party = ifelse(choice_party %in% c("DEM", "REP"), choice_party,
                               "other")) %>%
  group_by(election, new_ward, choice_party) %>%
  summarise(total = sum(total_votes)) %>%
  ungroup() %>%
  spread(choice_party, total) %>%
  mutate(election = ifelse(is.na(election), 2012, election)) %>%
  mutate(DEM_share = DEM/ (DEM+other+REP)) %>%
  mutate(winning = (DEM+other+REP)/2+1) %>%
  rowwise() %>%
  mutate(DEM_wasted_vote = ifelse(DEM - winning<0,DEM,DEM - winning),
         REP_wasted_vote = ifelse(REP - winning<0,REP,REP - winning)) %>%
    ungroup() %>%
  summarise(total_voter= sum(REP+other +DEM),
            wasted_dem = sum(DEM_wasted_vote),
            wasted_rep = sum(REP_wasted_vote))->efficiency

scales::percent((efficiency</span>wastedrep<spanclass="keywordoperatorarithmeticr"></span>efficiency<spanclass="keywordaccessordollarr"></span>wasted_rep<span class="keyword operator arithmetic r">-</span>efficiency<span class="keyword accessor dollar r">wasted_dem)/efficiency$total_voter)

“16%” \

So no real change in the efficiency gap.

Lynch, Harry. n.d. “Elimination of Straight-Ticket Voting Could Leave More Ballots Incomplete.” *Newsobserver*. Accessed April 8, 2019. .
[^1]: Reall what I should be doing here is writing a function that inspects what kind of delimiter is being used in the file and then import accordingly. However, I’m a bit lazy at the moment and not going to do that. The plus sides for doing that method is most importantly it is more reprocible and scalable should I go back and look at previous data. Additionally, I could do a `map` function to read and apply the newly written function rather than writing piecemeal as I am now.

Citation

BibTex citation:

@online{dewitt2019
author = {Michael E. DeWitt},
title = {Re-districting in Winston-Salem},
date = 2019-04-08,
url = {https://michaeldewittjr.com/articles/2019-04-08-re-districting-in-winston-salem},
langid = {en}
}

For attribution, please cite this work as:

Michael E. DeWitt. 2019. "Re-districting in Winston-Salem." April 8, 2019. https://michaeldewittjr.com/articles/2019-04-08-re-districting-in-winston-salem