Multiple Comparisons › FWER-Controlling Methods
Machine Learning from ScratchAll chapters Multiple Comparisons › FWER-Controlling MethodsOne section · Sections in this chapter4 / 12 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Bonferroni Correction
The Bonferroni correction is the simplest and most widely known method for controlling FWER. The idea is mathematically elegant: if you want the overall error rate to be at most across tests, make each individual test more stringent.
The method: To test hypotheses while controlling FWER at level :
- Reject hypothesis if
- Equivalently, compute adjusted p-values:
Mathematical justification: The Bonferroni inequality states that for any events :
Let be the event that test produces a false positive. If we use threshold for each test:
Properties:
- Always valid (makes no assumptions about test dependence)
- Very conservative, especially for large
- Power decreases as increases
- Simple to implement and explain
def bonferroni_correction(p_values, alpha=0.05):
"""
Apply Bonferroni correction for multiple comparisons.
Parameters
----------
p_values : array-like
Raw p-values from multiple tests
alpha : float
Desired FWER level
Returns
-------
adjusted_p : ndarray
Bonferroni-adjusted p-values
rejected : ndarray
Boolean array indicating which hypotheses to reject
"""
p = np.asarray(p_values)
m = len(p)
adjusted_p = np.minimum(p * m, 1.0)
rejected = p < (alpha / m)
return adjusted_p, rejectedŠidák Correction
The Šidák correction is slightly less conservative than Bonferroni and is exact when tests are independent.
The method: For independent tests at FWER level :
- Reject hypothesis if
Mathematical justification: If tests are independent and we want FWER = :
Solving for the per-test significance level:
Comparison with Bonferroni: For tests at :
- Bonferroni:
- Šidák:
The difference is small but Šidák is slightly more powerful. However, Šidák requires independence while Bonferroni is always valid.
def sidak_correction(p_values, alpha=0.05):
"""
Apply Šidák correction for multiple comparisons.
Requires test independence for exact FWER control.
"""
p = np.asarray(p_values)
m = len(p)
alpha_adj = 1 - (1 - alpha) ** (1 / m)
# Adjusted p-values
adjusted_p = 1 - (1 - p) ** m
rejected = p < alpha_adj
return adjusted_p, rejectedHolm's Step-Down Procedure
Holm's method (1979) is uniformly more powerful than Bonferroni while still controlling FWER. The key insight is that we can be less stringent on tests with larger p-values.
The algorithm:
- Order p-values:
- For :
- If , stop and reject all hypotheses with smaller p-values
- Otherwise, continue to the next p-value
- If all p-values pass their thresholds, reject all hypotheses
Mathematical justification: The adjusted thresholds form a step-down sequence:
The first comparison uses the Bonferroni threshold, but subsequent comparisons are less stringent. This maintains FWER control because once we fail to reject at step , we know that hypothesis has , and remaining hypotheses have even larger p-values.
def holm_correction(p_values, alpha=0.05):
"""
Apply Holm's step-down procedure for FWER control.
More powerful than Bonferroni while maintaining FWER control.
"""
p = np.asarray(p_values)
m = len(p)
# Get sorted indices
sorted_idx = np.argsort(p)
p_sorted = p[sorted_idx]
# Calculate Holm thresholds
thresholds = alpha / (m - np.arange(m))
# Find first non-significant result
rejected_sorted = np.zeros(m, dtype=bool)
for j in range(m):
if p_sorted[j] > thresholds[j]:
break
rejected_sorted[j] = True
# Map back to original order
rejected = np.zeros(m, dtype=bool)
rejected[sorted_idx] = rejected_sorted
# Calculate adjusted p-values
adjusted_sorted = np.zeros(m)
for j in range(m):
adjusted_sorted[j] = min(
max(p_sorted[: j + 1] * (m - np.arange(j + 1))), 1.0
)
adjusted_p = np.zeros(m)
adjusted_p[sorted_idx] = adjusted_sorted
return adjusted_p, rejectedHochberg's Step-Up Procedure
Hochberg's method (1988) is similar to Holm but works in the opposite direction (step-up instead of step-down). It's more powerful than Holm but requires the assumption that tests are independent or positively correlated.
The algorithm:
- Order p-values:
- For (starting from largest):
- If , reject all hypotheses with p-values
- Otherwise, continue to the next smaller p-value
When to use:
- When tests are independent or positively correlated
- When you want more power than Holm
- Not valid for negatively correlated tests
Comparing FWER Methods
