Kruskal-Wallis Test
Nonparametric Tests
Nonparametric One-Way ANOVA for Multiple Groups
When you need to compare three or more independent groups without assuming normality, the Kruskal-Wallis test extends the Mann-Whitney U to multiple samples. It ranks all observations together and tests whether groups share the same distribution.
-
Agricultural Research — Compare crop yields across multiple fertilizers with skewed distributions
-
Education — Assess student performance across teaching methods with ordinal outcomes
-
Healthcare — Compare recovery times across multiple treatment protocols
When ANOVA's normality assumption breaks, Kruskal-Wallis carries the comparison forward.
The Kruskal-Wallis test is the nonparametric alternative to one-way ANOVA. It tests whether k independent groups have the same distribution (or equivalently, the same median when distributions are identically shaped).
DfKruskal-Wallis Test
A nonparametric test that determines whether k independent groups come from the same distribution, without assuming normality.
Kruskal-Wallis H Statistic
Here,
- =The Kruskal-Wallis test statistic
- =Total number of observations
- =Number of groups
- =Sum of ranks in group i
- =Sample size of group i
import numpy as np
from scipy import stats
import scikit_posthocs as sp # pip install scikit-posthocs
import matplotlib.pyplot as plt
np.random.seed(42)
# Test: Does pain relief differ across 3 medication types?
# Data is not normally distributed (skewed)
drug_a = np.random.lognormal(2.0, 0.6, 25) # pain scores
drug_b = np.random.lognormal(2.3, 0.5, 25)
drug_c = np.random.lognormal(2.6, 0.7, 25)
# Kruskal-Wallis
H, p = stats.kruskal(drug_a, drug_b, drug_c)
df = 3 - 1 # k-1
print(f"Kruskal-Wallis H({df}) = {H:.4f}, p = {p:.4f}")
print(f"Decision: {'Reject H0 — groups differ' if p < 0.05 else 'Fail to reject H0'}")
# Effect size: eta-squared for Kruskal-Wallis
n = len(drug_a) + len(drug_b) + len(drug_c)
eta2 = (H - df + 1) / (n - df)
print(f"Effect size ?²_KW = {eta2:.4f}")
# If significant -> post-hoc pairwise comparisons (Dunn's test)
try:
import scikit_posthocs as sp
data_combined = [drug_a, drug_b, drug_c]
posthoc = sp.posthoc_dunn(data_combined, p_adjust='bonferroni')
print("\nDunn's Post-hoc Test (Bonferroni corrected):")
print(posthoc.round(4))
except ImportError:
# Manual Mann-Whitney pairwise
pairs = [('A vs B', drug_a, drug_b), ('A vs C', drug_a, drug_c), ('B vs C', drug_b, drug_c)]
bonf_alpha = 0.05 / 3
for name, g1, g2 in pairs:
_, p_mw = stats.mannwhitneyu(g1, g2, alternative='two-sided')
print(f"{name}: p={p_mw:.4f} -> {'Significant' if p_mw < bonf_alpha else 'Not significant'} (Bonferroni a={bonf_alpha:.4f})")
# Box plots
fig, ax = plt.subplots(figsize=(8, 5))
ax.boxplot([drug_a, drug_b, drug_c], labels=['Drug A', 'Drug B', 'Drug C'], patch_artist=True)
ax.set_title(f'Pain Scores by Drug\nKruskal-Wallis H={H:.3f}, p={p:.4f}')
ax.set_ylabel('Pain Score')
plt.tight_layout()
plt.savefig('kruskal_wallis.png', dpi=150)
plt.show()
Post-Hoc Testing
When the Kruskal-Wallis test is significant, you must use post-hoc tests (like Dunn's test with Bonferroni correction) to determine which specific groups differ.
Key Takeaways
Summary: Kruskal-Wallis Test
-
Nonparametric alternative to one-way ANOVA — uses ranks
-
Assumes independence, ordinal+ data, and identically shaped distributions between groups
-
If significant: use Dunn's test (post-hoc) with Bonferroni or BH correction
-
More robust than ANOVA for skewed or heavy-tailed distributions
-
Less powerful than ANOVA when normality truly holds