Runs Test for Randomness

Nonparametric TestsNonparametric TestsFree Lesson

Advertisement

Runs Test for Randomness

The runs test (Wald-Wolfowitz test) checks whether a sequence of binary observations is randomly ordered or shows systematic patterns.

A run is a consecutive sequence of identical values.

import numpy as np
from scipy.stats import runstest_1samp
import matplotlib.pyplot as plt

# Example: Quality control — is the defect pattern random?
np.random.seed(42)

# Scenario 1: Truly random sequence
random_seq = np.random.choice([0, 1], size=50, p=[0.7, 0.3])

# Scenario 2: Systematic pattern (not random)
pattern_seq = np.array([0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
                         0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
                         0,0,0,1,0,0,0,1,0,0])

def runs_test(sequence, label):
    """Manual runs test computation."""
    n = len(sequence)
    n1 = sequence.sum()     # number of 1s
    n0 = n - n1             # number of 0s
    
    # Count runs
    runs = 1
    for i in range(1, n):
        if sequence[i] != sequence[i-1]:
            runs += 1
    
    # Expected runs and variance under H₀
    E_R = 2*n1*n0/n + 1
    Var_R = 2*n1*n0*(2*n1*n0 - n) / (n**2 * (n-1))
    z = (runs - E_R) / np.sqrt(Var_R)
    
    from scipy import stats as sc
    p = 2 * sc.norm.sf(abs(z))
    
    print(f"\n{label}:")
    print(f"  n₀={n0}, n₁={n1}, Runs={runs}")
    print(f"  E[R]={E_R:.2f}, Var[R]={Var_R:.4f}")
    print(f"  z={z:.4f}, p={p:.4f}")
    print(f"  Decision: {'NOT random (reject H₀)' if p < 0.05 else 'Appears random'}")
    return runs, z, p

r1, z1, p1 = runs_test(random_seq, "Random sequence")
r2, z2, p2 = runs_test(pattern_seq, "Systematic pattern")

# Visualize
fig, axes = plt.subplots(2, 1, figsize=(14, 6))
for ax, seq, label, p_val in zip(axes, [random_seq, pattern_seq],
                                   ['Random Sequence', 'Systematic Pattern'],
                                   [p1, p2]):
    colors = ['steelblue' if x == 0 else 'coral' for x in seq]
    ax.bar(range(len(seq)), seq, color=colors, edgecolor='black', linewidth=0.5)
    ax.set_title(f'{label} (p={p_val:.4f})')
    ax.set_ylabel('Value')
    ax.set_yticks([0, 1])
plt.tight_layout()
plt.savefig('runs_test.png', dpi=150)
plt.show()

Key Takeaways

  1. Runs test checks randomness in binary sequences
  2. Too few runs: positive autocorrelation (clustering)
  3. Too many runs: negative autocorrelation (alternating)
  4. Applications: quality control, testing for systematic trends, auditing
  5. For continuous data: convert to binary using median as cutpoint, then apply runs test

Advertisement

Need Expert Statistics Help?

Get personalized tutoring, dissertation support, or statistical consulting.

Advertisement