Monocurl Essays
Series Download

P-values and hypothesis tests without the mysticism

A coin lands heads 60 times in 100. We compute the exact p-value, define the two kinds of error, and show with Bayes' theorem what a p-value is not.

22 min·High school and early college·

AssumesBinomial distribution: Pascal's triangle and a Galton boardBayes' rule: why a positive test is usually a false alarmWhat a 95% confidence interval does and does not say

A simulation of 400 experiments, each one 100 flips of a fair coin. Every dot is one experiment, placed at its number of heads. The red dots are the experiments that came out at least as lopsided as 60 heads to 40 tails, in either direction.
Monocurl source
let TRIALS = 400
let HEADS = map(range(0, TRIALS), |t| flips(t, 100, 3, 0.5))
let EXTREME = count(HEADS, |h| abs(h - 50) >= 10)
let X0 = -3.28
let DX = 0.16
let Y0 = -1.3
let R = 0.04
let xof = |k| X0 + (k - 30 + 0.5) * DX
let Pile = |k, hot| {
    let kk = to_int(floor(k + 0.0001))
    var h = map(range(0, 41), |i| 0)
    var parts = []
    for (i in range(0, kk)) {
        let b = HEADS[i] - 30
        if (b >= 0 and b < 41) {
            parts .= Pt([xof(HEADS[i]), Y0 + R + 0.03 + h[b] * 2 * R, 0], [AZURE, ROSE][hot > 0.5 and abs(HEADS[i] - 50) >= 10], R)
            h[b] = h[b] + 1
        }
    }
    parts .= center{[-2.6, 1.05, 0]} scale{1.35} color{INK} Number(kk, 0)
    return parts
}
mesh axis = [
    Seg([X0, Y0, 0], [X0 + 41 * DX, Y0, 0], SLATE, 2.5),
    map(range(30, 71, 5), |v| [
        Seg([xof(v), Y0 - 0.08, 0], [xof(v), Y0 + 0.08, 0], SLATE, 2),
        center{[xof(v), Y0 - 0.32, 0]} Math(to_string(v), 0.72, SLATE)
    ]),
    center{[0, Y0 - 0.7, 0]} Note("heads in 100 flips of a fair coin", 0.74),
    center{[-2.6, 1.55, 0]} Cap("experiments", 0.78, INK)
]
mesh pile = Pile(k: 0, hot: 0)
mesh verdict = []

slide "pile"
    play Wait(0.5)
    pile.k = TRIALS
    play Lerp(6.5, [&pile], linear)
    pile.hot = 1
    verdict = [
        Tip([xof(60), 0.35, 0], [xof(60), -0.72, 0], EMBER, 3, 0.16),
        center{[xof(60) + 0.3, 0.6, 0]} Cap("our coin: 60", 0.78, EMBER),
        center{[2.45, 1.75, 0]} Cap("40 or fewer, or 60 or more:", 0.72, ROSE),
        center{[2.45, 1.32, 0]} Math(str_join([to_string(EXTREME), "\text{ of }", to_string(TRIALS)]), 0.95, ROSE)
    ]
    play [Lerp(0.5, [&pile]), Fade(0.7, [&verdict])]
    play Wait(2.2)

Uses the shared definitions for this lesson's figures.

You flip a coin 100 times and it lands heads 60 times. Is the coin biased?

Most people say yes at once: a fair coin should give about 50, and 60 is a long way from 50. But a fair coin almost never gives exactly 50 either, so “not what a fair coin should do” cannot be the test. Some gap between the result and 50 is certain. The question that can be answered is how large a gap a fair coin produces by luck alone, and how often. That question has an exact numerical answer, called a p-value. This lesson computes it, and then spends as long on what the number does not mean, because that is where most of the damage is done.

What a fair coin does

The cleanest way to find out what luck alone produces is to take a coin known to be fair and repeat the whole experiment many times. The simulation above does that 400 times. The counts of heads range from 34 to 65. Results like 53 or 46 are ordinary, and 60 is out in the thin part of the pile, though it is not alone there: 20 of the 400 fair-coin experiments landed at least ten away from 50, at 60 or more or at 40 or fewer. That is 5%.

So here is the idea in one sentence. We cannot observe whether the coin is fair, but we can calculate what a fair coin would do, and then see whether our data look like that.

The smallest case, by hand

A simulation gives an estimate. For coins we can do better and compute the probability exactly, and a small case shows how. Suppose the experiment were 10 flips, with 8 heads.

The lesson on the binomial distribution showed that the number of heads $X$ in $n$ independent flips, each landing heads with probability $p$, satisfies

$$ P(X = k) = \binom{n}{k} p^k (1 - p)^{n-k}, $$

where $\binom{n}{k} = \frac{n!}{k!\,(n-k)!}$ counts the ways to choose which $k$ of the $n$ flips are the heads. For a fair coin every one of the $2^n$ sequences is equally likely, and the formula reduces to $\binom{n}{k}/2^n$.

Pause and try

For 10 flips of a fair coin, find the probability of 8 or more heads. You will need $\binom{10}{8} = 45$, $\binom{10}{9} = 10$ and $\binom{10}{10} = 1$.

Show answerHide answer

There are $2^{10} = 1024$ equally likely sequences. Of these, 45 have exactly 8 heads, 10 have exactly 9, and 1 has 10. So $P(X \ge 8) = \frac{45 + 10 + 1}{1024} = \frac{56}{1024} \approx 0.055$.

