Atlas
statminds
NonparametricThe underlying model family class (e.g. GLM, linear model, categorical matrix, log-linear).Parametric ReferenceStatistical methods that assume a specific probability distribution family (typically normal).12-stage workflow

Runs Test for Randomness

Tests if sequence of binary outcomes is random; detects patterns, trends, or autocorrelation..

Model familyNonparametric
Hypothesistwo-tailed
AliasesWald-Wolfowitz runs test · Runs test for randomness · Wald-Wolfowitz single-sample runs test
G1
description
Visual Overview Dashboard
1

What is it?

Runs Test evaluates whether a binary sequence is ordered randomly, testing for systematic dependencies over time.

2

When to use it

  • Sequence Randomness: Check binary data transitions (e.g. residual signs, heads/tails).
  • Independence check: Verify zero autocorrelation assumptions.
3

Core Idea

A "run" is a consecutive block of identical states. Too few runs indicate clustering; too many runs indicate systematic oscillation:

Run 1 (A)Run 2 (B)
4

Hypotheses

H0: The binary sequence ordering is random
Ha: The sequence ordering is non-random (clustered or oscillating)
5

How it works

  1. Count occurrences of State A (n1) and State B (n2).
  2. Count observed run boundaries (R).
  3. Compute expected runs: E(R) = (2n1n2/N) + 1.
  4. Calculate Z-score of deviation. Low p-value indicates non-randomness.
6

Assumptions

📊 Binary Sequence: Data consists of ordered binary categories.
7

Effect Size

Represented by the **ratio of observed to expected runs**. Ratio < 1 indicates clustering, while Ratio > 1 indicates fast oscillation.

8

Quick Example

SeqObserved RunsExpectedp-value
A B A B A B64.00.180 (Oscillating)
A A A B B B24.00.015 (Clustered)
Interactive Sandbox

Sequence Runs Live Laboratory

Change the probability bias of State A to see how clustering blocks form.

Presets
State A Bias0.50
Sequence length30
Observed binary sequence runs timeline (Amber: A, Green: B)Tick marks identify run transitions
Calculations Output
MetricValue
Count State A11
Count State B19
Observed Runs (R)11
Expected Runs E(R)14.93
p-value (two-tailed)0.1176
Statistical Verdict
❌ Random Sequence
Runs occur at a standard random rate (R = 11, p = 0.118). Fail to reject H0.
01Hypothesis test logic

Hypotheses

Pragmatic null and alternative hypotheses defined in mathematical notation.

A hypothesis is a question sharpened to a point. Ambiguity is the enemy of inference.
Logic Core
Null · H₀

H₀: Sequence is random (independent observations)

Alternative · Hₐ

Hₐ: Sequence is NOT random (has pattern/trend/autocorrelation)

Why it matters two-tailed

Counts 'runs' (uninterrupted sequences of same value). Too few runs = clustering/trend/positive autocorrelation. Too many runs = oscillation/alternation/negative autocorrelation. Can test one-tailed for specific patterns.

02Model diagnostics

Assumptions

The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.

Build your analysis on rock, not sand. Verify the mathematical foundation before building the model.
Integrity Shield
5
Assumptions
0
Critical / High Severity
How to check
Quick
Verify two categories exist (e.g., success/failure, above/below median)
Rigorous
Count unique values; if continuous, establish cut point (median, mean, fixed threshold)
If violated
Consequence: Cannot compute runs; test undefined for categorical data with >2 categories Fixes: Dichotomize continuous data at median; for categorical, create meaningful binary split Alternatives: If >2 ordered categories, use other randomness tests (e.g., autocorrelation tests)
How to check
Quick
Verify data has meaningful sequence (time, space, position)
Rigorous
Confirm sequence reflects data collection order; check for proper time/position indexing
If violated
Consequence: Test meaningless - randomness concept requires sequence. Runs test inappropriate. Fixes: Establish proper ordering if possible; otherwise runs test not applicable Alternatives: For unordered data, use chi-square test of independence or other non-sequential tests
chi square independence
How to check
Quick
Count n₁ (number of first category) and n₂ (number of second category)
Rigorous
Check if both n₁ ≥ 10 and n₂ ≥ 10; if smaller, use exact distribution
If violated
Consequence: Normal approximation unreliable; p-values may be inaccurate Fixes: Use exact runs test distribution tables (available for small n₁, n₂) Alternatives: Permutation test for runs; Fisher's exact test for small samples
fisher exact
How to check
Quick
Check ratio n₁/n₂; ideally between 1:2 and 2:1
Rigorous
Compute min(n₁, n₂) / max(n₁, n₂); if < 0.3, test loses substantial power
If violated
Consequence: Reduced power; harder to detect non-randomness; test becomes conservative Fixes: Use alternative cut point (e.g., terciles instead of median) to balance groups Alternatives: Autocorrelation tests (ACF), Durbin-Watson test (for regression residuals)
How to check
Quick
Clarify whether goal is randomness detection vs autocorrelation quantification
Rigorous
Consider alternative tests if need to quantify autocorrelation magnitude (not just detect it)
If violated
Consequence: Runs test detects but doesn't quantify autocorrelation; less powerful than specialized tests Fixes: Use runs test as screening; follow up with ACF/PACF for quantification Alternatives: Autocorrelation function (ACF), Ljung-Box test, Durbin-Watson test for autocorrelation
03Residual Forensics

