Wilcoxon Signed-Rank Test
Nonparametric Tests
The Nonparametric Alternative to the Paired t-Test
When your data violates normality assumptions, the Wilcoxon signed-rank test provides a robust alternative for comparing paired observations. It's essential for analyzing ordinal data, Likert scales, and small samples where normality cannot be verified.
- Clinical Trials — Assess treatment effects on patient symptom ratings before and after intervention
- Quality Control — Compare measurements from two instruments without assuming normal distributions
- Survey Research — Analyze pre-post responses on non-normal ordinal scales
When normality fails, ranks still speak the truth about paired differences.
The nonparametric alternative to the paired t-test. Tests whether the median of differences equals zero, without assuming normality.
DfWilcoxon Signed-Rank Test
A nonparametric test that evaluates whether the median of pairwise differences between two related samples equals zero, without assuming normality of the differences.
import numpy as np
from scipy import stats
np.random.seed(42)
before = np.array([85, 90, 78, 92, 88, 76, 95, 82, 87, 91, 73, 89])
after = np.array([78, 82, 75, 88, 85, 70, 89, 79, 80, 86, 68, 84])
differences = before - after
print(f"Median difference: {np.median(differences):.2f}")
# Wilcoxon signed-rank test
stat, p = stats.wilcoxon(before, after, alternative='two-sided')
print(f"W = {stat:.2f}, p = {p:.4f}")
# Compare with paired t-test
t, p_t = stats.ttest_rel(before, after)
print(f"Paired t-test: t={t:.4f}, p={p_t:.4f}")
# When to prefer Wilcoxon
print("\nUse Wilcoxon signed-rank when:")
print("* n is small and normality of differences can't be verified")
print("* Differences have heavy tails or outliers")
print("* Data is ordinal")
How It Works
- Compute differences dᵢ = x₁ᵢ − x₂ᵢ
- Remove zero differences
- Rank |dᵢ| (absolute values)
- Assign signs from original dᵢ
- W = sum of positive ranks (or negative ranks)
- Compare W to null distribution
# Manual computation
d = before - after
d_nonzero = d[d != 0]
n = len(d_nonzero)
ranks = stats.rankdata(np.abs(d_nonzero))
W_plus = ranks[d_nonzero > 0].sum()
W_minus = ranks[d_nonzero < 0].sum()
W = min(W_plus, W_minus)
print(f"W+ = {W_plus:.1f}, W- = {W_minus:.1f}, W = {W:.1f}")
print(f"n = {n}")
When to Use Wilcoxon
Use the Wilcoxon signed-rank test when sample sizes are small, normality of differences cannot be verified, differences have heavy tails or outliers, or data is ordinal.
Key Takeaways
Summary: Wilcoxon Signed-Rank Test
- Nonparametric equivalent of paired t-test — no normality assumption
- More powerful than sign test — uses magnitude of differences, not just direction
- Use when: small n, non-normal differences, ordinal data
- Exact test for small n; normal approximation for n greater than 25
- Effect size: matched-pairs rank biserial correlation r = W / (n(n+1)/2)