The distribution of the number of heads in 10 flips of a fair coin. The number over each bar counts the sequences, out of 1024, with that many heads. The red bars are the outcomes with 8 or more heads, and together they hold 56 of the 1024 sequences.
The distribution of the number of heads in 10 flips of a fair coin. The number over each bar counts the sequences, out of 1024, with that many heads. The red bars are the outcomes with 8 or more heads, and together they hold 56 of the 1024 sequences.
Monocurl source
let fb = Frame(-0.7, 10.7, 0, 0.29, [0.2, 0.05, 0], 6.4, 2.7)
let TAIL = upper(10, 8, 0.5)
mesh frame = CountAxis(fb, range(0, 11), [0.05, 0.1, 0.15, 0.2, 0.25], 2, "heads in 10 flips of a fair coin")
mesh bars = Bars(fb, 10, 0.5, 0, 10, |k| [AZURE, ROSE][k >= 8])
mesh counts = map(range(0, 11), |k| center{at(fb, k, pmf(10, k, 0.5)) + [0, 0.22, 0]} Math(to_string(choose(10, k)), 0.74, [AZURE, ROSE][k >= 8]))
mesh labels = [
    center{[fx(fb, -0.7) + 0.62, fy(fb, 0.29) + 0.3, 0]} Note("probability", 0.74),
    center{at(fb, 9.15, 0.262)} Math("P(X \ge 8)", 0.85, ROSE),
    center{at(fb, 9.15, 0.2)} Math(str_join(["= \frac{56}{1024} \approx ", to_string(TAIL, 3)]), 0.85, ROSE)
]

Uses the shared definitions for this lesson's figures.

Notice what we added up. We observed exactly 8 heads, and we computed the probability of 8 or more. The reason comes next.

Why “at least as extreme”

The natural first idea is to ask how likely our exact result is. For 100 flips,

$$ P(X = 60) = \binom{100}{60}\frac{1}{2^{100}} \approx 0.0108. $$

About 1%, which sounds damning. But try the same calculation for the most ordinary result imaginable: $P(X = 50) \approx 0.0796$. Even a perfect 50-50 split has less than an 8% chance. With 101 possible outcomes, every individual one is improbable, so the smallness of $P(X = 60)$ on its own tells us nothing. With a million flips, every individual count would have probability under 0.001.

What separates 60 from 50 is its position. A count of 50 is in the middle, with half of all outcomes on each side. A count of 60 is far out, with very little beyond it. So we measure how much probability lies at our result and everything further out: all the outcomes that would have surprised us at least as much.

  1. If the coin is fair, the number of heads in 100 flips follows this distribution. It is centered at 50, and almost all of it lies between 35 and 65.
  2. Our result is 60 heads. That exact count has probability 0.011, but even 50, the likeliest count of all, has only 0.080.
  3. Add the probabilities of 60, 61, and so on up to 100: every count at least as far above 50 as ours. The total is 0.028.
  4. A count of 40 or fewer is just as far from 50. Counting both directions, the p-value is 0.057.
The exact distribution of the number of heads in 100 flips of a fair coin, and the p-value for an observed 60 heads. The right tail from 60 upward holds probability 0.028. The left tail from 40 downward is equally far from 50 and holds the same. Together they give 0.057.
Monocurl source
let fb = Frame(29.4, 70.6, 0, 0.088, [0.2, 0.0, 0], 6.5, 2.6)
let HI = upper(100, 60, 0.5)
let LO = lower(100, 40, 0.5)
mesh frame = [
    CountAxis(fb, range(30, 71, 5), [0.02, 0.04, 0.06, 0.08], 2, "heads in 100 flips of a fair coin"),
    center{[fx(fb, 29.4) + 0.62, fy(fb, 0.088) + 0.28, 0]} Note("probability", 0.74)
]
mesh bars = Bars(fb, 100, 0.5, 30, 70, |k| AZURE)
mesh right = []
mesh left = []
mesh mark = []
mesh readout = []

slide "If the coin is fair, the number of heads in 100 flips follows this distribution. It is centered at 50, and almost all of it lies between 35 and 65."
    play Fade(0.6)

slide "Our result is 60 heads. That exact count has probability 0.011, but even 50, the likeliest count of all, has only 0.080."
    mark = [
        Tip([fx(fb, 60), fy(fb, 0.05), 0], [fx(fb, 60), fy(fb, 0.0135), 0], EMBER, 3, 0.16),
        center{[fx(fb, 60) + 0.25, fy(fb, 0.058), 0]} Cap("observed: 60", 0.78, EMBER)
    ]
    play Fade(0.6, [&mark])

slide "Add the probabilities of 60, 61, and so on up to 100: every count at least as far above 50 as ours. The total is 0.028."
    right = Bars(fb, 100, 0.5, 60, 70, |k| ROSE)
    readout = [
        center{[2.45, 1.92, 0]} Math("P(X \ge 60)", 0.85, ROSE),
        center{[2.45, 1.5, 0]} Math(str_join(["= ", to_string(HI, 3)]), 0.9, ROSE)
    ]
    play Fade(0.6, [&right, &readout])

slide "A count of 40 or fewer is just as far from 50. Counting both directions, the p-value is 0.057."
    left = Bars(fb, 100, 0.5, 30, 40, |k| ROSE)
    readout = [
        readout,
        center{[-0.9, 1.92, 0]} Math("P(X \le 40)", 0.85, ROSE),
        center{[-0.9, 1.5, 0]} Math(str_join(["= ", to_string(LO, 3)]), 0.9, ROSE),
        center{[0.8, 1.92, 0]} Cap("p-value", 0.8, INK),
        center{[0.8, 1.5, 0]} Math(to_string(HI + LO, 3), 1.0, INK)
    ]
    play Fade(0.6, [&left, &readout])

Uses the shared definitions for this lesson's figures.

The sum is a finite one, and a computer does it exactly:

$$ P(X \ge 60) = \sum_{k=60}^{100} \binom{100}{k}\frac{1}{2^{100}} \approx 0.0284. $$

We asked whether the coin is biased, without saying in which direction. A result of 40 heads would have been as striking as 60, so outcomes at least as far from 50 on the low side count as “at least as extreme” too. The distribution is symmetric, so they contribute the same amount:

$$ P(|X - 50| \ge 10) = 2 \times 0.0284 = 0.0569. $$

A fair coin produces a result at least as lopsided as ours about once in 18 tries. That is uncommon, and far from impossible. The simulation’s 20 in 400, or 0.05, was a rough estimate of this number. If you have seen this example quoted with a p-value of 0.028, that figure is the right tail alone, and it answers a narrower question that the next section takes up.

The definitions

Each piece of what we did has a standard name.

For the coin, $H_0$ says “each flip is heads with probability $\frac12$, independently”. It pins down the whole distribution in the figure. The test statistic is $T = |X - 50|$, the distance of the count from 50, and we observed $t = 10$. The bar in $P(\,\cdot \mid H_0)$ is read “given”, as in conditional probability: the whole calculation takes place inside a world where the coin is fair. This is the clause that people forget.

