🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
Search courses…
💼 Servicesℹ️ About✉️ ContactView Pricing Plansfrom $10

Kruskal-Wallis Test — Nonparametric One-Way ANOVA

Nonparametric TestsNonparametric Tests🟢 Free Lesson

Advertisement

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).


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()

Key Takeaways

Need Expert Statistics Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement