learn-bayesian-with-phoebe / Practitioner session 7 of 10
Learn Bayesian with Phoebe · Practitioner session 7 of 10

Bayesian A/B decisions

Sessions 1 to 6 built posteriors. Tonight two of them have to produce a decision. Halloway is running a new voucher design, B, against the current one, A, at Westfield, as one cumulative experiment: the first fifty customers per arm are the first fifty of the same three thousand. The true rates are written into the generator, A 0.150 and B 0.175, a lift of 2.5 points. After fifty customers per arm the observed rates were 0.100 and 0.200 and P(B > A) was 0.915, and the rule Halloway wrote down in advance said keep going. The rule is not on the probability. It is on the expected loss of the arm you would choose, E[max(theta_A - theta_B, 0)] for choosing B, a Monte Carlo over the two posteriors, and at n = 50 that was 0.0029 against a threshold of 0.001. At n = 100 it was 0.0005 and the rule stopped. The build-along computes both numbers in numpy, adds Kruschke's region of practical equivalence as a second rule, and then does the thing most write-ups skip: replays the experiment to count how often the rule stops on the wrong arm.

🔴 Hardest Data scientists and analysts Anyone who has to call an A/B test 45 min
0-3 · Welcome 3-16 · Probability, loss, ROPE 16-40 · Build-along: the rule and its error rate 40-45 · Q&A
Part 0

Where session 7 sits

Session 5 fitted one rate and session 6 fitted twelve at once. Tonight there are two, one per voucher, and the question is no longer what the rate is but which arm to ship. That is a decision, and a decision needs a stake as well as a probability. The whole session is the arithmetic that joins the two: the share of paired posterior draws in which B is ahead, and the average shortfall in the draws where it is not. Sessions 8 and 9 take the fitted model back to the data it came from, and then into the room where the number is read aloud.

Live = presented in the 45 minutes Self-study = the full depth, after class 🔍 Every diagram zooms

★ What you walk out with tonight. A function that takes two Beta posteriors and returns P(B > A) and both expected losses, reproducing the Westfield canon: 0.915 and 0.0029 at n = 50, 0.975 and 0.0005 at n = 100. A second decision rule, Kruschke's ROPE, that can say "no meaningful difference" out loud, which a probability of superiority never can. And the honest price of the stopping rule, counted rather than promised: across 200 seeded replays stopping at n = 100, 23 would have stopped and 2 of the 200 would have stopped on the wrong arm.

LiveTwo posteriors, one subtraction, twenty thousand times4 min▶

With flat Beta(1, 1) priors, each arm's posterior after the first fifty customers is exact and conjugate, session 1's arithmetic: five redemptions of fifty for A gives Beta(6, 46), ten of fifty for B gives Beta(11, 41). Draw 20,000 values from each. Pair them up. Every pair is one way the world could be, given what was seen, and every question about the decision is a question about the pairs.

P(B > A) is the share of pairs in which B's draw is higher: 0.915 at n = 50. Expected loss of choosing B is the mean over all pairs of max(theta_A - theta_B, 0): in the pairs where A is ahead, how far ahead, and zero everywhere else. At n = 50 that mean was 0.0029. The mirror, E[max(theta_B - theta_A, 0)], is the expected loss of choosing A, and it was 0.0984. Three numbers from the same two arrays, and only the losses are in the units of the business. Halloway's rule, written before the test, was to stop when the expected loss of the chosen arm fell below 0.001. At 0.0029, three times the threshold, it said keep going.

Real world

The product lead at Halloway read 0.915 as "91.5 percent sure" and wanted to ship. The analyst did not argue about the probability. She showed the 8.5 percent of pairs where A won, and how far A won by in those pairs at fifty customers, and the average of that shortfall was the 0.0029. "That is what we forfeit on average if B is the wrong call. The rule says under a tenth of a point." Fifty customers later the same arithmetic gave 0.0005 and she shipped it herself.