Had we suspected in advance that the coin favored heads specifically, the appropriate statistic would be $T = X$, with p-value $P(X \ge 60 \mid H_0) = 0.028$. This is a one-sided test; ours is two-sided. The choice has to be made before seeing the data. Choosing the side after noticing which way the data lean halves the p-value dishonestly.

Making a decision, and the two ways to be wrong

A p-value is a measure of surprise. Sometimes a decision is required: a casino must decide whether to pull a coin from play. The usual procedure fixes a threshold before the experiment.

With the common choice $\alpha = 0.05$, our coin’s $p = 0.057$ is not significant: by this convention, 60 heads in 100 is not enough to call the coin biased.

Pause and try

For 100 flips and $\alpha = 0.05$, which counts of heads lead to rejecting $H_0$? Use these two-sided p-values: 0.089 for 59 heads, 0.057 for 60, 0.035 for 61. Then find the probability that a fair coin gets rejected.

Show answerHide answer

The p-value falls as the count moves away from 50, and 61 is the first count with $p \le 0.05$. So the test rejects at 61 or more heads and, by symmetry, at 39 or fewer. A fair coin lands there with probability equal to the p-value of the boundary case, 0.035. It is below 0.05, not equal to it, because the count jumps in whole numbers.

That answer illustrates a general guarantee, which is the reason for the rule.

For the coin $t^* = 11$ and $p(t^*) = 0.035$. So $\alpha$ caps the rate of type I errors: among fair coins tested this way, at most 5% are wrongly flagged. The theorem says nothing about type II errors, and to compute those we must say how the coin is biased.

  1. A fair coin lands in the red tails, and is wrongly flagged, with probability 0.035. That is the type I error rate.
  2. Suppose the coin really lands heads 60%% of the time. Its counts follow the orange outline, and 54%% of them fall in the middle zone, where the coin goes unflagged: a type II error.
  3. A coin that lands heads 70%% of the time is much easier to catch: it is missed only 2%% of the time.
The decision rule for 100 flips: flag the coin at 39 or fewer heads or at 61 or more. The blue bars are the fair coin, whose red tails are type I errors. The orange outline is a biased coin, and the shaded part of it inside the middle zone is the probability of a type II error.
Monocurl source
let fb = Frame(27.4, 86.6, 0, 0.096, [0.2, -0.05, 0], 6.5, 2.5)
let Alt = |p| {
    let top = map(range(28, 87), |k| pmf(100, k, p))
    var outline = [at(fb, 27.5, 0)]
    for (i in range(0, 59)) {
        outline .= at(fb, 27.5 + i, top[i])
        outline .= at(fb, 28.5 + i, top[i])
    }
    outline .= at(fb, 86.5, 0)
    var inside = [at(fb, 39.5, 0)]
    for (k in range(40, 61)) {
        inside .= at(fb, k - 0.5, top[k - 28])
        inside .= at(fb, k + 0.5, top[k - 28])
    }
    inside .= at(fb, 60.5, 0)
    let miss = upper(100, 40, p) - upper(100, 61, p)
    return [
        fill{alpha{0.4} EMBER} stroke{alpha{0} EMBER, 0} Polygon(inside),
        Path(outline, EMBER, 3),
        center{[3.2, 1.95, 0]} scale{1.1} color{EMBER} Number(miss, 3)
    ]
}
mesh frame = [
    CountAxis(fb, range(30, 86, 5), [0.02, 0.04, 0.06, 0.08], 2, "heads in 100 flips"),
    center{[fx(fb, 27.4) + 0.62, fy(fb, 0.096) + 0.2, 0]} Note("probability", 0.72)
]
mesh bars = Bars(fb, 100, 0.5, 28, 86, |k| [AZURE, ROSE][flagged(k)])
mesh rule = [
    stroke{SLATE, 2.5} dashed{[0.12, 0.08]} Line(at(fb, 39.5, 0), at(fb, 39.5, 0.096)),
    stroke{SLATE, 2.5} dashed{[0.12, 0.08]} Line(at(fb, 60.5, 0), at(fb, 60.5, 0.096)),
    center{[fx(fb, 50), 1.42, 0]} Cap("do not flag", 0.74, SLATE),
    center{[fx(fb, 73.5), 1.42, 0]} Cap("flag as biased", 0.74, SLATE),
    center{[-2.25, 1.95, 0]} Cap("fair coin flagged:", 0.74, ROSE),
    center{[-0.82, 1.95, 0]} Math(to_string(ALPHA, 3), 0.85, ROSE)
]
mesh alt = []
mesh alt_label = []

slide "A fair coin lands in the red tails, and is wrongly flagged, with probability 0.035. That is the type I error rate."
    play Fade(0.6)

slide "Suppose the coin really lands heads 60%% of the time. Its counts follow the orange outline, and 54%% of them fall in the middle zone, where the coin goes unflagged: a type II error."
    alt = Alt(p: 0.6)
    alt_label = center{[1.72, 1.95, 0]} Cap("biased coin missed:", 0.74, EMBER)
    play Fade(0.7, [&alt, &alt_label])

slide "A coin that lands heads 70%% of the time is much easier to catch: it is missed only 2%% of the time."
    alt.p = 0.7
    play Lerp(2.2, [&alt])

Uses the shared definitions for this lesson's figures.

Against a coin that lands heads 60% of the time, the rule fails to raise a flag with probability 0.538. The probability of correctly rejecting a false null hypothesis is called the power of the test: here $1 - 0.538 = 0.462$. So if the coin in our opening question really is a 60% coin, a 100-flip experiment had less than an even chance of detecting it. A non-significant result from a test with low power is weak evidence that nothing is going on.

$H_0$ true$H_0$ false
test rejects $H_0$type I error (probability at most $\alpha$)correct (probability = power)
test does not rejectcorrecttype II error

What a p-value is not

The misreading that matters most reverses the conditional. Our p-value is $P(\text{data this extreme} \mid \text{coin fair}) = 0.057$. It is very tempting to turn this around and say that the probability the coin is fair, given the data, is 0.057, or that we are “94% sure the coin is biased”.

