F-Test for Equality of Variances
Tests H₀: σ₁² = σ₂² using the ratio of sample variances.
Under H₀, F follows an F-distribution with (n₁−1, n₂−1) degrees of freedom.
import numpy as np
from scipy import stats
np.random.seed(42)
group1 = np.random.normal(50, 8, 25) # σ=8
group2 = np.random.normal(52, 12, 30) # σ=12
# F-test
F = group1.var(ddof=1) / group2.var(ddof=1)
df1, df2 = len(group1)-1, len(group2)-1
# Two-tailed p-value
p_upper = stats.f.sf(F, df1, df2)
p_lower = stats.f.cdf(F, df1, df2)
p_value = 2 * min(p_upper, p_lower)
print(f"s₁² = {group1.var(ddof=1):.4f}, s₂² = {group2.var(ddof=1):.4f}")
print(f"F({df1}, {df2}) = {F:.4f}")
print(f"p-value = {p_value:.4f}")
# Levene's test (more robust — doesn't require normality)
stat_lev, p_lev = stats.levene(group1, group2)
print(f"
Levene's test: F={stat_lev:.4f}, p={p_lev:.4f}")
print("(Levene's is preferred — robust to non-normality)")
# Bartlett's test (sensitive to normality)
stat_bart, p_bart = stats.bartlett(group1, group2)
print(f"Bartlett's test: χ²={stat_bart:.4f}, p={p_bart:.4f}")
print("(Bartlett's is more powerful when normality holds)")
Choosing the Right Test
| Test | Normality Required | Robust | Use When |
|---|---|---|---|
| F-test | ✅ Yes (strictly) | ❌ No | Perfect normality |
| Levene's | ❌ No | ✅ Yes | Default choice |
| Bartlett's | ✅ Yes (approximately) | Moderate | Large samples, near-normal |
| Brown-Forsythe | ❌ No | ✅ Yes | Heavy tails |
Key Takeaways
- Use Levene's test as the default — it doesn't require normality
- F-test is sensitive to non-normality — type I error inflates severely
- Variance tests matter before ANOVA — need to confirm homogeneity of variances
- Large variance ratio (F >> 1 or F << 1) suggests unequal population variances
- Follow-up with Welch's t-test if variances are unequal