Sample Ratio Mismatch (SRM): How to Detect It With Chi-Square

⏱️ 3 min read 📈 Data Analysis

A sample ratio mismatch (SRM) is when the traffic split your A/B test actually delivered doesn't match the split you configured, for example 50/50 in setup but 49/51 in the logs. It's not a rounding quirk: an SRM means your randomization broke somewhere in the pipeline, and it invalidates every downstream result until you find and fix the cause.

Quick answer: Run a chi-square goodness-of-fit test on the observed counts against the expected split. For a 50/50 test with 24,500 in control and 25,500 in treatment (50,000 total), chi-square = Σ(O-E)²/E = (500²/25,000) + (500²/25,000) = 20.0, which gives p ≈ 0.000008 — a clear SRM. Because this check runs on every experiment, most teams use a stricter cutoff than the usual p < 0.05, commonly p < 0.001, to avoid flagging healthy tests as broken.

What is a sample ratio mismatch?

It's a statistically significant gap between the traffic allocation you set (say, 50/50 or 90/10) and the allocation your logs actually show. A small gap is expected from random noise; an SRM is a gap too large to be chance, which means something is systematically routing more users into one arm than the other, or dropping them unevenly before they get counted.

How do you test for an SRM?

Use a chi-square goodness-of-fit test comparing observed counts per variant to the expected counts under your configured split. The statistic is chi² = Σ(O-E)²/E, summed across variants, and for a two-arm test it has 1 degree of freedom.

from scipy.stats import chisquare

# 50,000 users, configured 50/50 split
observed = [24500, 25500]
expected = [25000, 25000]

chi2_stat, p_value = chisquare(observed, expected)
print(f"chi2 = {chi2_stat:.2f}, p = {p_value:.6f}")
# chi2 = 20.00, p = 0.000008

Sample size changes how much a given percentage skew matters. The same 5% relative imbalance (95 vs. 105 out of 200 users) gives chi² = 0.5 and p ≈ 0.48 — not significant, because there isn't enough data to distinguish it from noise. At 50,000 users, the same 5% imbalance would be enormous. This is exactly why you run the chi-square test instead of eyeballing the percentages.

Total usersObserved splitChi²p-valueSRM?
20095 / 1050.500.48No — too small to tell
20,0009,850 / 10,1504.500.034Borderline — flags at 0.05, not at 0.001
50,00024,500 / 25,50020.000.000008Yes — clear SRM

The middle row is why the threshold choice matters: at p < 0.05 it looks broken, at the stricter p < 0.001 convention it doesn't trigger an alarm yet. Neither answer is wrong; they're different tolerances for false alarms, discussed below.

What causes a sample ratio mismatch?

Almost every SRM traces back to something that treats the two variants differently before or during logging, not to the randomizer itself misbehaving.

Why does an SRM invalidate the whole test?

Because the mechanism that caused unequal counts almost never affects users at random — it correlates with device, network speed, geography, or bot-ness, and those same factors correlate with your outcome metric. An SRM is not a footnote to report alongside your results; it's a sign the two groups are no longer comparable, so any lift or drop you measured could be entirely an artifact of who got excluded, not what you changed.

Put differently, a valid A/B test depends on random assignment producing two groups that are identical in expectation except for the treatment. An SRM is direct evidence that assumption failed, which means the causal inference the whole test rests on is unsupported — no amount of statistical significance on the primary metric fixes that.

What's a safe SRM threshold to use?

Most experimentation platforms check for SRM automatically on every single test, and that changes the math on false positives. At the standard p < 0.05 cutoff, remember a p-value only tells you how surprising the data would be if the split really were 50/50 — it doesn't say the split is fine, and 1 in 20 perfectly healthy experiments would still get flagged just from noise; run that check across thousands of experiments and you generate a steady stream of false alarms. That's why the common industry convention is a much stricter cutoff for this specific check, typically p < 0.001 (chi² critical value ≈ 10.83 for 1 degree of freedom, versus ≈ 3.84 at p < 0.05).

A stricter threshold trades a slightly higher chance of missing a small, real mismatch for far fewer false alarms on a check you're running constantly — a reasonable trade for an automated gate, less reasonable if you're manually investigating a specific test you already suspect is broken.

Common mistakes with SRM checks

Pro Tip: Don't just check the overall split — segment it by device type, browser, and traffic source. An SRM that only shows up on mobile Safari points straight at a redirect or rendering bug in that path, and finding it in minutes beats re-running a two-week test from scratch.

← Back to Data Analysis Tips