Wilcoxon Signed-Rank Test
Maths: Statistics for machine learning
3 min read
Published Oct 22 2025, updated Aug 17 2026
Guide Sections
Guide Comments
The Wilcoxon Signed-Rank Test is a non-parametric statistical test used to compare two related (paired) samples to determine whether their population mean ranks differ.
It’s the non-parametric equivalent of the paired t-test, used when the differences between pairs are not normally distributed.
In simple terms:
“The Wilcoxon test checks whether the median difference between paired observations is zero — without assuming the data follow a normal distribution.”
When to Use It
- Two sets of paired data - e.g., before/after, left/right, matched subjects
- Data are ordinal or continuous - but not necessarily normal
- Goal - Test if the median of the differences is zero
When not to Use It
- Independent groups - Use Mann–Whitney U test instead
- Normal differences - You can use a paired t-test instead
Example Scenario
“Did a training course significantly improve students’ test scores?”
Each student is tested before and after the course.
If the differences in scores aren’t normally distributed, use the Wilcoxon Signed-Rank Test instead of a paired t-test.
Hypotheses
- H₀ (Null Hypothesis) - The median difference between pairs = 0 (no change)
- H₁ (Alternative Hypothesis) - The median difference ≠ 0 (a change exists)
How It Works (Step-by-Step)
- Calculate the difference (d) for each pair:

- Ignore pairs where di = 0 (no change).
- Take the absolute value of each difference.
- Rank the absolute differences (1 = smallest).
- Assign the original signs (+/–) back to each rank.
- Compute:
- W+ = sum of positive ranks
- W− = sum of negative ranks
- The test statistic (W) is the smaller of W+ and W−.
- Compare W to the critical value (from Wilcoxon table)
or compute the p-value.
If p < 0.05, reject H₀ → the difference is statistically significant.
Example in Python
Let’s test if a meditation program reduced stress levels (lower scores = less stress):
import numpy as npfrom scipy.stats import wilcoxon# Stress levels before and after a programbefore = np.array([72, 65, 78, 70, 69, 75, 80, 74, 77, 73])after = np.array([68, 60, 74, 66, 65, 72, 78, 70, 73, 69])# Perform Wilcoxon Signed-Rank Teststat, p = wilcoxon(before, after, alternative='two-sided')print(f"Wilcoxon Statistic: {stat:.3f}")print(f"P-value: {p:.4f}")Interpretation:
- If p < 0.05, reject H₀ → the program significantly reduced stress levels.
- If p ≥ 0.05, fail to reject H₀ → no significant change.
Alternative Options:
wilcoxon(before, after, alternative='greater')'greater'→ test if after > before'less'→ test if after < before'two-sided'→ test for any change (default)
If most ranks are negative (after < before),
the sum of negative ranks (W₋) will be much larger — indicating a significant decrease.
Advantages
- Doesn’t require normality
- Handles outliers well
- Works with small samples
- Simple and intuitive (rank-based)
Limitations
- Only works for paired data
- Less powerful than the paired t-test when data are normal
- Assumes data are symmetrically distributed about the median difference
Python code
import pingouin as pgimport numpy as npimport pandas as pd# Example: same subjects measured before and after treatmentnp.random.seed(42)df = pd.DataFrame({ 'Col3': np.random.normal(loc=50, scale=5, size=20), 'Col4': np.random.normal(loc=52, scale=5, size=20) })# Run Wilcoxon signed-rank test (paired samples)results = pg.wilcoxon(x=df['Col3'], y=df['Col4'], alternative='two-sided')print(results)Output:
W-val alternative p-val RBC CLESWilcoxon 73.0 two-sided 0.245487 -0.304762 0.42