Art of DS

Appearance

Palette
Theme
Unlock $20 one time
Sign in, or create an account

Sign in to Art of DSCreate your Art of DS account

We email you a link and a six-digit code. Either one signs you in; there is no password.Enter your email. We send a link and a six-digit code to confirm it — no password to remember. The account is free.

  • You come back to this page, right where you were.
  • Already have an account? The same email simply signs you in.

By continuing you accept the Terms and the Privacy policy.

Your account

Free accountLifetime access

difficulty 2/5 Free

Bayes' theorem

Definition

A rule for turning the probability of the evidence given a hypothesis into the probability of the hypothesis given the evidence. The two are not the same, and the gap between them is the prior.

Formula
p(H∣D)=p(H) p(D∣H)p(D)p(H \mid D) = \frac{p(H)\,p(D \mid H)}{p(D)}

Intuition

A test that catches 99% of the people who have a disease comes back positive. Most people conclude they are probably sick. They are wrong, and not by a little: the answer here is 7.4%.

The mistake is reading a number about the test as a number about the patient. "Sadly, p(A|B) is not the same as p(B|A), but there is a way to get from one to the other" [1, §1.3], and that way is Bayes' theorem. What it adds to the test's accuracy is the thing the accuracy leaves out — how many people had the disease before anyone was tested.

The theorem is a piece of arithmetic nobody disputes. What makes it worth a page is that its answer contradicts a strong intuition, in situations that matter, and the contradiction is not a subtlety. It is a factor of more than thirteen.

Formal definition

The derivation is three lines. A conjunction can be written two ways — p(A and B) = p(A) p(B|A) for any A and B [1, §1.2], and the same with the letters swapped — so the two products are equal, and dividing through by p(B) gives the theorem [1, §1.4].

The version worth memorising is the one in the frontmatter above, with H for a hypothesis and D for data. This is the diachronic interpretation, so called because "something is happening over time; in this case the probability of the hypotheses changes, over time, as we see new data" [1, §1.5]. Each term has a name:

  • p(H) — the prior, the probability of the hypothesis before the data.
  • p(D|H) — the likelihood, the probability of the data if the hypothesis is true. "The likelihood is usually the easiest part to compute."
  • p(D) — the normalizing constant, "the probability of the data under any hypothesis at all", which "in the most general case ... is hard to nail down".
  • p(H|D) — the posterior, what you wanted.

All four are quoted from [1, §1.5].

The normalizing constant stops being hard the moment the hypotheses are mutually exclusive — "at most one hypothesis in the set can be true" — and collectively exhaustive — "there are no other possibilities" [1, §1.5]. Then the law of total probability gives p(D) as a sum over the hypotheses, and it is just the column total.

Often you can skip it entirely. Comparing hypotheses against the same data, "the denominator is the normalizing term p(x) which does not depend on the value of the label y. As a result, we only need to worry about comparing the numerator across different values of y" [2, §22.9.3]. Every classifier that scores prior × likelihood and takes the largest is doing exactly this.

How it works

1000 people, 4 of whom have the diseasethe test finds 99% of the sick and wrongly flags 5% of the healthy4 in 1000 have the disease54 of the 1000 test positive4 of those 54 have it — 7.4%
The same thousand people, three times over. A test with 99% sensitivity and 95% specificity sounds close to certain, but at a prevalence of 0.4% the fifty false positives it produces outnumber the four true ones twelve to one, and a positive result leaves the odds of being sick at 7.4%. The prior is not a detail the evidence overrides — it is a factor the evidence multiplies, and 0.004 is a very small factor.

The figure is the screening test drawn out. Four people in a thousand have the disease and the test finds all but a hundredth of them; fifty of the remaining 996 are flagged anyway, because 5% of a large number is larger than 99% of a small one. Fifty-four positives, four of them real.

Nothing about the test is defective. Sensitivity and specificity are properties of the instrument; the prior is a property of the population, and the posterior is the product. Change the population — test people with symptoms rather than everyone — and the same instrument gives a completely different answer [3, §2.6.5].

Where it's used

  • Diagnostics and screening, where the base rate is usually the dominant term and is usually the one omitted from the report.
  • Naive Bayes classifiers, which apply the theorem per class and then assume the features are conditionally independent given the label. Without some such assumption the chain rule leaves "roughly 2^d parameters" to estimate [2, §22.9.3] — and a model that needs "to see every single possible example in order to predict the corresponding label" is "not really learning a pattern but just memorizing the dataset" [2, §22.9.2].
  • Spam filtering, A/B tests, sensor fusion — anywhere evidence arrives in pieces and yesterday's posterior becomes today's prior.
  • Bayesian regression and hierarchical models, where the same update runs over a continuous parameter instead of a handful of hypotheses. See Bayesian linear regression.
  • Generative classifiers such as GDA, which model p(x|y) and invert it.