Diagnostics

Checking residual plots and indices to examine model deviations and ensure standard error integrity.

Trust, but verify. The outliers often hold more truth than the averages.
System Health
Essential checks
  1. Number of runs (R) — Count of uninterrupted sequences of identical valuesR observed; compare to expected E(R). Low R = clustering, high R = alternation. Range: 2 to n (2 = perfect clustering, n = perfect alternation).
  2. Expected runs E(R) — Expected number of runs under randomness: E(R) = (2n₁n₂)/(n₁+n₂) + 1Baseline for comparison. Depends on n₁ and n₂. Balanced samples maximize E(R).
  3. Variance of runs — Var(R) = (2n₁n₂(2n₁n₂ - n₁ - n₂)) / ((n₁+n₂)²(n₁+n₂-1)); used for Z-score calculationQuantifies sampling variability. Larger samples → smaller variance → more precise test.
  4. Z-score (standardized runs statistic) — Z = (R - E(R)) / √Var(R); standardized deviation from expectedZ < -2: too few runs (clustering). Z > 2: too many runs (alternation). |Z| < 2: consistent with randomness.
  5. p-value — Probability of observing R as extreme under H₀ (randomness)p < 0.05 → reject randomness. But consider context: block randomization produces low R intentionally.
Recommended checks
  1. Sequence plot (time series visualization) — Visual inspection of sequence for patterns, trends, clustersLook for: runs of same value, temporal trends, periodic patterns, regime changes. Essential complement to test.
  2. Autocorrelation function (ACF) — Quantifies autocorrelation at multiple lags; more informative than runs testACF(1) > 0: positive autocorr (clustering). ACF(1) < 0: negative autocorr (alternation). More powerful than runs test.
  3. Run lengths distribution — Histogram of lengths of runs (how long sequences persist)Long runs suggest clustering. All runs length 1 = perfect alternation. Geometric distribution expected under randomness.
  4. Comparison with Durbin-Watson test — Alternative autocorrelation test (for regression residuals); compare resultsIf both agree → strong evidence. Durbin-Watson more powerful for lag-1 autocorrelation. Use for residuals.
  5. Cumulative proportions plot — Shows if category proportions stable over time (e.g., cum. prop. of heads should → 0.5)Systematic drift suggests bias. Should fluctuate randomly around expected proportion.
  6. Expected vs observed runs confidence interval — Shows sampling variability of R; 95% CI for R under randomnessIf observed R outside CI → significant departure. Visual assessment of effect magnitude.
  7. Permutation test (exact p-value) — Compute exact p-value via permutation (for small samples or verification)More accurate than normal approximation for small n. Use as gold standard for validation.
  8. Temporal stability check (split-half analysis) — Test randomness separately in first vs second half of sequenceIf one half random, other not → temporal change in process. Identifies when non-randomness emerges.
  9. Rolling/moving runs test — Apply runs test to sliding window to detect localized patternsIdentifies when/where randomness violated. E.g., early clustering, later random.
04Live Instances

Applied Minds

Review concrete study examples, data layout guidelines, and copy executable syntax scripts.

Theory is the map. Practice is the terrain. Simulation bridges the gap.
Applied Wisdom
Example 01