Self-studyThe words that will keep coming back3 min▶
  • P(B > A). The share of paired posterior draws in which B is ahead. A probability of superiority. Not a decision.
  • Expected loss of choosing B. E[max(theta_A - theta_B, 0)], estimated as the mean of that quantity over the paired draws. In the units of the rate. The mirror is the expected loss of choosing A.
  • Threshold. The expected loss below which Halloway agreed to act: 0.001. Set before the data, dated, and not moved.
  • Stopping rule. Stop when the expected loss of the arm you would choose is below the threshold. Controls the cost of a wrong stop, not how often one happens.
  • ROPE. Region of practical equivalence, Kruschke (2013). A band of differences too small to act on, compared against the posterior interval for the difference. The rule that can accept "no difference".
  • 89 percent equal-tailed interval. The 5.5 and 94.5 percent quantiles of the draws. The ArviZ 1.x default, and the interval every ROPE comparison tonight uses.
  • Wrong-stop rate. Across replays of the experiment from fresh seeds, the share that stopped on the arm that is truly worse. At Westfield, 2 of 200 at n = 100. A count, never a promise.
  • Cumulative experiment. The n = 100 customers include the n = 50 customers. One test read at six points, not six tests.
Part 1 · covers expected loss as a Monte Carlo and the ROPE as a second rule

The posterior of the difference

8 min live

Every decision number tonight lives on one distribution: theta_B - theta_A, the posterior of the difference, built by subtracting the paired draws. P(B > A) is the mass to the right of zero. The expected loss of choosing B is the mass to the left of zero, weighted by how far left it sits. The ROPE is a band around zero that the interval either clears, sits inside, or straddles. The figure draws that one distribution at n = 50 and at n = 100, with the loss region shaded.

Westfield, the posterior of theta_B minus theta_A. The shaded tail is where choosing B is the wrong call. n = 50 per arm · observed A 0.100, B 0.200 -0.10 0 0.10 0.20 0.30 A ahead the loss region P(B > A) = 0.915 E[loss] choose B 0.0029 E[loss] choose A 0.0984 rule: keep going · 0.0029 is three times the 0.001 threshold n = 100 per arm · observed A 0.100, B 0.200 -0.10 0 0.10 0.20 0.30 a thinner tail, a smaller loss P(B > A) = 0.975 E[loss] choose B 0.0005 E[loss] choose A 0.0989 rule: stop, choose B · 0.0005 is below 0.001 Grey band at zero: an example ROPE of half a point either side, an illustration and not a Halloway rule. The dashed line is zero difference.
🔍 Click to zoom - the probability is the area right of zero. The expected loss is the area left of zero, weighted by distance. Between n = 50 and n = 100 the first moved by six points and the second fell by a factor of six.
LiveWhy the loss falls faster than the probability rises3 min▶

Between n = 50 and n = 100 the observed rates did not move: 0.100 and 0.200 at both sizes. P(B > A) went from 0.915 to 0.975, a gain of six points. The expected loss of choosing B went from 0.0029 to 0.0005, a fall by a factor of six. Both come from the same left tail of the difference distribution, and the figure shows why they move at different speeds. The probability counts the tail's mass. The loss weights that mass by its distance from zero. When the posterior narrows, the tail loses mass and every remaining piece of it moves closer to zero, so the loss is hit twice.

That is the property that makes expected loss the right quantity to threshold. It is monotone in the evidence when a real difference exists, it is in the units of the stake, and it punishes a wide posterior harder than a probability does. The threshold itself, 0.001, is a business number: the average forfeit in redemption rate Halloway was willing to carry for a wrong call. A different business would write a different number, and the rule would still work the same way.

LiveKruschke's ROPE, the rule that can accept the null3 min▶

