How to run a Mann-Whitney U test in SciPy (with Code)
The Short Answer (TL;DR)
Use scipy.stats.mannwhitneyu for two independent samples and state the alternative and p-value method explicitly. For the small untied example below, method="exact" computes the two-sided null probability without a normal approximation.
from scipy import stats
x, y = [2, 3, 4, 5, 7], [1, 6, 8, 9, 10, 11]
print(stats.mannwhitneyu(x, y, alternative="two-sided", method="exact", 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
# Two independent synthetic samples, with no equal pooled values.
x = np.array([2, 3, 4, 5, 7], dtype=float)
y = np.array([1, 6, 8, 9, 10, 11], dtype=float)
if any(v.ndim != 1 or v.size == 0 or not np.isfinite(v).all() for v in [x,y]):
raise ValueError("Use nonempty, finite, one-dimensional samples.")
pooled = np.concatenate([x,y])
if np.unique(pooled).size != pooled.size:
raise ValueError("This exact example requires no ties; choose a tie-aware method.")
result = stats.mannwhitneyu(x, y, alternative="two-sided",
method="exact", nan_policy="raise")
u_x = result.statistic
u_y = x.size*y.size-u_x
report = (
f"Sample sizes: {x.size}, {y.size}\n"
f"U for first sample: {u_x:.0f}\n"
f"U for second sample: {u_y:.0f}\n"
f"Observed fraction of cross-sample pairs with x > y: {u_x/(x.size*y.size):.6f}\n"
f"Exact two-sided p-value: {result.pvalue:.8g}\n"
)
print(report)
Path("mann-whitney-output.txt").write_text(report, encoding="utf-8")
The first sample has U=6 and the second has U=24. The observed pairwise fraction is 0.2. The exact two-sided p-value agrees with enumeration of all 462 assignments of five pooled ranks to the first sample.
What does the U statistic describe?
SciPy returns U for the first sample. In this untied example it counts cross-sample pairs in which an x observation exceeds a y observation. Six of the thirty pairs have that ordering, so the observed fraction is 0.2. This describes these samples; it does not by itself establish a population effect, a causal direction or a probability that the null is true.
Why choose the method explicitly?
The exact calculation used here assumes no ties in the pooled data. The code checks that condition because requesting exact does not make tied observations untied. An asymptotic method applies a tie adjustment and optionally a continuity correction. For small samples with ties, consider a suitable permutation calculation through PermutationMethod, recording its resampling settings. Keep the method aligned with the design and data.
Is this a test of independent medians?
The independent-sample test concerns distributional ordering, with a location interpretation under additional assumptions about shape. Different spreads can make a median-only explanation misleading. A paired design needs a method that preserves pairs. Choose a one-sided alternative from the scientific question before examining the sample order; this worked example prespecifies a two-sided comparison.
Mathematical Context
Rank all eleven pooled observations from smallest to largest. The five x observations have ranks 2, 3, 4, 5 and 7, whose sum is 21. Subtract n_x(n_x+1)/2=15 to obtain U_x=6. The complementary statistic is n_x n_y-U_x=24. Under the continuous identical-distribution null with independent observations, the exact calculation treats each selection of five pooled ranks as equally likely. There are 462 such selections. The validation enumerates them and doubles the smaller inclusive tail probability, capped at one. With ties, pairwise comparisons assign half a count to equal values, but the untied exact reference calculation used in this example no longer applies unchanged.
\[ U_x=R_x-\frac{n_x(n_x+1)}{2},\qquad U_y=n_xn_y-U_x \]
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 Mann-Whitney U methods and interpretation
- SciPy permutation-method configuration
- SciPy pooled average ranks