24 — Make Decisions

Bayesian A/B Testing#

ExperimentationStatistical 1 min read
◆ The PatternIs version B actually better? Credible intervals and probability of improvement

Bayesian A/B testing gives you what you actually want: "the probability that B is better than A." Unlike frequentist tests, you can check results early, get direct probability statements, and don't need fixed sample sizes.

P(B > A | data) = ∫ P(θB > θA) dθ
P(B > A) = direct probability of improvement. No p-values, no confusion.
Beta(a + successes, b + failures)
For conversion rates, the Beta-Binomial model gives exact posteriors. a=b=1 is a uniform prior.
// Interactive — two posteriors, see probability of B > A
A conversions50
B conversions60
P(B>A)—
Pattern bridge: Bayesian updating is the same Bayes' theorem from ML. Prior belief + data = posterior. This connects to market sentiment — prices update beliefs with every new trade.
Worked example

A converts 120 of 1,000 visitors, B 135 of 1,000. With uniform priors, the posterior probability that B is better is 84.3%, and the expected loss from choosing B — how much conversion you give up on average in the worlds where A is better — is just 0.12 percentage points. A decision rule on expected loss can ship B even though a significance test would not reject.

Where it misleads
  • “Probability that B is better” is not immune to peeking: stopping when it crosses a line still raises error rates unless the decision rule accounts for it.
  • Priors matter with little data; report how the conclusion changes under a sceptical prior.
  • A posterior on conversion says nothing about long-term effects such as retention or novelty wearing off.
In code
rng = np.random.default_rng(16)
a = rng.beta(1 + 120, 1 + 880, 200_000)              # 120 of 1,000 converted, uniform prior
b = rng.beta(1 + 135, 1 + 865, 200_000)              # 135 of 1,000
p_b_better = (b > a).mean()
expected_loss_b = np.maximum(a - b, 0).mean()        # what choosing B costs on average if wrong
Python, in your browser — edit the code and run it again

Assumes import numpy as np and import pandas as pd (and SciPy where imported). Each snippet simulates or makes up its own data, as the comments say.

The same idea with a library
# Python — Bayesian A/B test
from scipy.stats import beta
import numpy as np

# Observed data
a_conv, a_total = 50, 1000
b_conv, b_total = 60, 1000

# Posterior distributions (uniform prior)
post_a = beta(a_conv + 1, a_total - a_conv + 1)
post_b = beta(b_conv + 1, b_total - b_conv + 1)

# P(B > A) via simulation
samples = 100000
p_b_wins = (post_b.rvs(samples) > post_a.rvs(samples)).mean()
print(f"P(B > A) = {p_b_wins:.3f}")
Sources
  • Bayesian Data Analysis (3rd ed.), A. Gelman, J. B. Carlin, H. S. Stern, D. B. Dunson, A. Vehtari & D. B. Rubin, CRC Press, 2013
  • C. Stucchio, “Bayesian A/B Testing at VWO”, VWO whitepaper, 2015 — expected-loss decision rules
  • Trustworthy Online Controlled Experiments, R. Kohavi, D. Tang & Y. Xu, Cambridge University Press, 2020

Last reviewed October 2026 · How this site checks its content · Report a mistake

Linked from 9
← Previous
Bootstrap Methods
Open in the full reader, with the topic sidebar