P(B > A) has a blind spot. Given enough data one arm is always ahead by a hair, so a probability of superiority can never say "these two are the same for any purpose that matters". Kruschke (2013), "Bayesian estimation supersedes the t test", Journal of Experimental Psychology: General, 142(2), 573-603, doi 10.1037/a0029146, supplies the missing rule. Decide, before the data, the band of differences too small to act on: the region of practical equivalence. Then compare the 89 percent equal-tailed interval for theta_B - theta_A to that band. Entirely outside the band: a difference worth acting on. Entirely inside: accept "no meaningful difference" as a positive finding. Straddling: not settled yet.

The second case is the one a t test cannot reach, and Kruschke's title is about exactly that. Estimation with a ROPE can conclude the null when the estimate is precise; a test can only fail to reject it. For Halloway the ROPE is a second rule beside expected loss, not a replacement. Expected loss says when acting is cheap enough. The ROPE says when the question is closed. The build-along runs the check with an example half-width of half a point, and the code prints its verdict rather than the page asserting one, because the width of the band is a business choice and not a course number.

Self-studyWhat the Monte Carlo is and is not3 min▶

Twenty thousand paired draws is an estimate of an integral, not the integral. P(B > A) to three decimals and expected loss to four will wobble in the last place between seeds. The canon values were computed once from fixed seeds and frozen, so your run should land on them to the precision printed and may differ by one in the last digit. What will not change is the verdict: 0.0029 is far enough above 0.001, and 0.0005 far enough below, that no seed flips the rule at either size.

The other thing the draws are not is a statement about the frequency of wrong decisions. They are a statement about one experiment's posterior. How often the rule, applied to many experiments, would stop on the wrong arm is a different quantity that needs many experiments, and Part 2 counts it.

Part 2 · covers the stopping rule across the whole experiment and its measured error rate

The ladder, and the price of the rule

5 min live

The experiment ran on to three thousand customers per arm, read at six points. The rule stopped at the second. Everything after n = 100 is what Halloway would have learned had it kept going: the observed gap narrowing toward the true 2.5 points, the probability climbing to 1.000, the expected loss of choosing B sitting at 0.0000 to four decimals. And then the replay: the same rule applied to two hundred fresh runs of the same experiment, to count how often it stops on the wrong arm.

One cumulative experiment, read at six points. The rule fired at the second and never changed its mind. n per arm 50 100 200 400 900 3000 A 0.100 · B 0.200 A 0.100 · B 0.200 A 0.115 · B 0.190 A 0.135 · B 0.205 A 0.131 · B 0.187 A 0.152 · B 0.186 P(B > A), 0.90 to 1.00 1.00 0.90 0.915 0.975 0.980 0.996 0.999 1.000 E[loss] if choose B, 0 to 0.003 threshold 0.001 0.0029 0.0005 0.0003 0.0000 0.0000 0.0000 choose A: 0.0984 choose A: 0.0989 choose A: 0.0743 choose A: 0.0695 choose A: 0.0557 choose A: 0.0339 keep going stop, choose B stop, choose B stop, choose B stop, choose B stop, choose B
🔍 Click to zoom - the probability track climbs slowly. The loss bars collapse. Only the second track has a line on it that anyone agreed to in advance.
LiveTwo in two hundred, and the sentence never to write3 min▶

Take the Westfield experiment, keep the true rates at 0.150 and 0.175, draw fresh customers from a fresh seed, and apply the same rule at n = 100. Do that two hundred times. In 23 of the 200 replays the rule would have stopped at n = 100. In 2 of the 200 it would have stopped on the wrong arm. That is a 1 percent wrong-stop rate at that sample size, and it is the number that belongs beside every stopped test: the rule is not free, it is cheap.

Which is why there is a claim you must not make. Never write that Bayesian A/B testing "has no peeking problem" or "lets you stop whenever you like". The expected-loss rule controls the cost of a wrong decision: each of those two wrong stops forfeited, on average, less than the threshold. It does not control the frequency of a wrong decision, and the 2 in 200 is the frequency. Whether reading the data as they arrive inflates that frequency, and by how much, is a real argument, and learn-experimentation-with-phoebe b6 owns it, along with Thompson sampling and bandits, the traffic-allocation cousins of tonight's rule that shift customers toward the winning arm while the test runs. Power and sample size are that course's b2, randomisation its b3. Tonight reports the measured rate and hands the argument over.