Why & when

Use it whenever you have the conditional the wrong way round. That is the whole trick: "if p(A|B) is hard to compute, or hard to measure experimentally, check whether it might be easier to compute the other terms in Bayes's theorem" [1, §1.8].

Do not fear the prior on the grounds that it is subjective. With enough data it stops mattering — "with enough data, people who start with different priors will tend to converge on the same posterior" [1, §4.3]. The worked example measures how quickly.

Do fear a prior of zero. "In a Bayesian update, we multiply each prior probability by a likelihood, so if p(H) is 0, p(H|D) is also 0, regardless of D" [1, §4.6]. This is Cromwell's rule — "avoid giving a prior probability of 0 to any hypothesis that is even remotely possible" [1, §4.6].

Reach for a conjugate prior when the update has to be cheap. For a binomial likelihood the beta distribution is conjugate, so "if the prior is a beta distribution with parameters alpha and beta, and we see data with h heads and t tails, the posterior is a beta distribution with parameters alpha+h and beta+t. In other words, we can do an update with two additions" [1, §4.5].

Worked example

Two questions, both answered by counting rather than by asserting. First: how much of a positive result is the test and how much is the population? Second: how long does a strongly held prior survive contact with data?

Python 57 lines Show the code Hide the code
import numpy as np

rng = np.random.default_rng(0)

# --- 1. a screening test, simulated one person at a time -------------------
PREV, SENS, SPEC = 0.004, 0.99, 0.95
N = 2_000_000

sick = rng.random(N) < PREV
pos1 = np.where(sick, rng.random(N) < SENS, rng.random(N) >= SPEC)

print(f"  {N:,} people, prevalence {PREV:.1%}, sensitivity {SENS:.0%}, specificity {SPEC:.0%}")
print(f"    tested positive          {pos1.sum():>8,}")
print(f"    of those, actually sick  {(pos1 & sick).sum():>8,}")
print(f"    P(sick | positive)         {(pos1 & sick).sum() / pos1.sum():>8.4f}   counted")
bayes = PREV * SENS / (PREV * SENS + (1 - PREV) * (1 - SPEC))
print(f"                               {bayes:>8.4f}   Bayes's theorem")

# --- 2. the same people take the test a second time ------------------------
pos2 = np.where(sick, rng.random(N) < SENS, rng.random(N) >= SPEC)
both = pos1 & pos2
print(f"\n    positive twice           {both.sum():>8,}")
print(f"    of those, actually sick  {(both & sick).sum():>8,}")
print(f"    P(sick | positive twice)   {(both & sick).sum() / both.sum():>8.4f}   counted")
b2 = bayes * SENS / (bayes * SENS + (1 - bayes) * (1 - SPEC))
print(f"                               {b2:>8.4f}   yesterday's posterior as today's prior")

# --- 3. three analysts who disagree about the coin -------------------------
TRUE_P = 0.35
priors = {
    "uniform      Beta(1, 1)": (1.0, 1.0),
    "sure it's ~0.9  B(30, 3)": (30.0, 3.0),
    "sure it's ~0.1  B(3, 30)": (3.0, 30.0),
}
flips = rng.random(10_000) < TRUE_P
print(f"\n  a coin with p = {TRUE_P}, three priors, posterior mean after n flips")
print("    " + "prior".ljust(26) + "".join(f"{n:>9}" for n in (0, 10, 100, 1_000, 10_000)))
for label, (a, b) in priors.items():
    row = []
    for n in (0, 10, 100, 1_000, 10_000):
        h = int(flips[:n].sum())
        row.append(f"{(a + h) / (a + b + n):>9.3f}")
    print("    " + label.ljust(26) + "".join(row))

# --- 4. an analyst who ruled the truth out in advance ----------------------
grid = np.linspace(0.0005, 0.9995, 1000)
flat = np.ones_like(grid)
closed = np.where(grid >= 0.5, 1.0, 0.0)  # "I am certain the coin favours heads"
print()
for prior, name in ((flat, "open mind    "), (closed, "p >= 0.5 only")):
    for n in (100, 10_000):
        h = int(flips[:n].sum())
        # in log space: theta**3500 underflows to zero in float64
        loglik = h * np.log(grid) + (n - h) * np.log(1 - grid)
        post = prior * np.exp(loglik - loglik.max())
        post = post / post.sum()
        print(f"    {name}  after {n:>6,} flips   posterior mean {(grid * post).sum():.3f}")