Those are different conditional probabilities, and Bayes’ theorem says how they are related. For a hypothesis $H$ and data $D$,

$$ P(H \mid D) = \frac{P(D \mid H)\,P(H)}{P(D \mid H)\,P(H) + P(D \mid \text{not } H)\,P(\text{not } H)}. $$

The right side contains two things the p-value never looked at: the prior probability $P(H)$ that the hypothesis was true before any data, and the probability of the data under the alternative.

Pause and try

Your coin was drawn at random from a jar of 100 coins. Ninety-nine are fair, and one is a trick coin that lands heads 60% of the time. It then showed exactly 60 heads in 100 flips. Given $P(X = 60 \mid \text{fair}) = 0.0108$ and $P(X = 60 \mid \text{trick}) = 0.0812$, find the probability that your coin is fair.

Show answerHide answer

By Bayes’ theorem,

$$ P(\text{fair} \mid 60 \text{ heads}) = \frac{0.0108 \times 0.99}{0.0108 \times 0.99 + 0.0812 \times 0.01} = \frac{0.01069}{0.01069 + 0.00081} \approx 0.93. $$

The data favor the trick coin by a factor of $0.0812/0.0108 \approx 7.5$, but fair coins were 99 times as common to begin with, and $99/7.5 \approx 13$ to 1 still favors fair.

The p-value is 0.057, and the probability that the null hypothesis is true is 0.93. If the jar had held equal numbers of fair and trick coins, the same data would give $P(\text{fair} \mid 60 \text{ heads}) = \frac{0.0108}{0.0108 + 0.0812} \approx 0.12$. The experiment and the p-value are the same for both jars, yet the probability that the coin is fair drops from 0.93 to 0.12, because that probability depends on the prior and the p-value does not.

A simulation of ten such jars: 990 fair coins and 10 trick coins (bottom right), each flipped 100 times and tested with the rule of the previous figure. Red squares are fair coins that were flagged. Most of the flagged coins are fair, even though each fair coin had only a 3.5% chance of being flagged.
A simulation of ten such jars: 990 fair coins and 10 trick coins (bottom right), each flipped 100 times and tested with the rule of the previous figure. Red squares are fair coins that were flagged. Most of the flagged coins are fair, even though each fair coin had only a 3.5% chance of being flagged.
Monocurl source
let COLS = 50
let GAP = 0.132
let FAIR = map(range(0, 990), |t| flips(t, 100, 47, 0.5))
let TRICK = map(range(0, 10), |t| flips(t, 100, 96, 0.6))
let FF = count(FAIR, |h| flagged(h))
let TF = count(TRICK, |h| flagged(h))
let cell = |i| [-3.234 + mod(i, COLS) * GAP, 2.02 - (i // COLS) * GAP, 0]
let Sq = |i, col, s| center{cell(i)} fill{col} stroke{col, 1} Rect([s, s])
# a legend entry: a swatch at pos with its label starting just to the right
let Key = |pos, inner, col, txt| {
    let swatch = center{pos} fill{inner} stroke{col, 1.5} Rect([0.16, 0.16])
    return [swatch, next_to{swatch, 1r, 0.16} Cap(txt, 0.74, col)]
}
mesh fair = map(range(0, 990), |i| Sq(i, [tint(AZURE, 0.35), ROSE][flagged(FAIR[i])], [0.085, 0.118][flagged(FAIR[i])]))
mesh trick = map(range(0, 10), |i| Sq(990 + i, [GOLD, LEAF][flagged(TRICK[i])], 0.118))
mesh legend = [
    Sq(0, tint(AZURE, 0.35), 0),
    Key([-2.6, -0.95, 0], tint(AZURE, 0.35), AZURE, "990 fair coins"),
    Key([-2.6, -1.35, 0], ROSE, ROSE, str_join(["fair but flagged: ", to_string(FF)])),
    Key([0.6, -0.95, 0], GOLD, GOLD, str_join(["trick coins missed: ", to_string(10 - TF)])),
    Key([0.6, -1.35, 0], LEAF, LEAF, str_join(["trick coins flagged: ", to_string(TF)])),
    center{[0, -1.92, 0]} Math(str_join(["\text{of the }", to_string(FF + TF), "\text{ flagged coins, }", to_string(FF), "\text{ are fair}"]), 0.92, INK)
]

Uses the shared definitions for this lesson's figures.

In the simulation 40 coins were flagged as biased, and 35 of them are fair. Someone who reads “significant at the 5% level” as “95% likely to be a real effect” would be wrong about 88% of the coins in that pile. The test did what the theorem promised: it flagged 35 of 990 fair coins, about 3.5%. The flagged pile is mostly fair coins anyway, because there were so many fair coins to draw false alarms from. This run is close to what the exact rates predict: a flagged coin is fair with probability $\frac{0.99 \times 0.0352}{0.99 \times 0.0352 + 0.01 \times 0.462} \approx 0.88$, where 0.462 is the power against the trick coin.

Many tests, some false alarms

The same arithmetic causes trouble when one researcher runs many tests. Each test of a true null hypothesis has a small chance of a false alarm, and small chances add up.

Pause and try

Twenty fair coins are each flipped 100 times and tested with our rule, which flags a fair coin with probability 0.0352. What is the probability that at least one of the twenty is flagged?

Show answerHide answer

The coins are independent, so the probability that none is flagged is $(1 - 0.0352)^{20} = 0.9648^{20} \approx 0.488$. The probability of at least one false alarm is $1 - 0.488 = 0.512$. It is more likely than not.

This calculation is usually quoted as $1 - 0.95^{20} \approx 0.64$. That version assumes each test has a false-alarm rate of exactly 0.05, which holds when the test statistic is continuous. Our rule can only cut between whole numbers of heads, so its rate is 0.0352, and 0.512 is the correct value for these coins.

A simulation of twenty fair coins, each flipped 100 times. Each tile shows one coin’s number of heads, with a dot marking it on a scale from 30 to 70 whose ticks are the flagging limits. In this run nineteen coins pass and one, with 61 heads, is flagged as biased, though it is as fair as the rest. About half of such runs flag no coin at all, as the next figure shows.
Monocurl source
let BATCH = map(range(0, 20), |c| flips(c, 100, 27, 0.5))
let pos = |i| [-2.64 + mod(i, 5) * 1.32, 1.02 - (i // 5) * 0.88, 0]
let Tile = |i| {
    let h = BATCH[i]
    let col = [AZURE, ROSE][flagged(h)]
    let c = pos(i)
    let gx = |v| c[0] + (v - 50) * 0.026
    return [
        center{c} fill{tint(col, [0.1, 0.3][flagged(h)])} stroke{col, [2, 3.5][flagged(h)]} Rect([1.18, 0.76]),
        center{[c[0], c[1] + 0.12, 0]} Cap(to_string(h), 0.9, col),
        Seg([gx(30), c[1] - 0.22, 0], [gx(70), c[1] - 0.22, 0], SLATE, 1.5),
        Seg([gx(39.5), c[1] - 0.29, 0], [gx(39.5), c[1] - 0.15, 0], SLATE, 1.5),
        Seg([gx(60.5), c[1] - 0.29, 0], [gx(60.5), c[1] - 0.15, 0], SLATE, 1.5),
        Pt([gx(h), c[1] - 0.22, 0], col, 0.055)
    ]
}
let Tested = |k| {
    let kk = to_int(floor(k + 0.0001))
    return [
        map(range(0, kk), |i| Tile(i)),
        center{[-1.45, 1.92, 0]} scale{1.15} color{INK} Number(kk, 0),
        center{[2.2, 1.92, 0]} scale{1.15} color{ROSE} Number(count(take(BATCH, kk), |h| flagged(h)), 0)
    ]
}
mesh labels = [
    center{[-2.55, 1.92, 0]} Cap("coins tested:", 0.78, INK),
    center{[1.0, 1.92, 0]} Cap("flagged as biased:", 0.78, ROSE)
]
mesh tested = Tested(k: 0)

slide "twenty"
    play Wait(0.5)
    tested.k = 20
    play Lerp(7, [&tested], linear)
    play Wait(2.2)

Uses the shared definitions for this lesson's figures.

A researcher who tests twenty things that do nothing, and reports the one that reached significance, has found nothing. This is the multiple comparisons problem. It arises whenever many hypotheses are tested and the winners are reported without the losers: twenty foods checked against one disease, one drug checked against twenty outcomes, or one data set sliced twenty ways.

The twenty-coin experiment repeated 1000 times, always with fair coins. The bars show how many of the 1000 batches produced 0, 1, 2, 3 or 4 false alarms, and the rings show the exact binomial probabilities. About half of the batches flag at least one fair coin.
The twenty-coin experiment repeated 1000 times, always with fair coins. The bars show how many of the 1000 batches produced 0, 1, 2, 3 or 4 false alarms, and the rings show the exact binomial probabilities. About half of the batches flag at least one fair coin.
Monocurl source
let NB = 1000
let FALSE = map(range(0, NB), |b| count(range(0, 20), |c| flagged(flips(b * 20 + c, 100, 27, 0.5))))
let FREQ = map(range(0, 5), |k| count(FALSE, |f| f == k) / NB)
let SOME = count(FALSE, |f| f > 0)
let fm = Frame(-0.6, 4.6, 0, 0.56, [-0.1, 0.0, 0], 6.0, 2.6)
mesh frame = [
    GridLines(fm, [], [0.1, 0.2, 0.3, 0.4, 0.5]),
    Seg(at(fm, -0.6, 0), at(fm, 4.6, 0), SLATE, 2.5),
    Seg(at(fm, -0.6, 0), at(fm, -0.6, 0.56), SLATE, 2.5),
    map(range(0, 5), |k| center{[fx(fm, k), fy(fm, 0) - 0.32, 0]} Math(to_string(k), 0.76, SLATE)),
    map([10, 20, 30, 40, 50], |v| center{[fx(fm, -0.6) - 0.45, fy(fm, v / 100), 0]} Math(str_join([to_string(v), "\%%"]), 0.72, SLATE)),
    center{[fx(fm, 2), fy(fm, 0) - 0.7, 0]} Note("fair coins flagged in a batch of 20", 0.74),
    center{[fx(fm, -0.6) + 0.95, fy(fm, 0.56) + 0.3, 0]} Note("share of batches", 0.74)
]
mesh bars = map(range(0, 5), |k| Box(at(fm, k, max(FREQ[k], 0.002) / 2), [0.72, max(FREQ[k], 0.002) * 2.6 / 0.56], [AZURE, ROSE][k > 0], 0.5, 2))
mesh exact = map(range(0, 5), |k| Ring(at(fm, k, pmf(20, k, ALPHA)), INK, 0.085, 3))
mesh counts = map(range(0, 5), |k| center{at(fm, k, max(FREQ[k], pmf(20, k, ALPHA))) + [0, 0.3, 0]} Math(to_string(to_int(round(FREQ[k] * NB))), 0.8, [AZURE, ROSE][k > 0]))
mesh labels = [
    center{at(fm, 3.2, 0.45)} Cap("at least one false alarm:", 0.76, ROSE),
    center{at(fm, 3.2, 0.35)} Math(str_join([to_string(SOME), "\text{ of }", to_string(NB), "\text{ batches}"]), 0.9, ROSE),
    center{at(fm, 3.2, 0.25)} Math(str_join(["\text{exact probability: }", to_string(1 - (1 - ALPHA) ^ 20, 3)]), 0.8, INK)
]

Uses the shared definitions for this lesson's figures.

The simplest defense is the Bonferroni correction: when running $m$ tests, require $p \le \alpha/m$ for each. Then the chance of any false alarm at all is at most $m \times \alpha/m = \alpha$, because the probability that at least one of several events happens is never more than the sum of their probabilities. One of the problems below works through this.

Worked examples

Where the reasoning stops

A p-value above $\alpha$ does not show that $H_0$ is true. A coin with a 60% chance of heads, flipped 10 times, gives 6 heads as its single most likely result, and the two-sided p-value of 6 heads in 10 is $P(|X - 5| \ge 1) = 1 - \frac{252}{1024} \approx 0.75$. That is nowhere near significant, and the coin is biased. The experiment was too small to tell.

The calculation also inherits every assumption in $H_0$. Our null hypothesis said the flips were independent with probability $\frac12$ each. If the flipper’s technique makes consecutive flips tend to agree, a perfectly balanced coin produces lopsided counts more often than the binomial distribution allows, and a small p-value would be evidence against “independent and fair” as a package, not against the coin’s balance.

Stepping back

The logic of a hypothesis test is an argument by near-contradiction. Assume the dull explanation, work out what it predicts, and see whether the data fall in a region the dull explanation almost never reaches. The hard part was choosing what to compute: the probability of the exact outcome is useless, and the tail of outcomes at least as extreme is what carries the information.

Everything delicate about p-values follows from the direction of the conditional. We compute the probability of data given a hypothesis, and what we want is the probability of the hypothesis given data. The first can be calculated from the null hypothesis alone, which is why it is so widely used. The second needs to know how plausible the hypothesis was beforehand. So far every test and interval in this series has concerned one variable at a time, and the next lesson turns to the relationship between two.

Check yourself

A drug trial reports that patients on the drug recovered faster than patients on a placebo, with $p = 0.02$. What does this mean?

Problems

The first three compute p-values. The next four are about errors, priors and many tests. The last is a challenge.

Problem 1

A coin is flipped 10 times and lands heads 9 times. Find the one-sided and the two-sided p-value for the null hypothesis that the coin is fair.

Show solutionHide solution

There are 1024 equally likely sequences. One-sided: $P(X \ge 9) = \frac{\binom{10}{9} + \binom{10}{10}}{1024} = \frac{11}{1024} \approx 0.0107$. Two-sided: outcomes at least as far from 5 as 9 are $X \ge 9$ and $X \le 1$, and by symmetry $P(|X - 5| \ge 4) = \frac{22}{1024} \approx 0.0215$.

Problem 2

A fair-coin test uses 12 flips and rejects $H_0$ when the number of heads is 0, 1, 2, 10, 11 or 12. Find its type I error rate.

Show solutionHide solution

Under $H_0$ each of the $2^{12} = 4096$ sequences is equally likely. The numbers of sequences with 0, 1 and 2 heads are $\binom{12}{0} = 1$, $\binom{12}{1} = 12$ and $\binom{12}{2} = 66$, in total 79, and by symmetry there are 79 with 10, 11 or 12 heads. The type I error rate is $\frac{158}{4096} \approx 0.0386$.

Problem 3

A coin is flipped 400 times and lands heads 230 times. Use the normal approximation with the continuity correction to find the two-sided p-value. (A standard normal variable exceeds 2.95 in absolute value with probability 0.0032.)

Show solutionHide solution

Under $H_0$ the count has mean 200 and standard deviation $\sqrt{400 \times 0.25} = 10$. The tail “230 or more” begins at 229.5, so $z = (229.5 - 200)/10 = 2.95$. The two-sided p-value is about 0.0032, strong evidence against a fair coin. Note that 230 of 400 is 57.5% heads, a smaller departure from one half than 60 of 100. It is more significant because there are four times as many flips, which halves the standard error of the proportion.

Problem 4

A test of fairness uses 10 flips and rejects when the number of heads is 0, 1, 9 or 10. (a) Find its type I error rate. (b) If the coin actually lands heads 70% of the time, find the power and the type II error rate. Use $0.7^{10} \approx 0.0282$ and $0.7^9 \approx 0.0404$.

Show solutionHide solution

(a) $\frac{1 + 10 + 10 + 1}{1024} = \frac{22}{1024} \approx 0.0215$. (b) With $p = 0.7$: $P(X = 10) = 0.7^{10} \approx 0.0282$ and $P(X = 9) = 10 \times 0.7^9 \times 0.3 \approx 0.1211$. At the other end, $P(X = 0) = 0.3^{10}$ and $P(X = 1) = 10 \times 0.7 \times 0.3^9$ together are about 0.0001. The power is about $0.0282 + 0.1211 + 0.0001 = 0.149$, and the type II error rate is about 0.851. Ten flips will miss a 70% coin about 85% of the time.

Problem 5

A laboratory screens many candidate drugs. Suppose 10% of the candidates really work. Each is tested at $\alpha = 0.05$ with power 0.8. Of the candidates that reach significance, what fraction do not work?

Show solutionHide solution

Take 1000 candidates. 100 work, and $0.8 \times 100 = 80$ of them reach significance. 900 do not work, and $0.05 \times 900 = 45$ of them reach significance anyway. So 125 results are significant, of which 45 are false: $\frac{45}{125} = 0.36$. In Bayes’ form, $P(H_0 \mid \text{significant}) = \frac{0.05 \times 0.9}{0.05 \times 0.9 + 0.8 \times 0.1} = 0.36$. More than a third of the “discoveries” are false, although each test had only a 5% type I error rate.

Problem 6

A study tests 14 independent null hypotheses, all of them true, each at level 0.05. (a) Find the probability of at least one significant result. (b) Let $A_1, \dots, A_m$ be events. Explain why $P(\text{at least one } A_i \text{ occurs}) \le P(A_1) + \dots + P(A_m)$, and deduce that testing each of $m$ true null hypotheses at level $\alpha/m$ keeps the probability of any false alarm at most $\alpha$. (c) Compute that probability exactly for $m = 14$ and $\alpha = 0.05$.

Show solutionHide solution

(a) $1 - 0.95^{14} \approx 1 - 0.488 = 0.512$. (b) The probability of “at least one” is the total probability of the outcomes that lie in one or more of the $A_i$. The sum $P(A_1) + \dots + P(A_m)$ counts every such outcome at least once, and counts outcomes lying in several events more than once, so it is at least as large. With $A_i$ the event that test $i$ gives a false alarm, each $P(A_i) \le \alpha/m$ by the lesson’s theorem, so the probability of any false alarm is at most $m \times \alpha/m = \alpha$. This argument does not need independence. (c) Each test uses level $0.05/14 \approx 0.00357$, and for independent tests $1 - (1 - 0.00357)^{14} \approx 0.0489$, just under 0.05.

Problem 7

Let $\bar x$ be the mean of a sample from a normal population with known standard error $\operatorname{SE}$. To test $H_0\colon \mu = \mu_0$, use $T = |\bar X - \mu_0|/\operatorname{SE}$, and reject at level 0.05 when $T \ge 1.96$. Show that the test rejects $H_0$ exactly when $\mu_0$ lies outside the 95% confidence interval $\bar x \pm 1.96\operatorname{SE}$. What does this say about the values inside a confidence interval?

Show solutionHide solution

Under $H_0$, $(\bar X - \mu_0)/\operatorname{SE}$ is standard normal, so $P(T \ge 1.96 \mid H_0) = 0.05$, and the p-value is at most 0.05 exactly when the observed $T$ is at least 1.96. Now $T \ge 1.96$ means $|\bar x - \mu_0| \ge 1.96\operatorname{SE}$, which says $\mu_0$ is at least $1.96\operatorname{SE}$ away from $\bar x$: that is, $\mu_0 \le \bar x - 1.96\operatorname{SE}$ or $\mu_0 \ge \bar x + 1.96\operatorname{SE}$. So the test rejects exactly when $\mu_0$ is outside the interval (or on its boundary). The 95% confidence interval is therefore the set of all values $\mu_0$ that a two-sided test at level 0.05 would not reject: the values of the mean with which the data are compatible.

Problem 8

(Peeking.) A researcher tests whether a coin favors heads. She flips 5 times and declares success if all 5 are heads. If not, she flips 5 more and declares success if the 10 flips contain at least 9 heads. (a) Find the type I error rate of each of the two tests taken alone. (b) Find the probability that a fair coin leads her to declare success. (c) What goes wrong if she reports only the test that succeeded?

Show solutionHide solution

(a) All 5 heads has probability $\frac{1}{32} = \frac{32}{1024} \approx 0.031$. At least 9 heads in 10 has probability $\frac{11}{1024} \approx 0.011$.

(b) She succeeds at the first look with probability $\frac{32}{1024}$. She succeeds at the second look only if the first five were not all heads and the ten flips contain at least 9 heads. Ten heads would require the first five to be all heads, so the total must be exactly 9, with the single tail among the first five flips. There are 5 such sequences out of 1024. The two ways of succeeding cannot both happen, so the probability is $\frac{32 + 5}{1024} = \frac{37}{1024} \approx 0.036$.

(c) The procedure as a whole has type I error rate 0.036, larger than that of either test alone. If she succeeds at the second look and reports “9 heads in 10, $p = 0.011$”, the reported p-value understates how often a fair coin would have given her a success somewhere along the way. Looking at the data repeatedly and stopping when it looks good is another form of multiple comparisons. An honest p-value must be computed for the whole procedure that was followed.

Shared definitions used by this lesson's figures
# house kit: prepended to every essay figure after the std imports

# palette. AZURE and EMBER are the two actors, LEAF marks an answer, ROSE a problem
let PAPER = [1, 1, 1, 1]
let INK = [0.08, 0.09, 0.11, 1]
let SLATE = [0.40, 0.44, 0.50, 1]
let MIST = [0.85, 0.86, 0.88, 1]
let AZURE = [0.13, 0.42, 0.68, 1]
let EMBER = [0.89, 0.42, 0.12, 1]
let LEAF = [0.13, 0.55, 0.36, 1]
let ROSE = [0.78, 0.22, 0.24, 1]
let PLUM = [0.47, 0.30, 0.66, 1]
let GOLD = [0.85, 0.62, 0.08, 1]

background = PAPER

# an opaque pastel of col; use for fills so overlaps do not darken
let tint = |col, amount = 0.18| [
    1 - (1 - col[0]) * amount,
    1 - (1 - col[1]) * amount,
    1 - (1 - col[2]) * amount,
    1
]

# text: Cap for labels, Note for quiet labels, Math for typeset maths
let Cap = |txt, size = 0.75, col = INK| color{col} Text(txt, size, "Charter")
let Note = |txt, size = 0.65| color{SLATE} Text(txt, size, "Charter")
let Math = |tex, size = 0.9, col = INK| color{col} Tex(tex, size)

# a paper rectangle to sit behind text that crosses lines
let Plate = |pos, size| center{pos} fill{PAPER} stroke{alpha{0} PAPER, 0} Rect(size)

# shapes with fill and stroke both set, so no stray black appears
let Pt = |pos, col = INK, r = 0.075| center{pos} fill{col} stroke{col, 1} Circle(r, 48)
let Ring = |pos, col = INK, r = 0.1, w = 3| center{pos} fill{PAPER} stroke{col, w} Circle(r, 48)
let Seg = |a, b, col = INK, w = 3| stroke{col, w} Line(a, b)
let Path = |pts, col = INK, w = 3| stroke{col, w} Polyline(pts)
let Region = |pts, col| fill{col} stroke{alpha{0} col, 0} Polygon(pts)
let Outline = |pts, col = INK, w = 3| fill{alpha{0} PAPER} stroke{col, w} Polygon(pts)
let Shape = |pts, col, amount = 0.18, w = 3| fill{tint(col, amount)} stroke{col, w} Polygon(pts)
let Disc = |pos, r, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Circle(r, 96)
let Box = |pos, size, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Rect(size)
# a line from a to b ending in a small solid arrowhead
let Tip = |a, b, col = INK, w = 3, head = 0.17| {
    let d = normalize([b[0] - a[0], b[1] - a[1], 0])
    let n = [-d[1], d[0], 0]
    let base = [b[0] - d[0] * head, b[1] - d[1] * head, 0]
    return [
        stroke{col, w} Line(a, base),
        fill{col} stroke{col, 1} Polygon([
            b,
            [base[0] + n[0] * head * 0.42, base[1] + n[1] * head * 0.42, 0],
            [base[0] - n[0] * head * 0.42, base[1] - n[1] * head * 0.42, 0]
        ])
    ]
}

# plots. a frame is [x0, x1, y0, y1, centre, width, height]: a data window and the scene box it fills
let Frame = |x0, x1, y0, y1, c = [0, 0, 0], w = 6.4, h = 3.6| [x0, x1, y0, y1, c, w, h]
let fx = |fr, x| fr[4][0] + (x - (fr[0] + fr[1]) / 2) * fr[5] / (fr[1] - fr[0])
let fy = |fr, y| fr[4][1] + (y - (fr[2] + fr[3]) / 2) * fr[6] / (fr[3] - fr[2])
let at = |fr, x, y| [fx(fr, x), fy(fr, y), 0]

# axes through the data origin (or the nearest edge of the window), with optional names
let Axes = |fr, x_name = "", y_name = "", col = SLATE| {
    let ox = clamp(fr[0], 0, fr[1])
    let oy = clamp(fr[2], 0, fr[3])
    var parts = [
        Tip(at(fr, fr[0], oy), [fx(fr, fr[1]) + 0.25, fy(fr, oy), 0], col, 2.5, 0.15),
        Tip(at(fr, ox, fr[2]), [fx(fr, ox), fy(fr, fr[3]) + 0.25, 0], col, 2.5, 0.15)
    ]
    if (x_name != "") {
        parts .= center{[fx(fr, fr[1]) + 0.5, fy(fr, oy), 0]} Math(x_name, 0.8, col)
    }
    if (y_name != "") {
        parts .= center{[fx(fr, ox), fy(fr, fr[3]) + 0.5, 0]} Math(y_name, 0.8, col)
    }
    return parts
}

# tick marks with numbers at the listed data values
let XTicks = |fr, xs, col = SLATE, places = 0| block {
    let oy = fy(fr, clamp(fr[2], 0, fr[3]))
    for (x in xs) {
        . Seg([fx(fr, x), oy - 0.07, 0], [fx(fr, x), oy + 0.07, 0], col, 2)
        . center{[fx(fr, x), oy - 0.33, 0]} Math(to_string(x, places), 0.72, col)
    }
}
let YTicks = |fr, ys, col = SLATE, places = 0| block {
    let ox = fx(fr, clamp(fr[0], 0, fr[1]))
    for (y in ys) {
        . Seg([ox - 0.07, fy(fr, y), 0], [ox + 0.07, fy(fr, y), 0], col, 2)
        . center{[ox - 0.36, fy(fr, y), 0]} Math(to_string(y, places), 0.72, col)
    }
}

# faint grid lines at the listed data values
let GridLines = |fr, xs, ys, col = MIST| block {
    for (x in xs) {
        . Seg(at(fr, x, fr[2]), at(fr, x, fr[3]), col, 1.5)
    }
    for (y in ys) {
        . Seg(at(fr, fr[0], y), at(fr, fr[1], y), col, 1.5)
    }
}

# the graph of f over [a, b], drawn in the frame. values are clamped to the window
let Graph = |fr, f, a, b, col = AZURE, w = 4, n = 160|
    stroke{col, w} Polyline(map(sample(a, b, n), |x| at(fr, x, clamp(fr[2], f(x), fr[3]))))

# the region between the graph of f and the x axis over [a, b]
let AreaUnder = |fr, f, a, b, col = AZURE, amount = 0.2, n = 80| {
    var pts = [at(fr, a, 0)]
    for (x in sample(a, b, n)) {
        pts .= at(fr, x, clamp(fr[2], f(x), fr[3]))
    }
    pts .= at(fr, b, 0)
    return fill{tint(col, amount)} stroke{alpha{0} col, 0} Polygon(pts)
}

# a uniform value in [0, 1) for each (i, salt). std's seeded_random is a single sine hash, and
# coin counts built from it come out overdispersed (variance 26 where 25 is exact), which
# inflates the simulated false-alarm rate; a second hash keyed on the first removes that
let frac = |x| mod(mod(x, 1) + 1, 1)
let rnd = |i, salt| frac(sin(i * 39.3468 + salt * 11.135 + frac(sin(i * 12.9898 + salt * 78.233) * 43758.5453) * 6.2831853) * 24634.6345)
# number of heads in experiment t: n flips of a coin that lands heads with chance p
let flips = |t, n, salt, p| {
    var c = 0
    for (j in range(0, n)) {
        if (rnd(t * n + j + 1, salt) < p) {
            c = c + 1
        }
    }
    return c
}
# chance of exactly k heads in n flips when each is heads with chance p
let pmf = |n, k, p| {
    var v = 1.0
    for (i in range(1, k + 1)) {
        v = v * (n - k + i) / i * p
    }
    for (i in range(0, n - k)) {
        v = v * (1 - p)
    }
    return v
}
let upper = |n, k, p| sum(map(range(k, n + 1), |j| pmf(n, j, p)))
let lower = |n, k, p| sum(map(range(0, k + 1), |j| pmf(n, j, p)))
# the decision rule used throughout: flag a coin at 39 or fewer heads, or 61 or more
let flagged = |h| abs(h - 50) >= 11
let ALPHA = 2 * upper(100, 61, 0.5)
# one bar per count from lo to hi, heights from the binomial(n, p) distribution
let Bars = |fr, n, p, lo, hi, col_of| block {
    for (k in range(lo, hi + 1)) {
        let h = pmf(n, k, p)
        let hs = h * fr[6] / (fr[3] - fr[2])
        if (hs > 0.004) {
            . Box(at(fr, k, h / 2), [fr[5] / (fr[1] - fr[0]) * 0.86, hs], col_of(k), 0.55, 1.5)
        }
    }
}
let CountAxis = |fr, xs, ys, places, name| [
    GridLines(fr, [], ys),
    Seg(at(fr, fr[0], 0), at(fr, fr[1], 0), SLATE, 2.5),
    Seg(at(fr, fr[0], 0), at(fr, fr[0], fr[3]), SLATE, 2.5),
    map(xs, |v| [
        Seg([fx(fr, v), fy(fr, 0) - 0.07, 0], [fx(fr, v), fy(fr, 0) + 0.07, 0], SLATE, 2),
        center{[fx(fr, v), fy(fr, 0) - 0.32, 0]} Math(to_string(v), 0.72, SLATE)
    ]),
    map(ys, |v| center{[fx(fr, fr[0]) - 0.42, fy(fr, v), 0]} Math(to_string(v, places), 0.72, SLATE)),
    center{[fx(fr, (fr[0] + fr[1]) / 2), fy(fr, 0) - 0.7, 0]} Note(name, 0.74)
]