The North Carolinian election landscape has shifted dramatically in recent years. Since 1976, the only Democrat to win North Carolina was Obama in 2008. However, recent elections have narrowed the Republican advantage, with Trump winning North Carolina by only 74,000 votes in 2020 . Moreover, the Harris campaign is increasing their presence in North Carolina in an attempt to attract independent, third-party, and swing voters. As North Carolina enters the fray as a Democratic target in the 2024 election, the state’s outcome may shift the outcome for future campaigns. Given this increased attention, North Carolinian third-party voters and their families are positioned to alter the outcome of the 2024 election. Understanding how third-party voters and their immediate family intend to vote and whether the voting behavior and turnout of third-party parents are the same as their children could be critical in understanding this and future election outcomes. While research has been conducted on the transfer of partisanship between parents and children for different demographic groups, the dynamics within third-party families remain underexplored. Children of third-party voters may face different pressures and incentives when deciding whether to maintain their parents’ political alignments and whether to vote. Examining how third-party parents influence their children’s political identity and voter turnout will provide valuable insight into the North Carolinian election and potentially broader election patterns. Thus, I aim to answer whether third-party and independent households have a higher rate of partisan transfer and whether that partisan transfer impacts turnout rates more for third-party and independent households.
There are several hypotheses for this analysis. Firstly, third-party & unaffiliated households will have a higher level of partisan transfer than other households in North Carolina. This is because parents of third-party households go out of their way to determine the values and stances of third parties that reflect their values. Therefore, they are potentially more researched on the issues than the average Republican or Democratic voter. Secondly, children of third-party & unaffiliated voters who have partisan transfer will have a higher turnout rate than children of Republican and Democratic voters with partisan transfer. I predict third-party & unaffiliated voters will have generally higher turnout rates than Democratic and Republican voters since they’ve looked at nontraditional options for party affiliation, which means they’ve likely researched party platforms more than the average voter. Finally, independent voters will have lower partisan transfer than all other voters. As independent voters aren’t associated with a particular political party, there’s less messaging aimed at getting them to turn out to vote compared to Republican and Democratic voters. As a result, the independent voters are likely to be slightly less involved in voting than the traditional voters.
To answer these questions, the North Carolina voter registration data and voter history data will be analyzed. Data will be taken from the 2020 and 2016 presidential election,The data will be joined together by voter registration number and then matched via addresses and last names to determine the number of households. Once households are determined, an examination will start of the party affiliation for all members of the household, with children determined based on age. All possible combinations of party affiliation will be examined, with third parties grouped together for analysis. Using these methods, this analysis should definitively determine the rate of partisan transfer among third-party and independent households and whether that partisan transfer impacts voter turnout.
To begin, we load the following libraries.
library(tidyverse)
library(ggplot2)
library(dplyr)
library(data.table)
library(tidyr)
library(stringr)
library(readr)
library(R.utils)
library(vroom)
library(MatchIt)
library(ggalluvial)
Here we create a unique household_id for matching purposes. The voter registration number included in the table is only unique per county, not for the whole state. To identify potential parents and their children, we look at household_id matching, age, number of parents, etc. It should be noted that for now we only look at households where there is one male and one female in the household matching this description, which does exclude same-sex couples and families in multi-generational households. Thus, this analysis is limited to traditional “nuclear” families. We also use race and ethnicity to find matches, so we can be as certain of a match as possible. This may exclude some adopted children, children in divorced homes, and other nontraditional families as a result. Following this, we create a table for households we each row is a household including all children and parents.
We have a very small number of 3rd party homogeneous households in the sample, which is unsurprising due to the lack of 3rd party registrants generally. As a result this analysis will primarily focus on unaffiliated voters, thought it should be noted that 3rd party voters will be included in these analyses. Below is all of the households with third party homogeneous households.
household_table %>%
filter(parent_party_type == "3RD")
## # A tibble: 18 × 13
## household_id parent_voter_ids parent_party_codes children_info homogeneity
## <chr> <chr> <chr> <list> <lgl>
## 1 AGUILAR_220__F… 36 7640705, 36 … LIB <tibble> TRUE
## 2 BIRCHFIELD_11_… 41 10071768, 41… LIB <tibble> TRUE
## 3 CHAMBERS_1511_… 23 1036735, 23 … LIB <tibble> TRUE
## 4 CROWLEY_468_N_… 31 46304, 31 46… LIB <tibble> TRUE
## 5 GAUVIN_8223__N… 43 852633, 43 8… LIB <tibble> TRUE
## 6 GOUSHAW_440__O… 1 9101404, 1 91… LIB <tibble> TRUE
## 7 GRAILER_1264__… 1 9034975, 1 90… LIB <tibble> TRUE
## 8 HAMILTON_407__… 29 164993, 29 1… LIB <tibble> TRUE
## 9 HILL_3002__ARB… 41 10176675, 41… LIB <tibble> TRUE
## 10 JASINSKI_2954_… 41 503491, 41 5… LIB <tibble> TRUE
## 11 MCBAY_3905__WA… 41 10143688, 41… LIB <tibble> TRUE
## 12 NAJERA_1830__A… 34 30196985, 34… LIB <tibble> TRUE
## 13 RANDAZZO_197__… 22 12268, 22 11… LIB <tibble> TRUE
## 14 SHIPMAN_860__L… 43 872451, 43 8… LIB <tibble> TRUE
## 15 SHUMATE_7551__… 41 634947, 41 9… LIB <tibble> TRUE
## 16 SUMMERELL_6__I… 41 207385, 41 2… LIB <tibble> TRUE
## 17 TOWEY_7065__OL… 1 9143290, 1 91… LIB <tibble> TRUE
## 18 WOOLBRIGHT_970… 13 30021117, 13… LIB <tibble> TRUE
## # ℹ 8 more variables: full_transfer <lgl>, partial_transfer <lgl>,
## # no_transfer <lgl>, all_party_codes <chr>, household_homogeneous <lgl>,
## # parent_homogeneous <lgl>, parent_party_type <chr>, transfer_type <chr>
household_table %>%
ggplot(aes(x = parent_party_type, fill = factor(household_homogeneous))) +
geom_bar(position = "fill") +
scale_fill_manual(values = c("orange", "blue"), labels = c("Heterogeneous", "Homogeneous")) +
labs(
title = "Homogeneous vs Heterogeneous Households by Homogeneous Parent Party",
x = "Party Affiliation",
y = "Proportion",
fill = "Household Type"
) +
theme_minimal()
The above graph shows the proportion of homogeneous households for each homogeneous parent grouping. Among households where both parents are 3rd party, approximately 20% have all members the household registered to a 3rd party. For households with homogeneous unaffiliated parents, this number is closer to 60%. For Republican and Democrat homogeneous parent households, the percent of homogeneous households lies around 40%.
Now we examine 3rd party and unaffiliated voter partisan transfer with different partied spouses.
ggplot(children_party_proportions_third, aes(x = spouse_party, y = proportion, fill = child_party_code)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = c("blue", "yellow", "red", "purple"),
labels = c("Democrat", "3rd Party", "Republican", "Unaffiliated")) +
labs(
title = "Proportion of Children's Party Registration by Third Party Parent Combination",
x = "Parent Combination (3rd Party + Spouse)",
y = "Proportion",
fill = "Child's Party"
) +
theme_minimal()
The above graph shows the proportion of child party registrations by spouse of a third party voter. So among households with one third party voter and one Democratic Spouse, 50% of children are unaffiliated, around 15% are Republican, 5% are 3rd party, and the remaining are Democrat.
# Count the children's party affiliations for each parent combination
# Plot the proportions
ggplot(children_party_proportions_una, aes(x = spouse_party, y = proportion, fill = child_party_code)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = c("blue", "yellow", "red", "purple"),
labels = c("Democrat", "3rd Party", "Republican", "Unaffiliated")) +
labs(
title = "Proportion of Children's Party Registration by Unaffiliated Parent Combination",
x = "Parent Combination (Unaffiliated Party + Spouse)",
y = "Proportion",
fill = "Child's Party"
) +
theme_minimal()
ggplot(children_party_proportions_dem, aes(x = spouse_party, y = proportion, fill = child_party_code)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = c("blue", "yellow", "red", "purple"),
labels = c("Democrat", "3rd Party", "Republican", "Unaffiliated")) +
labs(
title = "Proportion of Children's Party Registration by Democrat Parent Combination",
x = "Parent Combination (Dem Party + Spouse)",
y = "Proportion",
fill = "Child's Party"
) +
theme_minimal()
# Count the children's party affiliations for each parent combination
# Plot the proportions
ggplot(children_party_proportions_rep, aes(x = spouse_party, y = proportion, fill = child_party_code)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = c("blue", "yellow", "red", "purple"),
labels = c("Democrat", "3rd Party", "Republican", "Unaffiliated")) +
labs(
title = "Proportion of Children's Party Registration by Republican Parent Combination",
x = "Parent Combination (Rep Party + Spouse)",
y = "Proportion",
fill = "Child's Party"
) +
theme_minimal()
These bar graphs aim to visualize the proportion of children registered to various parties depending on the combination of parent party registrations. These bar graphs help us understand the different levels of partisan transfer rate among different parties, and homogeneous parent households. Households with at least one Republican or Unaffiliated parent have a large proportion of children of that same party identification. On the contrary, households with Democrat or 3rd party parents have a relatively small proportion of children of that same party affiliation. It’s particularly low for 3rd party households, even when both parents are 3rd party. Thus,upon initial inspection it seems that unaffiliated and republican parents have the strongest partisan transfer to their children.
Here we conduct a chi-squared test to see if there’s a difference in the partisan transfer among homogeneous households
chi_squared_results_homogeneous <- homogeneous_transfer_rates %>%
group_by(household_type) %>%
summarize(
chi_sq = chisq.test(proportion)$statistic,
p_value = chisq.test(proportion)$p.value
)
## Warning: There were 8 warnings in `summarize()`.
## The first warning was:
## ℹ In argument: `chi_sq = chisq.test(proportion)$statistic`.
## ℹ In group 1: `household_type = "Democratic"`.
## Caused by warning in `chisq.test()`:
## ! Chi-squared approximation may be incorrect
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 7 remaining warnings.
chi_squared_results_homogeneous
## # A tibble: 4 × 3
## household_type chi_sq p_value
## <chr> <dbl> <dbl>
## 1 Democratic 0.922 0.820
## 2 Republican 1.11 0.774
## 3 Third Party 0.210 0.976
## 4 Unaffiliated 1.05 0.790
The chi-squared test shows minimal if any difference among partisan transfer between types of homogeneous households. No homogeneous household type is statistically different in partisan transfer compared to the other, as all have p-values greater than 0.05. However, I suspect a large difference in partisan transfer rates to children between homogeneous and heterogeneous households, which we’ll explore next.
# Step 1: Define household types (Homogeneous vs Heterogeneous)
household_table <- household_table %>%
mutate(
household_type = case_when(
str_count(parent_party_codes, "REP|DEM|UNA|LIB") == 1 ~ "Homogeneous",
str_count(parent_party_codes, "REP|DEM|UNA|LIB") > 1 ~ "Heterogeneous",
TRUE ~ NA_character_
)
) %>%
filter(!is.na(household_type)) # Remove rows with undefined household types
# Step 2: Calculate transfer summary
transfer_summary <- household_table %>%
group_by(household_type, transfer_type) %>%
summarise(count = n(), .groups = "drop") %>%
group_by(household_type) %>%
mutate(proportion = count / sum(count))
# Step 3: Create a contingency table
contingency_table <- transfer_summary %>%
select(household_type, transfer_type, count) %>%
spread(key = household_type, value = count, fill = 0)
# Step 4: Perform chi-squared test
chi_squared_results <- chisq.test(as.matrix(contingency_table[,-1]))
# Print results
print(chi_squared_results)
##
## Pearson's Chi-squared test
##
## data: as.matrix(contingency_table[, -1])
## X-squared = 27164, df = 2, p-value < 2.2e-16
The chi-squared test shows a statistically significant difference between partisan transfer of heterogeneous and homogeneous households. With a p-value significantly less than 0.05, the results show a clear difference in the transfer rates of homogeneous and heterogeneous households. Below we’ll examine the difference between unaffiliated & 3rd party households compared to Republican and Democrat households.
# Create a new grouping variable for Major Party and Other
household_table_grouped <- household_table %>%
mutate(
parent_party_group = case_when(
str_detect(parent_party_codes, "REP|DEM") ~ "Major Party",
str_detect(parent_party_codes, "UNA|LIB") ~ "Other",
TRUE ~ NA_character_
)
) %>%
filter(!is.na(parent_party_group)) # Remove invalid groupings
# Summarize transfer counts by new group
transfer_summary_grouped <- household_table_grouped %>%
group_by(parent_party_group) %>%
summarise(
total = n(),
full_transfer_count = sum(transfer_type == "Full Transfer", na.rm = TRUE),
partial_transfer_count = sum(transfer_type == "Partial Transfer", na.rm = TRUE),
no_transfer_count = sum(transfer_type == "No Transfer", na.rm = TRUE)
) %>%
mutate(
full_proportion = full_transfer_count / total,
partial_proportion = partial_transfer_count / total,
no_proportion = no_transfer_count / total
)
# Create a contingency table for chi-squared test
contingency_table_grouped <- transfer_summary_grouped %>%
select(parent_party_group, full_transfer_count, partial_transfer_count, no_transfer_count) %>%
gather(key = "transfer_type", value = "count", full_transfer_count, partial_transfer_count, no_transfer_count) %>%
spread(key = "parent_party_group", value = "count", fill = 0)
# Perform chi-squared test
chi_sq_grouped <- chisq.test(as.matrix(contingency_table_grouped[, -1]))
# Display results
chi_sq_grouped
##
## Pearson's Chi-squared test
##
## data: as.matrix(contingency_table_grouped[, -1])
## X-squared = 1870.8, df = 2, p-value < 2.2e-16
Above we can see that there’s a significant difference between the partisan transfer rates of unaffiliated/3rd party voters and democrat/republican voters.
We can clearly see that transfer rates between homogeneous and heterogeneous parent households are statistically significantly different at the alpha level of 0.05. Similarly, we see that partisan transfer rates between 3rd party and unaffiliated are statistically significantly different than Republican & Democrat homogeneous parent households at the alpha =0.05 level. This indicates that partisan transfer rates differ significantly between different parent party registrations, and that partisan transfer is different when both parents are the same party affiliation compared to the parents having different party affiliations.
My ultimate goal is to run a difference in difference analysis, and to that end we’ll start by visualizing turnout rates among various groups. These groups are as follows
For data on voter turnout we access additional data from the North Carolina elections website, specifically their voter history file. The file includes all voters in North Carolina and every election they’ve participated in. We’ll load in the data, get information exclusively for those voters in the 2020 and 2016 election, and the join the table to the expanded household dataframe by matching for children’s unique voter registration number. From there we’ll be able to classify each child as one of the categories listed above, and then determine turnout rate for each category.
Below we see the table an various visualizations for turnout among these groups. Across the board turnout stays the same or decreases from 2016 to 2020, but as the visualizations below demonstrate, some groups had significantly larger decreases in turnout rates than others.
# Calculate turnout rates by parent_match and child_party_code
turnout_summary <- simplified_data %>%
group_by(parent_match, child_party_code) %>%
summarize(
total_voters = n(),
total_voted_2016 = sum(voted_2016, na.rm = TRUE),
total_voted_2020 = sum(voted_2020, na.rm = TRUE),
turnout_rate_2016 = total_voted_2016 / total_voters,
turnout_rate_2020 = total_voted_2020 / total_voters
) %>%
arrange(parent_match, child_party_code) # Optional: Sort for clarity
## `summarise()` has grouped output by 'parent_match'. You can override using the
## `.groups` argument.
# View the summarized table
print(turnout_summary)
## # A tibble: 12 × 7
## # Groups: parent_match [3]
## parent_match child_party_code total_voters total_voted_2016 total_voted_2020
## <chr> <chr> <int> <int> <int>
## 1 Both Parents… DEM 32116 12508 11378
## 2 Both Parents… LIB 12 2 0
## 3 Both Parents… REP 53702 20372 18688
## 4 Both Parents… UNA 17020 5766 5270
## 5 One Parent S… DEM 11154 3696 3232
## 6 One Parent S… LIB 42 12 14
## 7 One Parent S… REP 18450 6892 6486
## 8 One Parent S… UNA 23832 7532 6932
## 9 Zero Parents… DEM 8676 2558 2276
## 10 Zero Parents… LIB 1932 568 504
## 11 Zero Parents… REP 10942 4066 3970
## 12 Zero Parents… UNA 44842 14090 13042
## # ℹ 2 more variables: turnout_rate_2016 <dbl>, turnout_rate_2020 <dbl>
plot_both_parents_same
plot_one_parent_same
plot_no_parents_match
These visualizations help us determine the change and relative levels of voter turnout among different political affiliations and among those with different levels of partisan transfer. As the above graphs demonstrate, voter turnout varies depending on parent matching and partisan registration. Though all turnout decreased for 2020, republican children maintained the most consistent turnout rates across all possible parental partisan matching, with rates around 38% in 2016 and 35% in 2020. Third party voters with one parent had a visually striking decline from 2016 to 2020, though that could be due to the low number of third party children in our sample. Additionally, those with no matched parents seemed to have larger falloffs from 2016 to 2020 compared to those where both parents matched.
head(turnout_summary)
## # A tibble: 6 × 7
## # Groups: parent_match [2]
## parent_match child_party_code total_voters total_voted_2016 total_voted_2020
## <chr> <chr> <int> <int> <int>
## 1 Both Parents … DEM 32116 12508 11378
## 2 Both Parents … LIB 12 2 0
## 3 Both Parents … REP 53702 20372 18688
## 4 Both Parents … UNA 17020 5766 5270
## 5 One Parent Sa… DEM 11154 3696 3232
## 6 One Parent Sa… LIB 42 12 14
## # ℹ 2 more variables: turnout_rate_2016 <dbl>, turnout_rate_2020 <dbl>
We’ll conduct a variety of ANOVA tests for our data. We’ll start by doing simple ANOVA tests to see if turnout_rates can be determined by the party registration and amount of parents in the same household with the same party affiliation. Then, we’ll be calculating the difference in turnout for each of our groups between 2016 and 2020 and then do anova testing on the difference to determine if there are significant differences. Initially a difference in difference approach was considered, but due to computational constraints this ANOVA analysis was conducted.
anova_2016 <- aov(turnout_rate_2016 ~ parent_match + child_party_code, data = turnout_summary, weights = total_voted_2016)
summary(anova_2016)
## Df Sum Sq Mean Sq F value Pr(>F)
## parent_match 2 44.09 22.044 9.891 0.0126 *
## child_party_code 3 22.75 7.582 3.402 0.0942 .
## Residuals 6 13.37 2.229
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova_2020 <- aov(turnout_rate_2020 ~ parent_match + child_party_code, data = turnout_summary, weights = total_voted_2020)
summary(anova_2020)
## Df Sum Sq Mean Sq F value Pr(>F)
## parent_match 2 26.22 13.111 3.617 0.107
## child_party_code 3 25.08 8.360 2.306 0.194
## Residuals 5 18.13 3.625
We’ll compare general differences among both parent_match and child_party_code groups using ANOVA.
anova_general <- aov(turnout_difference ~ parent_match + child_party_code, data = turnout_summary, weights = total_voted_2016)
summary(anova_general)
## Df Sum Sq Mean Sq F value Pr(>F)
## parent_match 2 1.490 0.7451 3.745 0.088 .
## child_party_code 3 1.163 0.3875 1.948 0.223
## Residuals 6 1.194 0.1990
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The above analysis indicates that the number of parents in a household with the same party registration as the child has a statistically significant impact on voter turnout for the 2016 election at the alpha = 0.05 level. Additionally, the party registration is also significant for 2016 turnout at the alpha = 0.1 level. Neither value was significant in determining 2020 turnout. However, in determining the difference in turnout rates form 2016 to 2020, the amount of parents in the household with the same party affiliation is significant at the alpha = 0.1 level. Though, the child’s party does not have a statistically significant effect on the difference in voter turnout. This indicates that the party of a child will not significantly effect turnouts, but the party affiliation of their parents will.
Our first tests showed that there was not a significant difference in the partisan transfer rates of homogeneous parent households for different parent parties. However, our second chi-squared test revealed that there is a difference in transfer rates between homogeneous parent and heterogeneous parent households. We saw this visually with our bar graph visualizations, which reinforces the results of our chip-squared test. Overall we can conclude that partisan transfer rates from parent to child for parents with a spouse of the same political affiliation is different than the transfer rate from parent to child for parents with a spouse of a different political affiliation.
Our next chi-squared test showed that the transfer rates for unaffiliated & 3rd party parents compared to Republican & Democrat parents is significantly different. While we found differences when grouping, we did find somewhat contradictory results for transfer rates of homogeneous households for different political affiliations. With more data, future research could and should focus on the expansion of this analysis, looking at a variety of combinations and comparing more groups of parental voters.
Our final ANOVA tests reveal that partisan transfer does make a difference for voter turnout in the immediate and future elections. The type of partisan transfer, in the form of the number of parents with the same political party affiliation had an immediately significant effect on voter turnout for the 2016 election. This was paired with a still significant effect on voter turnout of the party code. While neither were significant for the 2020 election turnout, importantly partisan transfer was significant for the difference in election turnout between 2016 and 2020. Future research should focus on conducting a difference in difference analysis among these various groups to determine a causal relationship, and help identify the specific influences of different partisan transfer levels on coter turnout and voter turnout differences.