Real world

Halloway's board pack said "the new voucher wins, probability 0.975". The analyst added one line: "under this rule, replayed two hundred times, we would expect to ship the wrong voucher about twice, and the average forfeit each time would be under a tenth of a point." Nobody on the board asked what a peeking problem was. They asked whether two in two hundred was acceptable, which is the right question and theirs to answer.

Self-studyReading the rungs after the rule fired2 min▶

Halloway stopped at n = 100 and the ladder above is what it would have seen had it not. The observed gap shrank from ten points to 3.4 at n = 3000, closing on the true 2.5, while P(B > A) rose to 1.000 and the expected loss of choosing B stayed at 0.0000 to four decimals from n = 400 on. The decision was never in doubt after the second rung; what the later rungs bought was a better estimate of how much B wins by, not whether. The expected loss of choosing A tells the same story from the other side: 0.0984 at n = 50 down to 0.0339 at n = 3000, always the expensive option, falling as the posteriors narrowed onto a true difference smaller than the early data suggested.

A rule on expected loss stops when acting is cheap enough, and that is all it stops for. If the size of the lift matters for a budget, say so before the test and add a second condition, a ROPE or an interval width, so that the rule waits for the estimate as well as the decision.

Part 3 · build-along

The decision in numpy, then the replay

24 min build-along

Three blocks. The first reproduces the canon: 20,000 draws per arm from each Beta posterior at n = 50 and n = 100, P(B > A) and both expected losses, with the frozen values in the comments so you know when you have it. The second adds the ROPE check with an example half-width. The third is the replay loop, written as a sketch: it prints a count, and the count is yours, from your seeds, not the course's.

Turn observed rates into posteriors. Five of fifty and ten of fifty, then ten of a hundred and twenty of a hundred. With flat priors the posteriors are Beta(1 + y, 1 + n - y), the conjugate update from session 1. Nothing is fitted; it is arithmetic.

Draw 20,000 from each and pair them. One rvs call per arm with a seeded generator. The pairing is positional: draw i of A against draw i of B. Every decision number is a mean over those pairs.

Compute three means. np.mean(post_b > post_a) is P(B > A). np.mean(np.maximum(post_a - post_b, 0)) is the expected loss of choosing B, and the mirror is the expected loss of choosing A. Check the comments: 0.915, 0.0029, 0.0984 at n = 50.

Apply the rule. Choose the arm with the smaller expected loss; stop if that loss is below 0.001. Keep going at n = 50, stop and choose B at n = 100. If your verdict differs, the threshold or the counts are wrong, not the seed.

Add the ROPE. The 89 percent equal-tailed interval of the difference, quantiles 0.055 and 0.945, against a band of plus or minus half a point. Half a point is an example; the band is a business choice and the code labels it so. Read the verdict the code prints.

Replay the rule to price it. Two hundred fresh experiments from the true rates, stopped at n = 100 by the same rule. Count how many stop and how many stop on A, the arm that is truly worse. Your seeds are not the course's seeds; the canon count is 23 and 2, and yours is whatever prints.