Test Randomness of Coin Flips

Test if sequence of 100 coin flips (H/T) is random or shows patterns (e.g., clustering, alternation). Classic application of runs test.

# Runs Test for Randomness: Coin Flip Sequence
# Test if sequence of H/T is random

library(randtests)  # runs.test
library(ggplot2)
library(gridExtra)

set.seed(42)

# Generate coin flip data
# Scenario: Mostly random, but slight tendency toward clustering
flips <- sample(c("H", "T"), 100, replace = TRUE, prob = c(0.5, 0.5))
# Add some artificial clustering (for demonstration)
flips[20:25] <- "H"  # Cluster of heads
flips[60:67] <- "T"  # Cluster of tails

# Convert to binary (0/1 for calculations)
flips_binary <- ifelse(flips == "H", 1, 0)

cat("RUNS TEST FOR RANDOMNESS: COIN FLIPS\n")
cat("======================================\n\n")

# ====================
# 1. DATA SUMMARY
# ====================

cat("DATA SUMMARY\n")
cat("=============\n\n")

cat("  Total flips: n =", length(flips), "\n")
n_heads <- sum(flips == "H")
n_tails <- sum(flips == "T")
cat("  Heads: n₁ =", n_heads, "(", round(n_heads/length(flips)*100, 1), "%)\n")
cat("  Tails: n₂ =", n_tails, "(", round(n_tails/length(flips)*100, 1), "%)\n\n")

cat("  Sample sequence(first 30):")
cat("\n   ", paste(flips[1:30], collapse = " "), "\n\n")

# ====================
# 2. CHECK ASSUMPTIONS
# ====================

cat("ASSUMPTION CHECKS\n")
cat("==================\n\n")

# A1: Binary data
cat("1. Binary data:\n")
cat("   Categories: H and T\n")
cat("   ✓ Binary outcomes\n\n")

# A2: Sequential
cat("2. Sequential order:\n")
cat("   ✓ Flips ordered by time\n\n")

# A3: Sample size
cat("3. Sample size check:\n")
cat("   n₁ =", n_heads, ", n₂ =", n_tails, "\n")
if (n_heads >= 10 & n_tails >= 10) {
  cat("   ✓ Both n₁, n₂ ≥ 10 (normal approximation valid)\n\n")
} else {
  cat("   ⚠ Small sample - use exact test\n\n")
}

# A4: Balance
cat("4. Category balance:\n")
ratio <- min(n_heads, n_tails) / max(n_heads, n_tails)
cat("   Ratio min/max =", round(ratio, 3), "\n")
if (ratio >= 0.5) {
  cat("   ✓ Reasonably balanced\n\n")
} else {
  cat("   ⚠ Imbalanced - may reduce power\n\n")
}

# ====================
# 3. COUNT RUNS MANUALLY
# ====================

cat("RUNS CALCULATION\n")
cat("=================\n\n")

# Count runs
runs_count <- 1
for (i in 2:length(flips)) {
  if (flips[i] != flips[i-1]) {
    runs_count <- runs_count + 1
  }
}

cat("  Observed runs(R):", runs_count, "\n\n")

# Expected runs under randomness
expected_runs <- (2 * n_heads * n_tails) / (n_heads + n_tails) + 1
cat("  Expected runs E(R):", round(expected_runs, 2), "\n\n")

# Variance of runs
var_runs <- (2 * n_heads * n_tails * (2 * n_heads * n_tails - n_heads - n_tails)) / 
            ((n_heads + n_tails)^2 * (n_heads + n_tails - 1))
sd_runs <- sqrt(var_runs)
cat("  SD of runs σ(R):", round(sd_runs, 2), "\n\n")

# Z-score
z_score <- (runs_count - expected_runs) / sd_runs
cat("  Z-score:", round(z_score, 3), "\n")
cat("  Interpretation:\n")
if (z_score < -2) {
  cat("    Too FEW runs → clustering/trend(positive autocorrelation)\n")
} else if (z_score > 2) {
  cat("    Too MANY runs → oscillation/alternation(negative autocorrelation)\n")
} else {
  cat("    Runs count consistent with randomness\n")
}
cat("\n")

# ====================
# 4. RUNS TEST (FORMAL)
# ====================

