How to run a Wilcoxon signed-rank test in SciPy (with Code)
The Short Answer (TL;DR)
Calculate the paired differences in the intended direction, then pass them to scipy.stats.wilcoxon with an explicit alternative and method. The exact example below has eight nonzero differences with distinct absolute values and tests symmetry of the difference distribution about zero.
from scipy import stats
differences = [1, 2, -3, 4, 5, -6, 7, 8]
print(stats.wilcoxon(differences, alternative="two-sided", method="exact", zero_method="wilcox", nan_policy="raise"))
Detailed Implementation
This example uses synthetic data and does not need a network connection. Install the recorded package versions in a separate Python environment before running it.
python -m pip install numpy==2.5.4 scipy==1.18.1
from pathlib import Path
import numpy as np
from scipy import stats
# Each position refers to the same unit before and after measurement.
before = np.array([20, 25, 30, 35, 40, 45, 50, 55])
after = np.array([21, 27, 27, 39, 45, 39, 57, 63])
if before.ndim != 1 or before.shape != after.shape or before.size == 0:
raise ValueError("Align nonempty, one-dimensional paired measurements.")
if not np.isfinite(before).all() or not np.isfinite(after).all():
raise ValueError("Resolve missing or nonfinite paired measurements explicitly.")
d = after-before # Positive differences represent increases.
if np.any(d==0) or np.unique(np.abs(d)).size != d.size:
raise ValueError("This exact example requires nonzero differences without absolute-rank ties.")
result = stats.wilcoxon(d, alternative="two-sided", method="exact",
zero_method="wilcox", correction=False, nan_policy="raise")
ranks = stats.rankdata(np.abs(d))
w_plus = ranks[d>0].sum()
w_minus = ranks[d<0].sum()
report = (
f"Paired differences (after minus before): {d.tolist()}\n"
f"Positive rank sum: {w_plus:.0f}\n"
f"Negative rank sum: {w_minus:.0f}\n"
f"Two-sided statistic: {result.statistic:.0f}\n"
f"Exact two-sided p-value: {result.pvalue:.8g}\n"
)
print(report)
Path("wilcoxon-signed-rank-output.txt").write_text(report, encoding="utf-8")
The report shows eight aligned differences, positive and negative rank sums of 27 and 9, and a two-sided statistic of 9. Exhaustive enumeration of all 256 sign assignments reproduces the exact p-value.
Which direction should the differences use?
Align measurements by unit and calculate after minus before when a positive value should mean an increase. Keep that convention in the report. Different units must contribute independent paired differences. The integer example avoids floating-point subtraction ambiguity; with measured decimals, respect the known measurement precision when deciding whether absolute differences are actually tied.
What do zeros and equal absolute differences change?
The example guards against zero differences and equal absolute differences before using the untied exact method. The wilcox zero convention discards zeros, while other conventions treat their ranks differently. With zeros or ties, method="exact" does not retain the same exact reference distribution. Use a suitable alternative, such as a configured permutation procedure or a justified asymptotic approximation, and state the convention.
Which null hypothesis is being tested?
The signed-rank test assesses whether the distribution of paired differences is symmetric about zero. A median-shift interpretation relies on an appropriate symmetry or location model; symmetry is not guaranteed by calling a rank test. The method does not establish that every unit changes by the same amount. Report the observed changes as well as the p-value, and preserve the pairing rather than using an independent-sample test.
Mathematical Context
Rank the absolute nonzero paired differences. Here the absolute differences are the integers one through eight, so their ranks are also one through eight. The positive ranks sum to 27 and the negative ranks to 9. For a two-sided call, SciPy reports the smaller sum, which is 9. Under independent continuous differences symmetric about zero, each of the 256 sign assignments is equally likely conditional on the absolute values. The checks enumerate those assignments, calculate the positive-rank sums, and reconstruct the two-sided probability from inclusive tails. Reversing every sign leaves the two-sided result unchanged; a one-sided alternative would depend on the chosen direction.
\[ W_+=\sum_{d_i>0}r_i,\qquad W_-=\sum_{d_i<0}r_i,\qquad W=\min(W_+,W_-) \]
Reproducibility
Executed with Python 3.12.14; numpy 2.5.4, scipy 1.18.1. Automated checks confirm the stated output properties.
Primary documentation
- SciPy Wilcoxon differences, zeros and exact-method limits
- SciPy average ranks for absolute differences
- SciPy permutation-method configuration