★ Run it now (Python) - P(B > A) and expected loss at Westfield# Westfield, voucher B against voucher A. Flat Beta(1, 1) priors, 20,000 posterior draws per arm. import numpy as np from scipy.stats import beta rng = np.random.default_rng(7) DRAWS = 20_000 THRESHOLD = 0.001 # Halloway's rule, written down before the test def decide(n, redeemed_a, redeemed_b): post_a = beta(1 + redeemed_a, 1 + n - redeemed_a).rvs(DRAWS, random_state=rng) post_b = beta(1 + redeemed_b, 1 + n - redeemed_b).rvs(DRAWS, random_state=rng) p_b_beats_a = np.mean(post_b > post_a) # share of pairs with B ahead loss_choose_b = np.mean(np.maximum(post_a - post_b, 0.0)) # E[max(theta_A - theta_B, 0)] loss_choose_a = np.mean(np.maximum(post_b - post_a, 0.0)) # the mirror return p_b_beats_a, loss_choose_b, loss_choose_a, post_a, post_b for n, a, b in [(50, 5, 10), (100, 10, 20)]: # observed A 0.100 and B 0.200 at both sizes p, lb, la, _, _ = decide(n, a, b) chosen = "B" if lb < la else "A" verdict = f"stop, choose {chosen}" if min(lb, la) < THRESHOLD else "keep going" print(f"n={n:4d} P(B>A)={p:.3f} loss B={lb:.4f} loss A={la:.4f} {verdict}") # n= 50 P(B>A)=0.915 loss B=0.0029 loss A=0.0984 keep going # n= 100 P(B>A)=0.975 loss B=0.0005 loss A=0.0989 stop, choose B # Monte Carlo: the last printed digit may differ by one between seeds. The verdicts do not.
★ Then the second rule - Kruschke's ROPE on the n = 100 posteriors# Region of practical equivalence, Kruschke (2013). A band of differences too small to act on, # chosen BEFORE the data. The half-width below is an EXAMPLE, not a Halloway number. ROPE = 0.005 # half a point of redemption rate, either side _, _, _, post_a, post_b = decide(100, 10, 20) diff = post_b - post_a lo, hi = np.quantile(diff, [0.055, 0.945]) # 89 percent equal-tailed, the ArviZ 1.x default if hi < -ROPE or lo > ROPE: print("interval clear of the ROPE: a difference worth acting on") elif lo > -ROPE and hi < ROPE: print("interval inside the ROPE: accept no meaningful difference") # the case a t test cannot reach else: print("interval straddles the ROPE: not settled yet") # Read the printed verdict. The interval endpoints are yours, from your draws; they are not course numbers.
★ Then price the rule - a seeded replay sketch# The honest error rate. Fresh customers from the TRUE rates, A 0.150 and B 0.175, two hundred # times over, each stopped at n = 100 by the same expected-loss rule. Count stops, count wrong stops. TRUE_A, TRUE_B = 0.150, 0.175 # written into the generator; B is truly better stopped = wrong = 0 for seed in range(200): r = np.random.default_rng(seed) a = r.binomial(100, TRUE_A) # redemptions in the first 100 customers on A b = r.binomial(100, TRUE_B) _, lb, la, _, _ = decide(100, a, b) if min(lb, la) < THRESHOLD: stopped += 1 if la < lb: # chose A, and A is truly the worse arm wrong += 1 print(f"stopped at n=100: {stopped} of 200 stopped on the wrong arm: {wrong} of 200") # The course's own 200 seeded replays gave 23 stopped and 2 wrong. Your seeds are not the course's, # so read your two counts rather than expecting those. The shape of the result is the lesson: # the rule keeps each wrong stop cheap; it does not make wrong stops impossible.

If the loss and the probability disagree about when to stop, the loss is right. At n = 50 the probability looked ready and the loss did not, and the loss was carrying information the probability throws away: how far behind B is in the draws where it is behind. A rule on P(B > A) alone would have shipped at fifty customers on a gap four times the true lift.

Check yourself

Three questions

You have 20,000 paired posterior draws, post_a and post_b. Which line is the expected loss of choosing B?

Expected loss is E[max(theta_A - theta_B, 0)]: a probability times a stake, summed over the draws. Option A is a probability with no stake. Option C nets the wins against the losses and would be negative here, since B is usually ahead. At n = 50 the right line gives 0.0029; at n = 100, 0.0005.

After a long test the 89 percent interval for theta_B - theta_A sits entirely inside a pre-agreed ROPE of plus or minus half a point. What can you conclude that P(B > A) alone could not?

