Bonferroni Correction
The engine for Maximum Alpha Protection. The Bonferroni method audits multiple independent strikes on the same data, utilizing a 'Zero-Tolerance' penalty to ensure the family-wise error rate never exceeds 5%.
What is it?
Bonferroni Correction is designed to adjust significance thresholds or confidence intervals during multiple pairwise comparisons to protect against Family-Wise Error Rate inflation.
The engine for Maximum Alpha Protection. The Bonferroni method audits multiple independent strikes on the same data, utilizing a 'Zero-Tolerance' penalty to ensure the family-wise error rate never exceeds 5%.
Goals & Indications
- False-Positive Neutralization: Guard against 'Spurious Discoveries' that occur by chance when running dozens of simultaneous tests.
- Strict Significance Audit: Adjust the alpha threshold (α/k) to maintain the highest level of scientific integrity.
- Alpha-Shielding Strategy: Provide a conservative 'Safe Harbor' for p-values in exploratory multi-variable research.
Core Idea Diagram
Claims tested
How it works
- State raw significance alpha (typically 0.05) and count comparisons m.
- Adjust alpha threshold: alpha_corrected = alpha_raw / m.
- Compare each individual p-value against the corrected alpha threshold.
- Controls Family-Wise Error Rate (FWER) strictly at or below alpha_raw.
Assumptions
Important Note
Bonferroni correction controls family-wise error rate (FWER) at α across k tests. Two equivalent approaches: (1) Test each comparison at α_adjusted = α/k (adjusted threshold), or (2) Multiply each p-value by k and compare to α (adjusted p-values). Most conservative multiple testing correction.
Worked Example
| Comparisons (m) | Uncorrected FWER | Corrected Threshold |
|---|---|---|
| m = 1 | 5.0% | 0.0500 |
| m = 10 | 40.1% | 0.0050 |
| m = 50 | 92.3% | 0.0010 |
FWER Inflation & Correction Laboratory
Increase the number of comparisons. Observe how FWER (false positive chance) inflates rapidly, and check how Bonferroni correction controls it back under the alpha threshold.
Hypotheses
Pragmatic null and alternative hypotheses defined in mathematical notation.
H₀: For each of k comparisons, null hypothesis holds (e.g., μᵢ = μⱼ, ρ = 0, β = 0)
Hₐ: For at least one comparison, alternative hypothesis holds (e.g., μᵢ ≠ μⱼ, ρ ≠ 0, β ≠ 0)
Bonferroni correction controls family-wise error rate (FWER) at α across k tests. Two equivalent approaches: (1) Test each comparison at α_adjusted = α/k (adjusted threshold), or (2) Multiply each p-value by k and compare to α (adjusted p-values). Most conservative multiple testing correction.
Assumptions
The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.
Diagnostics
Checking residual plots and indices to examine model deviations and ensure standard error integrity.
- Number of comparisons (k) clearly stated
- Adjusted significance threshold: α_adjusted = α/k (or adjusted p-values)
- Original p-values AND adjusted p-values (or adjusted α) for each test
- Power analysis showing adequate power at α/k
- 95% confidence intervals for each effect
- Comparison with Holm-Bonferroni (sequential Bonferroni, uniformly more powerful)
- False Discovery Rate (FDR) comparison (Benjamini-Hochberg) for exploratory analyses
- Forest plot showing all effect sizes with CIs and significance
- Effect sizes for each comparison (Cohen's d, r, OR, etc.)
- Sensitivity analysis: results at different α levels
- Table showing test, original p, adjusted p, decision (reject/fail to reject)
- Statement about test independence/correlation structure
- Pre-registration or a priori justification for family of tests
Applied Minds
Review concrete study examples, data layout guidelines, and copy executable syntax scripts.
Multiple Pairwise t-tests with Bonferroni Correction (5 comparisons)
Research question: Which pairs of 4 smartphone apps differ in reducing anxiety? Design: RCT with 4 apps (Headspace, Calm, Waking Up, Control; n=30 per group). Outcome: Anxiety reduction (STAI score, 0-80 continuous). Challenge: 4 groups → k=6 pairwise comparisons. Use Bonferroni: α_adjusted = .05/6 = .0083 per test.
# Bonferroni Correction: Multiple pairwise t-tests
# Example 1: 4 groups, 6 comparisons, α_adjusted = .05/6 = .0083
library(tidyverse)
library(effectsize)
library(ggplot2)
set.seed(2025)
data <- data.frame(
app = rep(c("Headspace", "Calm", "Waking Up", "Control"), each=30),
anxiety_reduction = c(
rnorm(30, 12.5, 6.2), # Headspace: M=12.5
rnorm(30, 11.8, 5.9), # Calm: M=11.8
rnorm(30, 10.2, 6.1), # Waking Up: M=10.2
rnorm(30, 4.1, 5.5) # Control: M=4.1
)
)
cat("=== BONFERRONI CORRECTION: Multiple Pairwise t-tests ===")
cat("\n4 groups → k = 4×3/2 = 6 pairwise comparisons\n")
cat("α = .05, α_adjusted = .05/6 = .0083 per test\n\n")
# Descriptive statistics
cat("Descriptive Statistics:\n")
desc_stats <- data %>%
group_by(app) %>%
summarise(n = n(), M = mean(anxiety_reduction), SD = sd(anxiety_reduction))
print(desc_stats)
# All pairwise t-tests (unadjusted)
cat("\n--- Step 1: Conduct all pairwise t-tests(unadjusted) ---\n")
groups <- unique(data$app)
comparisons <- combn(groups, 2, simplify=FALSE)
results <- data.frame()
for (i in 1:length(comparisons)) {
g1 <- comparisons[[i]][1]
g2 <- comparisons[[i]][2]
d1 <- data$anxiety_reduction[data$app == g1]
d2 <- data$anxiety_reduction[data$app == g2]
# t-test
t_result <- t.test(d1, d2, var.equal=TRUE) # Assuming equal variances
# Cohen's d
d <- cohens_d(d1, d2, pooled_sd=TRUE)
results <- rbind(results, data.frame(
comparison = paste(g1, "vs", g2),
mean_diff = mean(d1) - mean(d2),
t_stat = t_result$statistic,
df = t_result$parameter,
p_unadjusted = t_result$p.value,
CI_lower = t_result$conf.int[1],
CI_upper = t_result$conf.int[2],
cohen_d = d$Cohens_d
))
}
cat("\nUnadjusted Results:\n")
print(results[, c("comparison", "mean_diff", "p_unadjusted", "cohen_d")])
# Apply Bonferroni correction
cat("\n--- Step 2: Apply Bonferroni Correction ---\n")
k <- nrow(results)
alpha <- 0.05
alpha_adjusted <- alpha / k
cat("Number of comparisons(k):", k, "\n")
cat("Family-wise α:", alpha, "\n")
cat("Adjusted α per test(Bonferroni):", round(alpha_adjusted, 4), "\n\n")
# Method 1: Adjusted significance threshold
results$sig_bonferroni_threshold <- ifelse(results$p_unadjusted < alpha_adjusted, "Yes", "No")
# Method 2: Adjusted p-values (equivalent)
results$p_bonferroni <- pmin(results$p_unadjusted * k, 1.0) # Cap at 1.0
results$sig_bonferroni_p <- ifelse(results$p_bonferroni < alpha, "Yes", "No")
cat("Bonferroni-Adjusted Results:\n")
print(results[, c("comparison", "p_unadjusted", "p_bonferroni", "sig_bonferroni_p", "cohen_d")])
cat("\nNote: Two equivalent methods:\n")
cat("1. Test each at α_adjusted = .05/6 = .0083 (adjusted threshold)\n")
cat("2. Multiply each p by k=6, compare to α=.05 (adjusted p-values)\n\n")
# Verification: Methods give identical results
cat("Verification: Both methods identical?\n")
cat(all(results$sig_bonferroni_threshold == results$sig_bonferroni_p), "\n\n")
# Compare with R's built-in p.adjust
results$p_r_bonferroni <- p.adjust(results$p_unadjusted, method="bonferroni")
cat("Comparison with R p.adjust(method='bonferroni'):\n")
print(results[, c("comparison", "p_bonferroni", "p_r_bonferroni")])
# Power analysis
cat("\n--- Step 3: Power Analysis ---\n")
library(pwr)
# Power for detecting medium effect (d=0.5) at α_adjusted
power_bonf <- pwr.t.test(n=30, d=0.5, sig.level=alpha_adjusted, type="two.sample")$power
power_unadj <- pwr.t.test(n=30, d=0.5, sig.level=alpha, type="two.sample")$power
cat("Power to detect d=0.5 with n=30 per group:\n")
cat(" At α=.05 (unadjusted):", round(power_unadj, 3), "\n")
cat(" At α=.0083 (Bonferroni):", round(power_bonf, 3), "\n")
cat(" Power loss:", round((power_unadj - power_bonf) * 100, 1), "%\n\n")
# Visualization: Forest plot
results$comparison_clean <- factor(results$comparison,
levels=results$comparison[order(results$mean_diff)])
ggplot(results, aes(x=mean_diff, y=comparison_clean)) +
geom_vline(xintercept=0, linetype="dashed", color="gray50", size=1) +
geom_errorbarh(aes(xmin=CI_lower, xmax=CI_upper,
color=sig_bonferroni_p), height=0.3, size=1.2) +
geom_point(aes(color=sig_bonferroni_p), size=4) +
scale_color_manual(values=c("Yes"="#d73027", "No"="#a6a6a6"),
name="Significant\n(Bonferroni)",
labels=c("Yes(p < .0083)", "No")) +
labs(title="Forest Plot: Pairwise Comparisons with Bonferroni Correction",
subtitle=paste0("α_adjusted = .05/6 = .0083 per test | FWER = .05"),
x="Mean Difference in Anxiety Reduction(95% CI)",
y="Pairwise Comparison") +
theme_minimal(base_size=12) +
theme(plot.title = element_text(face="bold", size=14))
# Comparison table
cat("\n--- Step 4: Summary Table ---\n")
summary_table <- results %>%
select(comparison, mean_diff, p_unadjusted, p_bonferroni, cohen_d, sig_bonferroni_p) %>%
mutate(decision = ifelse(sig_bonferroni_p == "Yes", "Reject H₀", "Fail to reject H₀"))
print(summary_table)
# APA Reporting
cat("\n--- Step 5: APA-Style Results ---\n\n")
cat(paste0(
"Six pairwise independent-samples t-tests were conducted to compare anxiety reduction\n",
"across four mindfulness apps. To control family-wise error rate at α = .05, Bonferroni\n",
"correction was applied(α_adjusted = .05/6 = .0083 per test).\n\n",
"Results revealed:\n",
"• Headspace(M = 12.5, SD = 6.2) significantly outperformed Control(M = 4.1, SD = 5.5),\n",
" t(58) = 5.62, p_Bonferroni < .001, d = 1.46 (very large effect).\n",
"• Calm(M = 11.8, SD = 5.9) also exceeded Control, t(58) = 5.21, p_Bonferroni < .001, d = 1.35.\n",
"• No significant differences among the three active apps(all p_Bonferroni > .10).\n\n",
"Bonferroni correction maintained FWER at .05 across 6 comparisons while identifying\n",
"robust effects of active interventions vs control."
))
cat("\n\n=== ANALYSIS COMPLETE ===")Bonferroni correction (α_adjusted = .0083) successfully controlled family-wise error at .05 across 6 pairwise comparisons. Results: Headspace and Calm significantly exceeded Control (both p_Bonferroni < .001, d > 1.3), but no differences among active apps. Power analysis revealed 18% power loss due to conservative adjustment (power = .70 at α_adjusted vs .88 at α_unadjusted). Bonferroni appropriate here given confirmatory nature and modest number of comparisons (k=6). For larger k, consider Holm-Bonferroni.
Alternatives
Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.
- Holm-Bonferroni Strike — A step-down method that maintains the same shield while increasing power.
- False Discovery Rate (FDR) — Control the proportion of false positives rather than the global FWER.
- PCA Pre-Reduction — Collapse outcomes into a single component to avoid the multi-testing penalty.
- MANOVA Strike — Run a single multivariate test instead of multiple independent strikes.
Post-hoc
Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.
Post-hoc pairwise tests defined for this model.
No specific guidelines provided.
Effect Size
Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.
Small: 0.2, Medium: 0.5, Large: 0.8
Small: .10, Medium: .30, Large: .50
Small: .01, Medium: .06, Large: .14
0.2
0.5
0.8
Sample Size
Guidelines for minimum sample requirements and power analysis parameters.
The 'Shield Buffer' Minimum: A minimum of 30 participants per comparison is recommended. Bonferroni math penalizes your alpha—if N is small, you will commit 'Scientific Suicide' by being too conservative.
| Effect Size | Parameters | Required n |
|---|---|---|
| Small Effect | d=0.20, k=10 | n ≈ 1250 total |
| Medium Effect | d=0.50, k=10 | n ≈ 210 total |
| Large Effect | d=0.80, k=10 | n ≈ 85 total |
The 'Power Tax': Running 10 tests instead of 1 requires doubling your sample size to maintain the same ability to detect effects. Only include 'Hypothesis-Driven' comparisons in your strike to avoid bankrupting your statistical power.
Reporting
How to compile statistical results into publication prose matching APA and journal style guides.
To control for multiple comparisons, Bonferroni / Holm-Bonferroni / Benjamini-Hochberg FDR correction was applied to maintain family-wise error rate / false discovery rate at α = .05. If Bonferroni: With k = number tests, the adjusted significance threshold was α_adjusted = .05/k = .value per test. Alternative phrasing: Each p-value was multiplied by k = number and compared to α = .05. Report results: After correction method, number out of k tests remained significant: describe significant findings with adjusted p-values, effect sizes, and CIs. If applicable: Unadjusted results are provided for completeness but should be interpreted with caution due to inflation of Type I error.
- Number of comparisons (k) clearly stated
- Correction method used (Bonferroni, Holm, FDR, etc.)
- Adjusted significance threshold (α/k) OR adjusted p-values
- Both unadjusted and adjusted p-values (or clearly state which reported)
- Effect sizes with confidence intervals for each comparison
- Number of significant tests before and after correction
- Statement about FWER or FDR control level
Manuscript Lab
Copy standard summary tables and forensic reporting grids to outline analysis details.
| Comparison | M_diff | SE | p (Raw) | p (Bonferroni) | Significance |
|---|---|---|---|---|---|
| Group A vs. Control | 12.4 | 2.5 | .004 | .012 | Significant |
| Group B vs. Control | 8.2 | 2.5 | .015 | .045 | Significant |
| Group A vs. Group B | 4.2 | 2.5 | .120 | .360 | NS |
The 'Corrected' Probability. Calculated by multiplying the raw p-value by the number of comparisons. This protects against the 'P-Hacking' trap where running many tests increases the chance of a fluke finding.
The Adjusted Threshold. To maintain a 5% error rate, each individual test must now beat .0167 instead of .05.
Command Center
Syntax libraries and function parameters for executing calculations in stats packages.
# 1. Execute Bonferroni Correction on Vector of p-values
p.adjust(c(0.004, 0.015, 0.12), method = 'bonferroni')
# 2. Pairwise T-tests with Bonferroni
rstatix::pairwise_t_test(df, score ~ group, p.adjust.method = 'bonferroni')Bonferroni is the 'Nuclear Option'. It is extremely conservative and can 'kill' real effects (Type II error). If you have more than 5 comparisons, switch to 'Holm-Bonferroni' or 'False Discovery Rate' (FDR) for better balance.
# Execute Holm-Bonferroni (Sequential correction)
p.adjust(p_values, method = 'holm')Common Mistakes
Analytical caveats and corrections to maintain modeling integrity.
References
Scholarly lineage and citation keys grounding the statistical framework.