cat("RUNS TEST\n")
cat("==========\n\n")

# Note: randtests::runs.test expects factor
flips_factor <- factor(flips_binary)
runs_result <- runs.test(flips_factor, alternative = "two.sided")

cat("Test Results:\n")
cat("  Observed runs:", runs_result$statistic, "\n")
cat("  Expected runs:", round(expected_runs, 2), "\n")
cat("  Z-statistic:", round(runs_result$statistic, 3), "\n")
cat("  p-value:", round(runs_result$p.value, 4), "\n\n")

alpha <- 0.05
if (runs_result$p.value < alpha) {
  cat("Decision: REJECT H₀ (α = 0.05)\n")
  cat("Interpretation: Sequence is NOT random\n")
  if (runs_count < expected_runs) {
    cat("  Pattern: Too few runs → CLUSTERING\n\n")
  } else {
    cat("  Pattern: Too many runs → ALTERNATION\n\n")
  }
else {
  cat("Decision: FAIL TO REJECT H₀ (α = 0.05)\n")
  cat("Interpretation: Sequence consistent with randomness\n\n")
}

# ====================
# 5. AUTOCORRELATION CHECK
# ====================

cat("AUTOCORRELATION ANALYSIS\n")
cat("=========================\n\n")

# Lag-1 autocorrelation
acf_result <- acf(flips_binary, lag.max = 10, plot = FALSE)
cat("  Lag-1 autocorrelation:", round(acf_result$acf[2], 3), "\n")
if (abs(acf_result$acf[2]) > 0.2) {
  cat("  ⚠ Moderate autocorrelation detected\n\n")
} else {
  cat("  ✓ Low autocorrelation\n\n")
}

# ====================
# 6. VISUALIZATIONS (6 plots)
# ====================

# Plot 1: Sequence plot
sequence_df <- data.frame(
  Position = 1:length(flips),
  Outcome = flips,
  Binary = flips_binary
)

p1 <- ggplot(sequence_df, aes(x = Position, y = Binary, color = Outcome)) +
  geom_point(size = 2, alpha = 0.7) +
  geom_line(alpha = 0.3) +
  scale_color_manual(values = c("H" = "blue", "T" = "red")) +
  scale_y_continuous(breaks = c(0, 1), labels = c("T", "H")) +
  labs(title = "Coin Flip Sequence",
       subtitle = paste0("Observed runs: ", runs_count, ", Expected: ", 
                         round(expected_runs, 1)),
       x = "Flip Number", y = "Outcome") +
  theme_minimal() +
  theme(legend.position = "top")

# Plot 2: Runs distribution (under H₀)
p2_data <- data.frame(
  runs = seq(max(1, expected_runs - 4*sd_runs), expected_runs + 4*sd_runs, 0.5)
)
p2_data$density <- dnorm(p2_data$runs, mean = expected_runs, sd = sd_runs)

p2 <- ggplot(p2_data, aes(runs, density)) +
  geom_line(size = 1, color = "darkgreen") +
  geom_area(alpha = 0.3, fill = "darkgreen") +
  geom_vline(xintercept = runs_count, color = "red", linetype = "dashed", size = 1.2) +
  geom_vline(xintercept = expected_runs, color = "blue", linetype = "dotted", size = 1) +
  annotate("text", x = runs_count, y = max(p2_data$density) * 0.8, 
           label = paste0("Observed\nR = ", runs_count), 
           hjust = -0.1, color = "red") +
  labs(title = "Runs Distribution Under H₀",
       subtitle = "Red = Observed, Blue = Expected",
       x = "Number of Runs", y = "Density") +
  theme_minimal()

# Plot 3: Run lengths distribution
run_lengths <- rle(as.character(flips))
run_df <- data.frame(
  Length = run_lengths$lengths,
  Value = run_lengths$values
)

p3 <- ggplot(run_df, aes(x = Length, fill = Value)) +
  geom_histogram(binwidth = 1, alpha = 0.7, position = "dodge", color = "black") +
  scale_fill_manual(values = c("H" = "blue", "T" = "red")) +
  labs(title = "Run Lengths Distribution",
       subtitle = "Length of consecutive H or T sequences",
       x = "Run Length", y = "Frequency",
       fill = "Outcome") +
  theme_minimal() +
  theme(legend.position = "top")

# Plot 4: Cumulative proportion
cumulative_df <- data.frame(
  Position = 1:length(flips),
  CumProp_H = cumsum(flips == "H") / (1:length(flips)),
  CumProp_T = cumsum(flips == "T") / (1:length(flips))
)

p4 <- ggplot(cumulative_df, aes(x = Position)) +
  geom_line(aes(y = CumProp_H, color = "Heads"), size = 1) +
  geom_line(aes(y = CumProp_T, color = "Tails"), size = 1) +
  geom_hline(yintercept = 0.5, linetype = "dashed", color = "gray50") +
  scale_color_manual(values = c("Heads" = "blue", "Tails" = "red")) +
  labs(title = "Cumulative Proportions Over Time",
       subtitle = "Should converge to 0.5 if fair coin",
       x = "Flip Number", y = "Cumulative Proportion",
       color = "") +
  theme_minimal() +
  theme(legend.position = "top")

# Plot 5: Autocorrelation function (ACF)
acf_df <- data.frame(
  Lag = 1:10,
  ACF = acf_result$acf[2:11]
)

p5 <- ggplot(acf_df, aes(x = Lag, y = ACF)) +
  geom_col(fill = "steelblue", alpha = 0.7) +
  geom_hline(yintercept = 0, color = "black") +
  geom_hline(yintercept = c(-1.96/sqrt(length(flips)), 1.96/sqrt(length(flips))),
             linetype = "dashed", color = "blue") +
  labs(title = "Autocorrelation Function(ACF)",
       subtitle = "Blue lines = 95% significance bounds",
       x = "Lag", y = "Autocorrelation") +
  theme_minimal()

# Plot 6: Comparison to random sequences
set.seed(999)
n_sim <- 500
random_runs <- replicate(n_sim, {
  sim_flips <- sample(c("H", "T"), 100, replace = TRUE)
  sum(rle(sim_flips)$lengths > 0)
})

p6 <- ggplot(data.frame(Runs = random_runs), aes(Runs)) +
  geom_histogram(bins = 30, fill = "lightgray", color = "black", alpha = 0.7) +
  geom_vline(xintercept = runs_count, color = "red", linetype = "dashed", size = 1.2) +
  geom_vline(xintercept = expected_runs, color = "blue", linetype = "dotted", size = 1) +
  annotate("text", x = runs_count, y = 40, 
           label = paste0("Observed = ", runs_count), 
           hjust = -0.1, color = "red") +
  labs(title = "Simulated Runs Distribution(500 random sequences)",
       subtitle = paste0("Percentile of observed: ", 
                         round(mean(random_runs <= runs_count) * 100, 1), "%"),
       x = "Number of Runs", y = "Frequency") +
  theme_minimal()

grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 2)

# ====================
# 7. INTERPRETATION
# ====================

cat("\nINTERPRETATION\n")
cat("===============\n\n")

cat("The runs test assessed randomness of coin flip sequence.\n\n")

cat("Key findings:\n")
cat("  • Observed runs:", runs_count, "\n")
cat("  • Expected runs:", round(expected_runs, 2), "\n")
cat("  • Z-score:", round(z_score, 3), "\n")
cat("  • p-value:", round(runs_result$p.value, 3), "\n")
cat("  • Lag-1 autocorrelation:", round(acf_result$acf[2], 3), "\n\n")

if (runs_result$p.value < 0.05) {
  cat("Conclusion: Sequence is NOT random.\n")
  if (runs_count < expected_runs) {
    cat("  Pattern detected: TOO FEW runs\n")
    cat("  Interpretation: Clustering or trend present\n")
    cat("  Implication: Positive autocorrelation(outcomes tend to persist)\n")
  } else {
    cat("  Pattern detected: TOO MANY runs\n")
    cat("  Interpretation: Excessive alternation\n")
    cat("  Implication: Negative autocorrelation(outcomes tend to switch)\n")
  }
else {
  cat("Conclusion: Sequence is consistent with randomness.\n")
  cat("  No evidence of clustering, trends, or systematic patterns.\n")
  cat("  Coin flipping process appears to produce independent outcomes.\n")
}

cat("\nPractical notes:\n")
cat("  • Runs test is simple screening tool for randomness\n")
cat("  • Less powerful than ACF for detecting autocorrelation\n")
cat("  • Useful for quick visual + statistical check\n")
cat("  • Follow up with ACF/PACF for detailed autocorrelation analysis\n")
05Tactical Pivots

Alternatives

Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.

When the path is blocked, pivot. Rigor is not rigidity; it is the intelligent adaptation to reality.
Adaptive Strategy
Measurement Precision Ladder Ideal · Binary Sequence (+/-)
Ratio / Interval
Consider Durbin-Watson or ARIMA. Binary conversion of residuals (Dichotomization) ignores the magnitude of error.
Extreme Data Loss
Binary
Maintain Runs Test logic. The definitive audit for non-random temporal or spatial clustering.
Peak Signal
Multi-Nominal
Abandon Runs math. Use Join-Count Statistics or Markov-Chain audits to model multi-state randomness.
Information Suicide
Temporal Trajectory Audit Static Randomness Snapshot
Static Binary
Sequence audit.
Stay with Runs Test. Find if the overall 'Pulse' is random or systematic.
Autocorrelated
Continuous ghost.
Pivot to Ljung-Box Q-Test to audit autocorrelations across multiple temporal lags.
Adaptive Technical Safeguards · adaptive safeguards
imbalanced states detected
  • Conditional Runs Test — Adjust the p-value if one binary state (e.g., Success) is 3x more common than the other.
  • Wald-Wolfowitz Strike — The exact alternative for small imbalanced sequences.
residual ghosting
  • Breusch-Godfrey Test — Audit for higher-order serial dependence if the runs test reveals clustering.
06Adjusted Comparisons

Post-hoc

Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.

The omnibus test opens the door; post-hoc analysis explores the room.
Forensic Detail
Adjusted Comparisons
  • Compare with Wald-Wolfowitz runs test (two-sample version)
  • Examine runs above/below median separately
  • Use exact vs asymptotic p-values for small samples
  • Plot sequence to visually assess patterns
  • Compare with Ljung-Box test for autocorrelation
Interpretation Guidelines

Runs test assesses randomness of a sequence. Post-hoc tests are not applicable.

07Standardized scale impact

Effect Size

Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.

Significance is noise. Magnitude is the signal. Measure the impact, not just the probability.
Impact Magnitude
N/A
Recommended Measure
08Statistical Power

Sample Size

Guidelines for minimum sample requirements and power analysis parameters.

An underpowered study is an ethical failure. Respect the data by collecting enough of it.
Power Protocol
Floor Requirements

The 'Serial Independence' Minimum: A minimum of 20 observations is required. Randomness cannot be meaningfully distinguish from pattern in a sequence that is too short to allow for 'Runs' to emerge.

Effect SizeParametersRequired n
Small EffectSubtle Clusteringn ≈ 100
Medium EffectModerate Clusteringn ≈ 40
Large EffectSevere Clusteringn ≈ 20
Key considerations

The 'Binary Pivot': Runs test power is maximized when the two binary states (e.g., Above/Below Median) are balanced (50/50). If one state is rare (e.g., 90% Success), you must triple your sequence length to maintain discovery authority.

G*Power StrategyBenchmark: Non-parametric → Randomness (Runs). Parameters: Number of 'Switches', Expected vs Observed Runs, α = .05, Power = .80. Note: Power refers to the detection of 'Serial Correlation' or 'Cyclic Patterning'.
09APA narrative blueprint

Reporting

How to compile statistical results into publication prose matching APA and journal style guides.

Data does not speak for itself. It requires a translator. Be clear, be precise, be honest.
Narrative Arc
10Exhibit Builder

Manuscript Lab

Copy standard summary tables and forensic reporting grids to outline analysis details.

Table 1: Wald-Wolfowitz Runs Test for Sequence Randomness
Total RunsExpected Runsz-statisticp-valueResult
3250.5-3.82< .001NON-RANDOM (Clustered)
Note. N = 100 observations. Testing if values are randomly distributed around the median.
32 Runs (Expected 50.5)Identifies Serial Correlation. There are significantly fewer runs than expected, proving that values are 'Clustering' (high values follow high values).
Header glossary

The 'Switch' Count. The number of times the sequence crosses the median. Too few runs = Clustering; Too many runs = Alternating.

Independence Probability. If p < .05, the sequence has 'Memory' and the observations are not independent.

11Algorithmic Logic

Command Center

Syntax libraries and function parameters for executing calculations in stats packages.

Code is the modern laboratory. Clean execution ensures reproducible discovery.
Execution Engine
# 1. Execute Runs Test for randomness
randtests::runs.test(x)
Library stack
R
randtests
Python
statsmodels.stats.diagnostic
Elite Forensic Strike

Runs test detects non-random patterns like clustering or systematic oscillation. Always supplement with a sequence line plot to observe the cycle.

# Check for sequential autocorrelation to detect time dependency
acf(x, plot = TRUE)
12The Over-adjustment Trap

Common Mistakes

Analytical caveats and corrections to maintain modeling integrity.

Wisdom is learning from the failures of others. Anticipate the error before it occurs.
Defensive Logic
Why it's wrong
A run is an uninterrupted sequence of same values, not a single observation. Misunderstanding leads to incorrect interpretation.
The correction
Define run clearly: consecutive identical outcomes. E.g., H-H-H-T-T = 2 runs (3 H's, then 2 T's), not 5 runs.
Why it's wrong
Runs test is less powerful for detecting autocorrelation than ACF/PACF. It's a screening tool, not definitive autocorrelation measure.
The correction
Use runs test as quick screen for randomness. Follow up with ACF/PACF for quantifying autocorrelation magnitude and lag structure.
Why it's wrong
Test loses substantial power with imbalanced categories. Hard to detect non-randomness when one category dominates.
The correction
Prefer balanced dichotomization (median split). If imbalanced, acknowledge reduced power. Consider alternatives like ACF.
Why it's wrong
Too few runs = clustering/trend (positive autocorr). Too many runs = alternation (negative autocorr). Different interpretations!
The correction
Always report direction: R < E(R) indicates clustering; R > E(R) indicates alternation. Diagnose pattern type.
Why it's wrong
Normal approximation unreliable for small samples. P-values inaccurate; test invalid.
The correction
Use exact runs test tables for n₁, n₂ < 10. For larger samples, normal approximation appropriate.
Why it's wrong
Sequence plots reveal specific patterns (e.g., early trend, later randomness) that aggregate test misses.
The correction
Always plot sequence. Look for temporal patterns, runs clusters, systematic structure. Complements statistical test.
Why it's wrong
Runs test assumes linear sequence. Spatial data has 2D/3D structure - runs test ignores spatial relationships.
The correction
Use spatial randomness tests (Moran's I, Geary's C) for spatial data. Runs test only for sequential (1D) data.
Why it's wrong
Without E(R), readers can't assess deviation magnitude. Observed R = 30 could be high or low depending on E(R).
The correction
Always report: R (observed), E(R) (expected), and Z-score. Provides effect size context beyond p-value.
13Academic Lineage

References

Scholarly lineage and citation keys grounding the statistical framework.

We stand on the shoulders of giants. Honor the source of the method.
Academic Lineage
[1]
Wald, A., & Wolfowitz, J. (1940). On a test whether two samples are from the same population. Annals of Mathematical Statistics, 11(2), 147-162.
Original paper introducing the runs test (Wald-Wolfowitz test)
[2]
Bradley, J. V. (1968). Distribution-Free Statistical Tests. Prentice-Hall.
Comprehensive coverage of runs test with tables and examples
[3]
Siegel, S., & Castellan, N. J. (1988). Nonparametric Statistics for the Behavioral Sciences (2nd ed.). McGraw-Hill.
Classic textbook with detailed runs test explanation and applications
[4]
Gibbons, J. D., & Chakraborti, S. (2011). Nonparametric Statistical Inference (5th ed.). Chapman & Hall/CRC.
Modern treatment of runs test with power analysis and extensions
[5]
Ljung, G. M., & Box, G. E. P. (1978). On a measure of lack of fit in time series models. Biometrika, 65(2), 297-303.
Comparison of runs test with more powerful autocorrelation tests (Ljung-Box)
[6]
Matuszewski, L. J. (1996). The runs test and the randomness assumption in statistical inference. Quality and Quantity, 30(1), 17-33.
Practical guidance on runs test applications and limitations in research
statminds · RunsMind reference · v2.2 · updated 2026-01-1715 of 15 sections