Index Ai Academy
Phase 2 · Math, when you need it · Probability & statistics

Bayesian rates: trusting small numbers

Priors as pseudo-counts, Beta posteriors, shrinkage for small samples, and which of two rates is really higher.

About 25 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

A new product page converted 3 of its first 4 visitors: 75%. A page that has run for a year converts 4%. Should the new page get the whole ad budget? Small samples swing wildly, and Bayes' rule gives a principled way to combine a little new evidence with what you already knew. This lesson turns that idea into a few lines of Python: a prior, some data, and an estimate that pulls wild small-sample rates toward sensible ones. It's how review sites, A/B testing tools and recommendation systems avoid being fooled by tiny numbers.

What you'll be able to do

  • Explain why rates from a handful of trials swing wildly
  • Combine a prior with new data by adding counts, and read the posterior estimate
  • Choose how strong a prior should be, and see it fade as data grows
  • Rank items fairly with a Bayesian average, and get an interval from the Beta distribution

What it covers

  1. Small samples swing
  2. Prior plus evidence
  3. Shrink three rates
  4. How strong should the prior be?
  5. The whole picture: a Beta distribution
  6. A credible interval from samples
  7. Where this sits in Bayes' rule
  8. Bayesian rates in AI and product work
Quick check
Before any explaining, a judgment call. Product A made 3 sales from 4 visitors. Product B made 300 sales from 5,000 visitors. Which rate would you trust more?
  1. A's 75%: it's the higher of the two rates
  2. B's 6%: it rests on far more visitors
  3. Both equally: a rate is a rate
  4. Neither: rates can't be compared at all
Show the answer

B's 6%: it rests on far more visitors

With 4 visitors, one more or one fewer sale moves the rate by 25 points. B's rate comes from 5,000 visitors, so it can't swing like that. This lesson shows how to use a small sample without either ignoring it or believing it completely.

Small samples swing

Imagine a page whose true conversion rate is 5%. Show it to 4 visitors, note the rate, and do that again and again. Most of the time nobody buys and the rate reads 0%; now and then one visitor buys and it reads 25%:

import random
random.seed(3)
for trial in range(8):
    sales = sum(1 for _ in range(4) if random.random() < 0.05)
    print(sales / 4, end="  ")
It runs right here in your browser, with nothing to install.

With 4 visitors, the only rates you can ever see are 0%, 25%, 50%, 75% and 100%. None of them is close to the true 5%. And each new visitor moves the rate a lot: going from 1 sale in 4 visits to 1 in 5 drops it from 25% to 20%, while a page with 5,000 visits barely notices one more. A rate's reliability depends on the number of trials behind it, which is the same lesson the samples lesson taught with confidence intervals. The question here is what to do with a small sample, other than wait.

Quick check
A page has had exactly 4 visitors. What does this print: every rate it could possibly show?
print([k / 4 for k in range(5)])
  1. [0, 1, 2, 3, 4]
  2. [0.0, 0.25, 0.5, 0.75, 1.0]
  3. [0.0, 0.05]
  4. [0.25, 0.5, 0.75, 1.0]
Show the answer

[0.0, 0.25, 0.5, 0.75, 1.0]

With 4 visitors, 0 to 4 of them can buy, giving rates of 0, 0.25, 0.5, 0.75 or 1.0. A true rate of 0.05 can't show up exactly; you only ever see a coarse, swinging approximation.

Prior plus evidence

Bayes' rule, in its friendliest form, says: start from what you already believe, then update it with the new data. For rates, both can be written as counts.

The prior is what you knew before. If pages like this one usually convert around 4%, you can write that as if you'd already seen 2 sales in 50 visits. The new data is what this page did: 3 sales in 4 visits. The updated belief, the posterior, just adds them:

prior_sales, prior_visits = 2, 50
sales, visits = 3, 4
raw = sales / visits
posterior = (prior_sales + sales) / (prior_visits + visits)
print(round(raw, 3), round(posterior, 3))

The raw rate says 75%; the posterior says about 9%. The page looks promising, and it gets credit for that, but 4 visitors can't outweigh everything you know about pages like it. As visits pile up, the data will dominate.

The posterior always lands between the prior's rate and the raw rate, closer to whichever has more visits behind it. That's the whole behavior in one sentence, and it's why this is called shrinkage: noisy estimates are shrunk toward a sensible center, more when they're noisier.

Try it now

The prior acts like visits you've already seen. A big prior needs lots of data to move; a small one moves quickly.

Drill

Shrink three rates

Pages like these usually convert about 4%, which you'll use as a prior of 2 sales in 50 visits. Set post_a, post_b and post_c: each page's posterior rate, rounded to 3 decimal places.

prior_sales, prior_visits = 2, 50

a_sales, a_visits = 3, 4        # a brand-new page
b_sales, b_visits = 0, 20       # a page with a slow start
c_sales, c_visits = 240, 5000   # a page that has run for months

post_a = 0
post_b = 0
post_c = 0

print(post_a, post_b, post_c)
In the lesson, your code is checked as you write it.

How strong should the prior be?

The prior's visits are its weight. A prior of 2 in 50 says "I'm about as sure as 50 visits would make me". Choose it from history: if past pages converted around 4%, and their rates varied about as much as 50 visits' worth of noise, then 2 in 50 is a fair summary.