Kruschke (2013): when the posterior interval falls inside the region of practical equivalence, estimation can accept the null. P(B > A) can only ever say which arm is ahead by however little, so it can never close a question. The ROPE's width is a business choice made before the data, which is why the build-along labels its half-point as an example.

Across 200 seeded replays of the Westfield experiment, the rule stopped at n = 100 in 23 and on the wrong arm in 2. A colleague writes "Bayesian A/B testing has no peeking problem". What is wrong with the sentence?

The rule keeps each wrong stop cheap, below the threshold on average. It does not make them rare by construction, and no finite threshold drives the count to zero. Report the measured rate beside the estimate and hand the peeking argument to the course that owns it.

Homework

Before session 8

45 min

Take the replay sketch and make it a function of the threshold. Run it at 0.001, then at a threshold your own business would actually write down, and record two counts each time: how many of 200 stopped at n = 100, and how many stopped on the wrong arm. Then write the one sentence you would put in a board pack for each threshold, in the form "under this rule, replayed two hundred times, we would expect to ship the wrong arm about N times, forfeiting on average under X each time". Session 8 turns the fitted model back on the data it came from and asks whether it could have produced them, so arrive with the model from tonight still in a notebook.

Sources covered

What tonight maps to

✓ P(B > A) and expected loss as Monte Carlo over two posteriors E[max(theta_A - theta_B, 0)] for choosing B and its mirror, estimated over 20,000 paired draws. Westfield, n = 50: 0.915, 0.0029, 0.0984. n = 100: 0.975, 0.0005, 0.0989.
✓ The stopping rule on one cumulative experiment Stop when the expected loss of the chosen arm is below 0.001. Keep going at n = 50, stop and choose B at n = 100, unchanged at 200, 400, 900 and 3000.
✓ Kruschke (2013), the region of practical equivalence "Bayesian estimation supersedes the t test", Journal of Experimental Psychology: General, 142(2), 573-603, doi 10.1037/a0029146. The posterior interval against a ROPE; can accept the null when the estimate is precise. Run in the build-along with an example half-width.
✓ The honest error rate, as a count 200 seeded replays stopped at n = 100 by the same rule: 23 stopped, 2 of the 200 on the wrong arm. A 1 percent wrong-stop rate at that size. The rule controls cost, not frequency.
◐ Thompson sampling and bandits Named as the traffic-allocation cousin of the decision rule. learn-experimentation-with-phoebe b6 carries them; not taught here.
· The peeking argument, power, randomisation Not covered by design. learn-experimentation-with-phoebe b6, b2 and b3 own them; this session reports the measured 2 in 200 and hands the argument over.
· Bayes factors and marginal-likelihood comparison Not covered by design. Kruschke's rule is estimation with a ROPE, explicitly not a Bayes factor; session 8 names the distinction once.

Session 7 cheat sheet

Expected loss of choosing BE[max(theta_A - theta_B, 0)]. In numpy: np.mean(np.maximum(post_a - post_b, 0)). The mirror for A.
P(B > A) is not a decisionnp.mean(post_b > post_a). A probability with no stake. 0.915 at n = 50 and the rule said keep going.
The ruleStop when the expected loss of the chosen arm is below 0.001. Threshold written before the data, dated.
Why loss falls fasterIt weights the tail by distance as well as mass. 0.0029 to 0.0005 while the probability moved 0.915 to 0.975.
ROPE, Kruschke (2013)89 percent interval of the difference against a pre-agreed band. Inside: accept no meaningful difference. The case a t test cannot reach.
2 in 200Replayed and stopped at n = 100: 23 stopped, 2 on the wrong arm. Report it beside the estimate.
Never "no peeking problem"The rule controls cost, not frequency. learn-experimentation b6 owns the argument, Thompson sampling and bandits.
One experiment, six readsn = 100 contains n = 50. The ladder is one test read as it grew, and the rule fired at the second rung.