Output

  2,000,000 people, prevalence 0.4%, sensitivity 99%, specificity 95%
    tested positive           107,359
    of those, actually sick     7,940
    P(sick | positive)           0.0740   counted
                                 0.0737   Bayes's theorem

    positive twice             12,776
    of those, actually sick     7,851
    P(sick | positive twice)     0.6145   counted
                                 0.6116   yesterday's posterior as today's prior

  a coin with p = 0.35, three priors, posterior mean after n flips
    prior                             0       10      100     1000    10000
    uniform      Beta(1, 1)       0.500    0.417    0.314    0.352    0.351
    sure it's ~0.9  B(30, 3)      0.909    0.791    0.459    0.370    0.353
    sure it's ~0.1  B(3, 30)      0.091    0.163    0.256    0.344    0.351

    open mind      after    100 flips   posterior mean 0.314
    open mind      after 10,000 flips   posterior mean 0.351
    p >= 0.5 only  after    100 flips   posterior mean 0.512
    p >= 0.5 only  after 10,000 flips   posterior mean 0.501

The arithmetic and the counting agree, which is the point of running it. 0.0737 against 0.0740 over two million people: Bayes' theorem is not an approximation or a modelling choice here, it is what counting gives you.

A second positive changes everything. The posterior goes from 0.0740 to 0.6145, because the update ran with 0.0737 as the prior rather than 0.004. This is the diachronic reading made concrete [1, §1.5], and it is why repeat testing is the standard response to a rare-disease screen.

Note that the twice-positive figure is slightly above the chained arithmetic (0.6145 against 0.6116). The two tests were simulated as independent given the true status, which is the assumption the chaining makes; the gap is sampling noise over 12,776 people, not a flaw in either.

Two analysts who could not disagree more end up in the same place. One starts at 0.909, the other at 0.091 — a nine-to-one belief in opposite directions. After ten thousand flips they are at 0.353 and 0.351, against a truth of 0.35. Neither was argued out of their position; they were outvoted by data. That is swamping the priors [1, §4.3], measured.

The one prior that never recovers is the one that ruled the answer out. The analyst certain the coin favours heads is still at 0.501 after ten thousand flips of a coin that lands heads 35% of the time — pinned against the edge of what they were willing to consider, and no quantity of evidence moves them, because every hypothesis that could explain the data was multiplied by zero [1, §4.6].

Pitfalls

Confusing p(D|H) with p(H|D). The test is 99% sensitive and a positive result is 7.4% informative, at the same time, with no contradiction. This is the single most consequential arithmetic error in applied statistics.

Dropping the base rate. It is the term nobody measures and everybody assumes. If the prevalence is unknown, the posterior is unknown — the accuracy of the test cannot supply it.

Chaining updates that are not conditionally independent. The worked example runs two tests that are independent given the true status. Two runs of the same assay on the same badly drawn sample are not, and treating them as if they were inflates the posterior: run the same test twice and "no additional insight is gained" [3, §2.6.5].

A prior of zero, or a hypothesis set that is not exhaustive. Both are the same error: the truth is outside the suite, and "if p(H) is 0, p(H|D) is also 0, regardless of D" [1, §4.6]. So the posterior piles up on the least-wrong hypothesis it was allowed and reports high confidence — the worked example's analyst who allowed only p ≥ 0.5, pinned at 0.501.

Reading a posterior as if the model were certain. "All Bayesian analysis is based on modeling decisions. If you and I do not choose the same model, we might interpret data differently" [1, §4.6]. Priors get swamped; a wrong likelihood function does not.

Multiplying raw probabilities over a long dataset. The last block above needs its log-space rewrite: theta ** 3500 is zero in float64, and the posterior comes out as nan. See maximum likelihood.

Not read yetReach the end of the page and it counts as read.

Keep your progressWith lifetime access, every page you finish is marked and you pick up where you stopped, on all your devices.

Unlock

Check yourself

3 questions

Each answer is explained, with the section it tests and its source.

Sources

  1. Downey. Think Bayesbook · retrieved 2026-09-12https://greenteapress.com/thinkbayes/html/index.html
  2. Zhang, Lipton, Li, Smola. Dive into Deep Learningbook · retrieved 2026-09-12https://d2l.ai/chapter_appendix-mathematics-for-deep-learning/naive-bayes.html
  3. Zhang, Lipton, Li, Smola. Dive into Deep Learning — 2.6 Probability and Statisticsbookhttps://d2l.ai/chapter_preliminaries/probability.html

Have a promo code?

A code that takes 100% off unlocks everything at once. A smaller one takes its share off the price in the payment window: enter the same code there.

Write to support

A question, a problem with access, a refund? Write to us and we answer by email.

What is it about?

Your address is used only to answer you.

Message sent

Thank you. We will reply to the address you gave, by email.

Or write to us directly

support@artofds.com

Message sent Thank you. We will reply to the address you gave, by email.

Check the email address: we need it to reply.

That is our own address. Enter yours, so the reply reaches you.

This address cannot receive email. Check it for a typo.

Write a little more: at least 10 characters.

Too many messages in a short time. Try again in a few minutes.

The message could not be sent. Please write to us directly: support@artofds.com