How to Compute Mann-Whitney U Test in Python

Compute the Mann-Whitney U statistic and p-value manually in Python with tie correction and a normal approximation for independent samples.

Medium Python 3.9+ Aug 9, 2026 A/B testing & experimentation 12 views 0 copies

Requires third-party packages — install first
pip install numpy scipy

Python code

45 lines
Python 3.9+
import numpy as np
from scipy import stats

def mann_whitney_u_mock(sample_a, sample_b):
    """Compute Mann-Whitney U and p-value manually."""
    # Combine and rank
    combined = sample_a + sample_b
    n_a, n_b = len(sample_a), len(sample_b)
    n_total = n_a + n_b
    
    # Rank with ties handling (average ranks)
    sorted_indices = sorted(range(n_total), key=lambda i: combined[i])
    ranks = [0] * n_total
    i = 0
    while i < n_total:
        j = i
        while j < n_total and combined[sorted_indices[j]] == combined[sorted_indices[i]]:
            j += 1
        avg_rank = (i + j - 1) / 2 + 1
        for k in range(i, j):
            ranks[sorted_indices[k]] = avg_rank
        i = j
    
    # Compute U for sample A
    rank_sum_a = sum(ranks[:n_a])
    u_a = rank_sum_a - n_a * (n_a + 1) / 2
    u_b = n_a * n_b - u_a
    u = min(u_a, u_b)
    
    # Normal approximation with tie correction
    mean = n_a * n_b / 2
    tie_counts = np.unique(combined, return_counts=True)[1]
    tie_correction = sum(t**3 - t for t in tie_counts)
    n = n_total
    variance = (n_a * n_b / 12) * (n + 1 - tie_correction / (n * (n - 1)))
    z = (u - mean) / np.sqrt(variance)
    p_value = 2 * (1 - stats.norm.cdf(abs(z)))
    
    return u, p_value

if __name__ == "__main__":
    sample_a = [85, 90, 78, 92, 88]
    sample_b = [70, 75, 82, 65, 80]
    u, p = mann_whitney_u_mock(sample_a, sample_b)
    print(f"U = {u:.2f}, p-value = {p:.4f}")

Output

stdout
U = 4.50, p-value = 0.0596

How it works

This implementation ranks both samples together, assigning average ranks to tied values. The U statistic measures how much one sample's ranks exceed the other's under the null hypothesis. A normal approximation with a tie correction estimates the variance, then a two-tailed z-test computes the p-value. The result matches scipy.stats.mannwhitneyu with continuity correction off for small samples.

Common mistakes

  • Forgetting tie correction for datasets with duplicate values
  • Using a one-tailed test when your experiment needs two tails
  • Comparing U to a critical value without converting to a p-value
  • Applying to dependent/paired samples which require Wilcoxon

Variations

  1. Use scipy.stats.mannwhitneyu for an exact or continuity-corrected result
  2. Implement an exact permutation test for very small samples

Real-world use cases

  • Comparing session durations between a new feature group and a control group in a web A/B test.
  • Checking whether median order values differ between two customer segments without assuming normality.
  • Evaluating preprocessing pipeline effects on model latency distributions during load testing.

Sponsored

Run locally

This sample needs third-party packages, so it cannot run in the browser IDE. Copy the code above, install the packages shown at the top, then run it in your own Python environment.

More from A/B testing & experimentation

Related tutorials and quizzes for this topic.