library(tidyverse)
# Loading the case study data
# Missing values are represented in the Ipsos data with a value of -1
# R uses the symbol NA to represent values it knows are missing
# The "na.strings = c(-1, "NA")" argument tells R it should treat any -1 or NA values as NA
voter_data <- read_csv("https://raw.githubusercontent.com/fivethirtyeight/data/refs/heads/master/non-voters/nonvoters_data.csv", na = c(-1, "NA"))
# Creating the frequent voter indicator
voter_data <- voter_data |>
mutate(infrequent_voter = ifelse(voter_category == "rarely/never", 1, 0))Case Study 3
Inspired by an assignment from Professor Maria Tackett at Duke University
Setup
According to the American Civil Liberties Union, “voting is the cornerstone of our democracy and the funadmental right upon which all our civil liberties rest.” On paper, all United States citizens who are over the age of 18, meet their state’s residency requirements, and are registered to vote in that state are eligible to vote in state and federal elections. In practice, however, many Americans do not exercise this right. Voter turnout in the 2024 election was 63.9%, meaning that over a third of eligible Americans did not (for whatever reason) participate in the election. This was one of the higher turn-out elections in recent American history; in 1996, for example, only 51.7% eligible voters voted.
FiveThirtyEight (a now-defunct website that focused on opinion poll analysis) partnered with Ipsos to research and better understand these voting behaviors. Between September 15 and September 25, 2020, they polled a sample of over 5,000 U.S. citizens. Respondents were asked a variety of questions about their political beliefs, thoughts on current issues, and voting behavior. The full codebook with all variables and variable definitions is available on GitHub. A few potentially relevant variables are:
ppage: the age of the respondent, in yearseduc: highest educational attainment, represented categoricallyrace: self-reported race and ethnicity of the respondent, using census categories (all categories except “Hispanic” are non-Hispanic)gender: self-reported gender of the respondentincome_cat: household income, represented categoricallyQ30: response to the question “Generally speaking, do you think of yourself as a…”, with options Republican (1), Democrat (2), Independent (3), Another party (4), or No preference (5)voter_category: past voting behavior, categorized as “always” (meaning the respondent voted in all or all-but-one of the elections they were eligible in), “sporadic” (meaning the respondent voted in at least two, but fewer than all-but-one, of the elections they were eligible in), and “rarely/never” (meaning the respondent voted in 0 or 1 of the elections they were eligible in)
Task
A local political organization would like to better understand who constitutes an infrequent voter (i.e., a “low propensity voter” who rarely or never votes in elections).
- They suspect that political party affiliation plays a role in an individual’s likelihood of voting. Is this suspicion borne out in the data? In particular, is there a relationship between political party and voting frequency? What is the nature of this relationship?
- The organization would also like to identify which adults in the community are infrequent voters: these adults will receive targeted political mailings that will be different from the mailings sent to adults who are not low propensity voters. Use the
voter_traindata frame and the variable selection strategies and model comparison techniques described in class to identify the most parsimonious combination of characteristics for predicting voting frequency. - Once you have selected your prediction model, use this fitted model to classify 500 new community members (in the
voter_testdata frame below) as either “infrequent voters” or “frequent voters”. How well does your model do at making these predictions?
Please follow the general instructions and guidelines for case studies and incorporate any previous comments and feedback that you have received. Remember that your goal is to answer the bolded task, not to provide the reader with a full history of your analysis. Your report should describe your general modeling approach but does not need to include details about each specific step taken: your main goal should be to justify your “best” model choice, with all further details relegated to your Appendix. As you build this model, be mindful of missing data in the data set. (See below for some suggestions about how to handle missing data in the study!) Similarly, if you deem any of the respondents to be outliers or otherwise remove them from the analysis, your report should describe how many individuals were removed and explain why.
To access the data set for this case study, run the following lines in R:
Next, use the rsample package to separate the full data set into testing and training subsets.
set.seed(327486)
library(rsample)
voter_split <- rsample::initial_split(voter_data, prop = 0.9, strata = infrequent_voter)
voter_train <- training(voter_split)
voter_test <- testing(voter_split)Train your model only on the voter_train data.
When you are ready to use your fitted model on the new set of community members, you may use the voter_test data frame.
Missing Data
Missing data refers to scenarios in which the value of a particular variable is unknown for a given participant; it is encoded in R using the character NA. There are many reasons why this information might not be known: perhaps the data was accidentally lost, or perhaps the participant refused to answer a given question or return a particular survey. Entire semester-long courses could be (and are!) dedicated to the appropriate analysis of data sets that contain missing data. For the purposes of this case study, here are several ad hoc approaches that you might consider taking to address missing data in the voting data set:
- Variable deletion: Only include those explanatory variables that have complete information for all participants
- Complete-case analysis: Only include those participants who have complete information for all variables
- Indicator method: When the variable with missing data is a categorical variable, we can create an additional, new level of the variable that corresponds to those with missing data (e.g., we might turn smoking into a three-level categorical variable with levels
Yes,No, andNA) - Combining levels: Alternatively, when the variable with missing data is a categorical variable, we can combine the missing data in with an existing category (e.g., we might group missing observations and non-smokers together, so that our smoking variable has two levels
Yes(known to be a smoker) andNo(non-smoker or missing smoking information))
You might choose to use just one of these approaches, or you might choose to combine several of them. There is no “right” or “wrong” answer for the purposes of this case study—whatever you choose to do, just make sure you tell your reader what you did and why you did it! You might also think about any limitations that your particular choice of missing data strategy might introduce.
# What proportion of participants are missing certain variables?
voter_train |>
select(ppage, educ, race, gender, income_cat, Q30, voter_category) |>
map_int(~sum(is.na(.x))) / nrow(voter_train) ppage educ race gender income_cat
0.000000000 0.000000000 0.000000000 0.000000000 0.000000000
Q30 voter_category
0.008188916 0.000000000
# Example: using the indicator method
voter_train |>
pull(Q30) |>
table()
1 2 3 4 5
1418 1808 1282 91 609
voter_train <- voter_train |>
mutate(
Q30_cat_na = factor(
Q30,
levels = c(1, 2, 3, 4, 5, NA),
exclude = NULL
)
)
voter_train |>
pull(Q30_cat_na) |>
table()
1 2 3 4 5 <NA>
1418 1808 1282 91 609 43
# Example: combining levels 4, 5, and NA
# Forms a new group 4 that comprises another party/no preference/unknown
voter_train <- voter_train |>
mutate(Q30_combo_45na = factor(ifelse(Q30 >= 4 | is.na(Q30), 4, Q30)))
voter_train |>
pull(Q30_combo_45na) |>
table()
1 2 3 4
1418 1808 1282 743