This analysis revisits C. Richard Hofstetter’s 1969 study on political disengagement following the assassination of Martin Luther King Jr., published in The Public Opinion Quarterly.
The data for this study was collected through a survey project on electoral behavior in Central Ohio. Hofstetter used a probability sample of all adults living in residences in the greater Columbus, Ohio metropolitan area. The sampling frame was based on a recent city directory, from which residences were randomly selected. To ensure representativeness, respondents at specified residences were then systematically selected to provide sex-age distributions that matched the population. The interviewers achieved a completion rate of approximately 80% of attempted interviews.
The assassination of Martin Luther King Jr. on April 4, 1968, at approximately 7:10 PM, occurred midway through the interviewing phase. This created a natural experiment, with respondents interviewed before this time forming a “control” group and those interviewed after forming a “treatment” group exposed to knowledge of the assassination.
The author interpreted the results as following: He saw a clear increase in negative sentiment towards several figures and institutions perceived as representing the white establishment (police, white people, national politicians, Richard Nixon), there was also a simultaneous increase in positive affect towards groups representing Black identity and empowerment (NAACP and Black people as a group). This suggests a process of selective disengagement, where alienation from certain aspects of the political system coincided with a strengthening of ties to others. Hofstetter finds that white respondents were less significantly impacted by the assassination of Martin Luther King Jr. than Black respondents.
The statistical methods available to Hofstetter in 1969 were more limited compared to our modern analysis, but not entirely rudimentary. The chi-squared test, which we used in our initial analysis of changes in positive and negative affect, was indeed a widely known and established statistical method at that time. Hofstetter and his team would have been able to perform chi-squared tests, although the process would have been far more laborious than it is today However, more advanced techniques like the Kolmogorov-Smirnov test, while theoretically known, would have been much more challenging to implement in practice due to computational limitations. The polarization index and distribution analyses I conduct here represent modern approaches that weren’t readily available to researchers in the late 1960s.
In this analysis, I examine the statistical significance of attitude changes towards various political objects before and after the MLK assassination, stratified by race. Using the dataset of affect percentages and sample sizes reported in the article, I perform chi-squared tests on contingency tables to assess significant shifts in positive and negative attitudes for each political object and racial group. The results are presented in tables and visualized through bar charts, highlighting statistically significant changes (p < 0.05). Additionally, I conduct polarization analyses using both variance-based methods (Levene’s test) and more advanced techniques (polarization index, kurtosis, and Kolmogorov-Smirnov tests) to examine changes in the spread and shape of attitude distributions. For significant changes, I create density ridge plots to visualize the shifts in attitude distributions. This approach combines methods available in 1969 with modern techniques to provide a comprehensive view of attitude changes and potential polarization effects following the assassination.
The findings regarding the minimal effect on white respondents are largely aligned with Hofstetter’s article. No statistically significant changes in positive or negative affect were observed among white respondents for any political object. The advanced polarization analysis confirms this, showing no significant changes in the distribution of white attitudes (all KS p-values = 1). For Black respondents, the most notable change was towards the police. This change manifested in multiple ways: a significant increase in negative affect (from 3.7% to 31.0%, p = 0.01), a substantial but not statistically significant decrease in positive affect (from 88.9% to 58.6%, p = 0.274), a significant increase in opinion polarization as measured by variance ratio (2.450, p < 0.01), and a significant change in the overall distribution of attitudes (KS p-value = 0.0154). The polarization for police attitudes among Black respondents increased dramatically indicating a substantial increase in polarization. Other changes in Black respondents’ attitudes were observed but were not statistically significant.
# Input data
data <- tibble(
Political_Object = rep(c("Police", "White", "Democratic Party", "Lyndon Johnson", "NAACP", "Black", "Republican Party", "Richard Nixon", "National Politicians"), each = 4),
Time = rep(c("B", "A"), times = 18),
Race = rep(c("White", "Black"), each = 2, times = 9),
Negative = c(3.8, 5.8, 3.7, 31.0, 0.0, 1.9, 11.1, 24.1, 9.9, 15.4, 0.0, 3.4, 28.6, 32.7, 11.1, 17.2, 30.8, 31.4, 11.1, 3.4, 10.4, 16.7, 0.0, 0.0, 8.8, 9.6, 22.2, 31.0, 19.8, 24.4, 22.2, 48.3, 7.1, 9.6, 7.4, 24.1),
Neutral = c(8.2, 7.7, 7.4, 10.3, 18.1, 18.6, 18.5, 24.1, 21.4, 17.3, 22.2, 10.3, 16.5, 10.3, 14.8, 6.9, 24.2, 20.5, 22.2, 13.8, 33.0, 25.0, 14.8, 10.3, 22.0, 17.9, 29.6, 34.5, 15.9, 12.8, 18.5, 20.7, 26.9, 16.0, 37.0, 41.4),
Positive = c(87.9, 86.5, 88.9, 58.6, 81.9, 79.5, 70.4, 51.7, 68.7, 67.3, 77.8, 86.2, 54.9, 57.1, 74.1, 75.9, 45.1, 48.1, 66.7, 82.8, 56.6, 58.3, 85.2, 89.7, 69.2, 72.4, 48.1, 34.5, 64.3, 62.8, 59.3, 31.0, 65.9, 74.4, 55.6, 34.5)
)
# Add sample sizes
sample_sizes <- tibble(
Race = c("White", "White", "Black", "Black"),
Time = c("B", "A", "B", "A"),
Sample_Size = c(182, 156, 27, 29)
)
# Join sample sizes to main data
data <- data %>%
left_join(sample_sizes, by = c("Race", "Time"))
# Analysis function
analyze_affect <- function(object, race, affect_type) {
obj_data <- data %>%
filter(Political_Object == object, Race == race) %>%
select(Time, !!sym(affect_type), Sample_Size) %>%
mutate(Value = round(!!sym(affect_type) * Sample_Size / 100)) # Convert percentages to counts
contingency_table <- xtabs(Value ~ Time, data = obj_data)
# Check for zero entries in contingency table
if (all(contingency_table == 0)) {
return(tibble(
Political_Object = object,
Race = race,
Affect_Type = affect_type,
Before_Affect = 0,
After_Affect = 0,
Difference = 0,
P_value = NA
))
}
# Perform chi-squared test if there are positive entries
chi_test <- tryCatch({
chisq.test(contingency_table)
}, error = function(e) {
return(list(p.value = NA)) # Return NA if test fails
})
tibble(
Political_Object = object,
Race = race,
Affect_Type = affect_type,
Before_Affect = obj_data$Value[obj_data$Time == "B"] / obj_data$Sample_Size[obj_data$Time == "B"][1] * 100,
After_Affect = obj_data$Value[obj_data$Time == "A"] / obj_data$Sample_Size[obj_data$Time == "A"][1] * 100,
Difference = After_Affect - Before_Affect,
P_value = chi_test$p.value
)
}
# Run analysis for all combinations of Political Object, Race, and Affect Type
results <- crossing(
Political_Object = unique(data$Political_Object),
Race = unique(data$Race),
Affect_Type = c("Positive", "Negative")
) %>%
pmap_dfr(~analyze_affect(..1, ..2, ..3))
Political_Object | Race | Before_Affect | After_Affect | Difference | P_value | Significant |
---|---|---|---|---|---|---|
Police | Black | 88.9 | 58.6 | -30.3 | 0.274 | |
Richard Nixon | Black | 59.3 | 31.0 | -28.2 | 0.162 | |
National Politicians | Black | 55.6 | 34.5 | -21.1 | 0.317 | |
White | Black | 70.4 | 51.7 | -18.6 | 0.493 | |
NAACP | Black | 66.7 | 82.8 | 16.1 | 0.355 | |
Republican Party | Black | 48.1 | 34.5 | -13.7 | 0.532 | |
Democratic Party | Black | 77.8 | 86.2 | 8.4 | 0.555 | |
Black | Black | 85.2 | 89.7 | 4.5 | 0.668 | |
Lyndon Johnson | Black | 74.1 | 75.9 | 1.8 | 0.758 | |
National Politicians | White | 65.9 | 74.4 | 8.4 | 0.795 | |
Republican Party | White | 69.2 | 72.4 | 3.2 | 0.400 | |
NAACP | White | 45.1 | 48.1 | 3.0 | 0.576 | |
White | White | 81.9 | 79.5 | -2.4 | 0.130 | |
Lyndon Johnson | White | 54.9 | 57.1 | 2.1 | 0.424 | |
Black | White | 56.6 | 58.3 | 1.7 | 0.389 | |
Richard Nixon | White | 64.3 | 62.8 | -1.5 | 0.195 | |
Democratic Party | White | 68.7 | 67.3 | -1.4 | 0.187 | |
Police | White | 87.9 | 86.5 | -1.4 | 0.146 |
Political_Object | Race | Before_Affect | After_Affect | Difference | P_value | Significant |
---|---|---|---|---|---|---|
Police | Black | 3.7 | 31.0 | 27.3 | 0.0114 |
|
Richard Nixon | Black | 22.2 | 48.3 | 26.1 | 0.0736 | |
National Politicians | Black | 7.4 | 24.1 | 16.7 | 0.0956 | |
White | Black | 11.1 | 24.1 | 13.0 | 0.2059 | |
Republican Party | Black | 22.2 | 31.0 | 8.8 | 0.4386 | |
NAACP | Black | 11.1 | 3.4 | -7.7 | 0.3173 | |
Lyndon Johnson | Black | 11.1 | 17.2 | 6.1 | 0.4795 | |
Democratic Party | Black | 0.0 | 3.4 | 3.4 | 0.3173 | |
Black | Black | 0.0 | 0.0 | 0.0 | NA | NA |
Black | White | 10.4 | 16.7 | 6.2 | 0.2967 | |
Democratic Party | White | 9.9 | 15.4 | 5.5 | 0.3545 | |
Richard Nixon | White | 19.8 | 24.4 | 4.6 | 0.8162 | |
Lyndon Johnson | White | 28.6 | 32.7 | 4.1 | 0.9215 | |
National Politicians | White | 7.1 | 9.6 | 2.5 | 0.7055 | |
Police | White | 3.8 | 5.8 | 1.9 | 0.6171 | |
White | White | 0.0 | 1.9 | 1.9 | 0.0833 | |
Republican Party | White | 8.8 | 9.6 | 0.8 | 0.8575 | |
NAACP | White | 30.8 | 31.4 | 0.6 | 0.4945 |
Political_Object | Race | Affect_Type | Before_Affect | After_Affect | Difference | P_value |
---|---|---|---|---|---|---|
Police | Black | Negative | 3.7 | 31 | 27.3 | 0.0114 |
# Load necessary libraries
library(tidyverse)
# Function to calculate variance ratio and perform Levene's test
analyze_polarization <- function(object, race) {
obj_data <- data %>%
filter(Political_Object == object, Race == race) %>%
select(Time, Positive, Sample_Size) %>%
mutate(
Positive_Count = round(Positive * Sample_Size / 100),
Negative_Count = Sample_Size - Positive_Count
)
# Calculate variance for before and after
var_before <- var(c(rep(1, obj_data$Positive_Count[obj_data$Time == "B"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "B"])))
var_after <- var(c(rep(1, obj_data$Positive_Count[obj_data$Time == "A"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "A"])))
# Prepare data for Levene's test
levene_data <- data.frame(
affect = c(rep(1, obj_data$Positive_Count[obj_data$Time == "B"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "B"]),
rep(1, obj_data$Positive_Count[obj_data$Time == "A"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "A"])),
time = factor(c(rep("B", sum(obj_data$Sample_Size[obj_data$Time == "B"])),
rep("A", sum(obj_data$Sample_Size[obj_data$Time == "A"]))))
)
# Perform Levene's test
levene_result <- leveneTest(affect ~ time, data = levene_data)
tibble(
Political_Object = object,
Race = race,
Variance_Before = var_before,
Variance_After = var_after,
Variance_Ratio = var_after / var_before,
Levene_Statistic = levene_result$`F value`[1],
Levene_P_value = levene_result$`Pr(>F)`[1]
)
}
# Run analysis for all combinations of Political Object and Race
polarization_results <- crossing(
Political_Object = unique(data$Political_Object),
Race = unique(data$Race)
) %>%
pmap_dfr(~analyze_polarization(..1, ..2))
# Display results
polarization_results %>%
mutate(
Significant = ifelse(Levene_P_value < 0.05, "*", ""),
Variance_Ratio = round(Variance_Ratio, 3),
Levene_P_value = format.pval(Levene_P_value, digits = 3)
) %>%
arrange(Race, desc(abs(Variance_Ratio - 1))) %>%
knitr::kable(digits = 3, caption = "Within-Group Polarization Analysis: Before vs After MLK Assassination") %>%
kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
Political_Object | Race | Variance_Before | Variance_After | Variance_Ratio | Levene_Statistic | Levene_P_value | Significant |
---|---|---|---|---|---|---|---|
Police | Black | 0.103 | 0.251 | 2.450 | 7.130 | 0.00999 |
|
NAACP | Black | 0.231 | 0.148 | 0.640 | 1.929 | 0.17062 | |
Democratic Party | Black | 0.179 | 0.123 | 0.686 | 0.661 | 0.41975 | |
Black | Black | 0.131 | 0.096 | 0.733 | 0.247 | 0.62091 | |
White | Black | 0.217 | 0.259 | 1.194 | 2.040 | 0.15901 | |
Richard Nixon | Black | 0.251 | 0.222 | 0.884 | 0.559 | 0.45791 | |
Republican Party | Black | 0.259 | 0.234 | 0.903 | 1.061 | 0.30764 | |
National Politicians | Black | 0.256 | 0.234 | 0.913 | 0.567 | 0.45479 | |
Lyndon Johnson | Black | 0.199 | 0.190 | 0.951 | 0.023 | 0.88003 | |
National Politicians | White | 0.226 | 0.192 | 0.850 | 2.837 | 0.09307 | |
White | White | 0.149 | 0.164 | 1.099 | 0.305 | 0.58111 | |
Police | White | 0.107 | 0.117 | 1.097 | 0.142 | 0.70658 | |
Republican Party | White | 0.214 | 0.201 | 0.938 | 0.415 | 0.52004 | |
Democratic Party | White | 0.216 | 0.221 | 1.024 | 0.072 | 0.78793 | |
Richard Nixon | White | 0.231 | 0.235 | 1.018 | 0.077 | 0.78094 | |
Black | White | 0.247 | 0.245 | 0.990 | 0.103 | 0.74797 | |
Lyndon Johnson | White | 0.249 | 0.247 | 0.991 | 0.150 | 0.69845 | |
NAACP | White | 0.249 | 0.251 | 1.009 | 0.307 | 0.57998 |
# Visualization of polarization
ggplot(polarization_results, aes(x = reorder(Political_Object, Variance_Ratio), y = Variance_Ratio, fill = Race)) +
geom_col(position = position_dodge()) +
geom_text(aes(label = ifelse(Levene_P_value < 0.05, "*", "")),
position = position_dodge(width = 0.9),
vjust = -0.5, size = 5) +
coord_flip() +
facet_wrap(~ Race, scales = "free_y") +
labs(
title = "Change in Within-Group Variance After MLK Assassination",
subtitle = "Ratio of After/Before Variance (> 1 indicates increased polarization)\n* indicates statistically significant change (p < 0.05)",
x = "Political Object",
y = "Variance Ratio (After / Before)",
fill = "Race"
) +
theme_minimal() +
theme(legend.position = "none")
# Function to analyze polarization using kurtosis and KS test
analyze_polarization <- function(object, race) {
obj_data <- data %>%
filter(Political_Object == object, Race == race) %>%
select(Time, Positive, Sample_Size) %>%
mutate(
Positive_Count = round(Positive * Sample_Size / 100),
Negative_Count = Sample_Size - Positive_Count
)
# Create full distribution
before_dist <- c(rep(1, obj_data$Positive_Count[obj_data$Time == "B"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "B"]))
after_dist <- c(rep(1, obj_data$Positive_Count[obj_data$Time == "A"]),
rep(0, obj_data$Negative_Count[obj_data$Time == "A"]))
# Calculate kurtosis
K_before <- kurtosis(before_dist)
K_after <- kurtosis(after_dist)
# Perform KS test
ks_result <- ks.test(before_dist, after_dist)
tibble(
Political_Object = object,
Race = race,
K_Before = K_before,
K_After = K_after,
K_Ratio = K_after / K_before,
KS_Statistic = ks_result$statistic,
KS_P_value = ks_result$p.value
)
}
# Run analysis for all combinations of Political Object and Race
polarization_results <- crossing(
Political_Object = unique(data$Political_Object), # Use survey_data instead of data
Race = unique(data$Race)
) %>%
pmap_dfr(~analyze_polarization(..1, ..2))
# Display results
polarization_results %>%
mutate(
Significant = ifelse(KS_P_value < 0.05, "*", ""),
K_Ratio = round(K_Ratio, 3),
KS_P_value = format.pval(KS_P_value, digits = 3)
) %>%
arrange(Race, desc(abs(K_Ratio - 1))) %>%
knitr::kable(digits = 3, caption = "Kurtosis and KS Test Analysis: Before vs After MLK Assassination") %>%
kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
Political_Object | Race | K_Before | K_After | K_Ratio | KS_Statistic | KS_P_value | Significant |
---|---|---|---|---|---|---|---|
NAACP | Black | 1.500 | 4.008 | 2.672 | 0.161 | 0.2214 | |
Democratic Party | Black | 2.786 | 5.410 | 1.942 | 0.084 | 0.4966 | |
Police | Black | 7.125 | 1.123 | 0.158 | 0.303 | 0.0154 |
|
Black | Black | 4.924 | 7.782 | 1.580 | 0.045 | 0.7004 | |
Richard Nixon | Black | 1.142 | 1.672 | 1.464 | 0.282 | 0.0588 | |
White | Black | 1.796 | 1.005 | 0.559 | 0.186 | 0.1804 | |
Republican Party | Black | 1.005 | 1.426 | 1.419 | 0.137 | 0.4157 | |
National Politicians | Black | 1.050 | 1.426 | 1.358 | 0.211 | 0.1784 | |
Lyndon Johnson | Black | 2.207 | 2.461 | 1.115 | 0.018 | 1.0000 | |
National Politicians | White | 1.452 | 2.245 | 1.546 | 0.084 | 0.5900 | |
Republican Party | White | 1.694 | 2.008 | 1.185 | 0.032 | 1.0000 | |
White | White | 3.737 | 3.133 | 0.838 | 0.024 | 1.0000 | |
Police | White | 6.410 | 5.584 | 0.871 | 0.014 | 1.0000 | |
Democratic Party | White | 1.649 | 1.545 | 0.937 | 0.014 | 1.0000 | |
Richard Nixon | White | 1.356 | 1.281 | 0.945 | 0.015 | 1.0000 | |
Black | White | 1.071 | 1.114 | 1.041 | 0.017 | 1.0000 | |
Lyndon Johnson | White | 1.040 | 1.081 | 1.040 | 0.021 | 1.0000 | |
NAACP | White | 1.040 | 1.006 | 0.968 | 0.030 | 1.0000 |
# Visualization of kurtosis changes
ggplot(polarization_results, aes(x = reorder(Political_Object, K_Ratio), y = K_Ratio, fill = Race)) +
geom_col(position = position_dodge()) +
geom_text(aes(label = ifelse(KS_P_value < 0.05, "*", "")),
position = position_dodge(width = 0.9),
vjust = -0.5, size = 5) +
coord_flip() +
facet_wrap(~ Race, scales = "free_y") +
labs(
title = "Change in Kurtosis After MLK Assassination",
subtitle = "Ratio of After/Before Kurtosis (> 1 indicates more extreme views)\n* indicates statistically significant change in distribution (KS test p < 0.05)",
x = "Political Object",
y = "Kurtosis Ratio (After / Before)",
fill = "Race"
) +
theme_minimal() +
theme(legend.position = "none")