Two failure modes are worth knowing. A prior that's too weak (say 0.04 sales in 1 visit) hardly shrinks anything, and tiny samples fool you again. A prior that's too strong (400 in 10,000) drowns real differences, and a genuinely great page takes forever to be believed. Whatever you pick, enough data always wins: with thousands of real visits, any sensible prior fades into the background.

Be careful where the prior comes from. Taking it from the very data you're judging counts that data twice. A fair prior comes from history, from similar items, or from a deliberate, stated assumption that others can question.

Quick check
A page has 500 sales from 10,000 visits. How much does a prior of 2 sales in 50 visits change its estimate?
  1. Not at all: priors are only used once
  2. Completely: the prior replaces the data
  3. Hardly at all: 502 / 10,050 is still about 5%
  4. A lot: it pulls the rate down to the prior's 4%
Show the answer

Hardly at all: 502 / 10,050 is still about 5%

502 / 10,050 ≈ 4.995%, against the raw 5%. Fifty imagined visits are nothing next to 10,000 real ones; the prior matters only while data is scarce.

The whole picture: a Beta distribution

The posterior is more than one number. It's a whole spread of plausible rates, a Beta distribution named by two counts: α = prior sales + sales and β = prior non-sales + non-sales. Its mean is α / (α + β), the estimate you've been computing, and its width says how sure you can be.

Python can draw samples from it, which gives an interval with no formulas at all:

import random
random.seed(4)
alpha = 2 + 3            # prior sales + sales
beta = (50 - 2) + (4 - 3)   # prior non-sales + non-sales
draws = sorted(random.betavariate(alpha, beta) for _ in range(4000))
print(round(draws[100], 3), round(draws[3899], 3))   # the middle 95%

Bayesians call this a credible interval: given the prior and the data, the true rate is probably in there. With 4 visitors it's wide; with 5,000 it would be narrow. That width is useful in its own right: two pages whose intervals barely overlap are probably different, while two whose intervals mostly overlap may not be.

Quick check
A Beta posterior has α = 5 and β = 49. What is its mean? What does this print?
alpha, beta = 5, 49
print(round(alpha / (alpha + beta), 3))
  1. 5.49
  2. 0.093
  3. 0.102
  4. 0.5
Show the answer

0.093

The mean is α / (α + β) = 5 / 54 ≈ 0.093: the same estimate as adding the counts, 5 sales in 54 visits.

Drill

A credible interval from samples

A page made 12 sales in 200 visits, and you'll use the prior of 2 in 50. Set alpha and beta for the posterior, draw 4,000 samples with random.betavariate(alpha, beta), sort them, and set low and high to the values at positions 100 and 3899.

import random
random.seed(5)
prior_sales, prior_visits = 2, 50
sales, visits = 12, 200

alpha = 0
beta = 0
low = 0
high = 0

print(alpha, beta, round(low, 3), round(high, 3))
In the lesson, your code is checked as you write it.

Where this sits in Bayes' rule

The conditional probability lesson wrote Bayes' rule as P(A given B) = P(B given A) × P(A) / P(B): an updated belief is the old belief, P(A), reweighted by how well it explains the evidence. Adding counts to a prior is that same rule, worked out once and for all for rates. Statisticians call a prior that keeps its shape after updating "conjugate"; the Beta prior for rates is the classic example, which is why the update is just addition.

Quick check
As in the conditional probability lesson: 1% of messages are spam, and a filter flags 90% of spam but also 5% of normal messages. A message is flagged. Roughly how likely is it to be spam?
  1. About 95%
  2. About 90%
  3. About 50%
  4. About 15%
Show the answer

About 15%

Out of 1,000 messages, 10 are spam and 9 of them get flagged; of the 990 normal ones, about 50 get flagged too. So 9 of about 59 flagged messages are spam: roughly 15%. The rare base rate dominates.

Bayesian rates in AI and product work

Rankings use a Bayesian average so a product with one perfect review doesn't top the list. A/B testing tools report the probability that B beats A from two Beta posteriors. Thompson sampling decides which variant to show next by drawing from each variant's posterior, so promising newcomers get tried without wasting traffic, and clear losers are quietly dropped as their posteriors settle low. And when you report an eval score from a handful of test cases, the same honesty applies: say how many cases there were, or shrink the score toward what similar systems usually get. Small numbers deserve modest claims, always, however exciting the first few results look.

Quick check
A review site ranks by a Bayesian average with a prior of 10 reviews at the site's mean of 4.0. Product X has one 5-star review. What is its Bayesian average? What does this print?
prior_n, prior_mean = 10, 4.0
n, total = 1, 5.0
print(round((prior_n * prior_mean + total) / (prior_n + n), 2))
  1. 4.5
  2. 4.0
  3. 5.0
  4. 4.09
Show the answer

4.09

(10 × 4.0 + 5) / (10 + 1) = 45 / 11 ≈ 4.09. One perfect review nudges it up a little, instead of putting it at the top of the list.

Practice what you just read

  • 3 code examples you can edit and run
  • a practice exercise that checks your code
  • a 6-question quiz
  • 5 review cards for spaced repetition

Create a free account

Try a quiz question

As a page collects more and more visits, what happens to the prior's influence?

  1. It fades: the data dominates
  2. It grows, since it's added every time
  3. It stays exactly the same
  4. It reverses, pulling the other way

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson