learn-statistics-with-phoebe / Builder session 7 of 10
Learn Statistics with Phoebe · Builder session 7 of 10

Confidence intervals, the honest error bar

A point estimate without a range is a false precision. This session turns the standard error from b5 and the estimates from b6 into confidence intervals - the range you actually report. You will build t-intervals for a mean, Wald and Wilson intervals for Lumen's conversion rate, and land on the canonical result that explains why the experimentation course needs tens of thousands of sessions per arm.

🟠 Getting real Builders · DA / DE / DS Python · scipy / statsmodels 45 min
0-3 · Setup 3-15 · z, t, Wald, Wilson 15-40 · Build the intervals 40-45 · Wrap
Part 0

Report the range, not just the point

"Conversion is 3.2%" is a point estimate. "Conversion is 3.2%, 95% CI [2.71%, 3.69%]" is a claim you can defend - and, crucially, a claim that tells you what you cannot yet see. The interval carries the sample size, the noise, and the honesty all at once. Today you compute intervals for means and for proportions, learn when the textbook Wald interval quietly fails, and read the interval correctly - which is harder than it looks.

Live - built in session Self-study - run after ★ The one big idea The case: Lumen Skincare
★ What you walk out with today The z vs t distinction (and why t has fatter tails), Wald vs Wilson intervals for a proportion (and why Wilson wins when n*p is small), the bootstrap percentile CI, and the correct frequentist reading of "95% confidence." Plus the canonical Lumen result - a 95% CI of [2.71%, 3.69%] at n=5,000 - and why the variant's 3.6% sitting inside it is the whole reason experiments need to be big.
Part 1 · the vocabulary in code

z vs t, Wald vs Wilson, and what 95% means 7 min live

Which interval you reach for depends on what you know and how much data you have. Four tools, one correct interpretation.

Wald vs Wilson for a rare 3.2% conversion rate ✗ Wald interval Can spill below 0 or above 1 Too narrow when successes are few Assumes the normal has kicked in ✓ Wilson interval Always stays inside the [0, 1] range Built for rare-event proportions The safer default for conversion work For a rare 3.2% conversion rate, Wilson is the safer default; Wald can spill past 0 or 1.
🔍 Click to zoom - Wilson is the safer default when successes are rare
z (sigma known) tall, thin tails t (sigma unknown, small n) lower peak, fatter tails - wider CI fatter left tail fatter right tail When sigma is unknown and n is small, use t: its fatter tails widen the interval to pay for the extra uncertainty of estimating sigma. As n grows, t converges to z.
🔍 Click to zoom - the t distribution has fatter tails than z, so a t-interval is wider for the same confidence when n is small
LiveThe four tools, and when each applies3 min▶
  • z-interval (sigma known). When you somehow know the population standard deviation, the sampling distribution is normal and you use z critical values (1.96 for 95%). Rare in practice - you almost never know sigma.
  • t-interval (sigma unknown, small n). The realistic case: you estimate sigma from the sample, which adds uncertainty, so you use the t distribution. Its fatter tails give a slightly wider interval. As n grows, t converges to z and the difference vanishes.
  • Wald vs Wilson (for a proportion). The Wald interval, p-hat +/- z*sqrt(p-hat(1-p-hat)/n), is the textbook formula - but it behaves badly when n*p is small (few "successes"), producing intervals that are too narrow or run past 0 or 1. The Wilson interval fixes this and is the better default for rare events.
  • Bootstrap CI. Resample with replacement, recompute the statistic, and read the 2.5th and 97.5th percentiles. Works for any statistic, no formula needed - the b5 tool, applied.
What "95% confidence" actually means It is NOT "a 95% chance the true value is in this interval" - the true value is fixed, not random. It means the procedure captures the true value 95% of the time over many repeated samples. Build 100 intervals from 100 samples and about 95 of them will contain the truth. This is the frequentist reading from leader session a5, and getting it right separates people who understand intervals from people who quote them.
★ everyone builds · four intervals

Build every interval on Lumen's numbers ★ 22 min · everyone builds

A t-interval for mean order value, Wald and Wilson for the 3.2% conversion rate, the canonical [2.71%, 3.69%] result, and a bootstrap CI to tie it all back to b5.

t-interval for the mean. sigma is unknown (we estimate it from the sample), so t is correct.

t_interval.py
import numpy as np, pandas as pd
from scipy import stats

df = pd.read_parquet("lumen_orders.parquet")
ov = df["order_value"].to_numpy()

n   = ov.size
m   = ov.mean()                 # ~ 74
se  = ov.std(ddof=1) / np.sqrt(n)   # SE = s / sqrt(n),  s ~ 38

lo, hi = stats.t.interval(0.95, df=n - 1, loc=m, scale=se)
print(f"mean order value : {m:.2f}")
print(f"95% t-interval   : [{lo:.2f}, {hi:.2f}]")   # ~ [73.7, 74.3] at n=50000
# At huge n the interval is razor thin - and t is indistinguishable from z.

Wald vs Wilson for the conversion rate. 3.2% of 5,000 sessions = 160 conversions. Compare the two intervals.

proportion_ci.py
from statsmodels.stats.proportion import proportion_confint

count, nobs = 160, 5000          # 3.2% observed conversion at n = 5000

wald   = proportion_confint(count, nobs, alpha=0.05, method='normal')
wilson = proportion_confint(count, nobs, alpha=0.05, method='wilson')

print("Wald  :", [round(100*x, 2) for x in wald])    # ~ [2.71, 3.69] %
print("Wilson:", [round(100*x, 2) for x in wilson])  # ~ [2.74, 3.73] %
# At n=5000 with 160 successes they nearly agree. Now shrink the sample...

for nobs_small in (100, 50):
    c = round(0.032 * nobs_small)
    w  = proportion_confint(c, nobs_small, method='normal')
    wl = proportion_confint(c, nobs_small, method='wilson')
    print(f"n={nobs_small:>3}  Wald {[round(100*x,2) for x in w]}  "
          f"Wilson {[round(100*x,2) for x in wl]}")
# Small n*p: Wald turns too narrow / can dip below 0; Wilson stays sensible.
Wilson beats Wald when n*p is small With only a handful of conversions the Wald interval leans on a normal approximation that has not kicked in yet - it can be too narrow or spill below zero. The Wilson interval is built to stay inside [0, 1] and keeps its coverage honest for rare events. For conversion work, reach for Wilson by default.

The canonical result. State the assumptions, then read the punchline out loud.

canonical.py
# Assumptions: n = 5000 sessions, observed conversion 3.2% (160 conversions),
# 95% confidence, Wald (normal) interval.
p, n, z = 0.032, 5000, 1.96
se = (p * (1 - p) / n) ** 0.5          # ~ 0.00249  (0.25pp)
lo, hi = p - z*se, p + z*se
print(f"95% Wald CI: [{100*lo:.2f}%, {100*hi:.2f}%]")   # [2.71%, 3.69%]

variant = 0.036
print("variant 3.6% inside the control CI?", lo < variant < hi)   # True
# 3.6% sits INSIDE [2.71%, 3.69%] at n=5000 - the data cannot yet tell
# 3.2% and 3.6% apart. That is exactly why experimentation needs ~32,000/arm.

Bootstrap percentile CI. Formula-free, straight from b5 - a sanity check on the t-interval.

boot_ci.py
rng = np.random.default_rng(42)
B = 2000
boot = np.array([rng.choice(ov, size=ov.size, replace=True).mean()
                 for _ in range(B)])

lo, hi = np.percentile(boot, [2.5, 97.5])
print("bootstrap 95% CI :", [round(lo, 2), round(hi, 2)])   # ~ [73.7, 74.3]
print("t-interval  95% CI: matches - two roads, one destination")
Real world

Here is the bridge to the whole experimentation course. Lumen's control converts at 3.2%; a proposed variant is hoped to hit 3.6%. At n=5,000 the control's 95% CI is [2.71%, 3.69%] - and 3.6% sits comfortably inside it. The interval is telling you, before you spend a cent, that this sample is far too small to separate the two rates. Pushing that overlap apart to a confident "the variant wins" is what forces ~32,000 sessions per arm in the experimentation course. The confidence interval is not a footnote - it is the sample-size calculator in disguise.

Lumen's control conversion rate, with its 95% CI 0% 5% 2.71% 3.69% control 3.2% variant 3.6% The variant 3.6% sits inside the control 95% CI of [2.71%, 3.69%]. Too small a sample at n=5,000 to tell the two rates apart yet.
🔍 Click to zoom - the variant's 3.6% is not outside the control's own uncertainty
Before Session 8

This week ◐ 40 min total

Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · What does a 95% confidence interval actually mean?

The true value is fixed, not random, so it is not "95% likely" to be in one given interval. Confidence is a property of the procedure: repeat the sampling many times and about 95% of the intervals you build will contain the truth.

2 · You estimate a mean from a small sample and do not know the population sigma. Which interval, and why?

When sigma is unknown you estimate it, adding uncertainty. The t distribution's fatter tails widen the interval to account for that. As n grows, t converges to z and the two agree.

3 · Lumen's control converts at 3.2%; at n=5,000 its 95% CI is [2.71%, 3.69%]. The variant hopes for 3.6%. What does the interval tell you?

Because 3.6% falls inside the control's confidence interval at n=5,000, the data cannot distinguish it from 3.2%. That overlap is exactly why the experimentation course sizes tests at ~32,000 per arm to detect the effect.

Source material

What this session covers

This builder session covers ~80% of the computational confidence-interval content in the sources below, on the Lumen dataset. The interpretation - what "95%" does and does not mean - is the part most courses rush; we do not.

✓z vs t intervals for a mean; Wald vs Wilson for a proportion; bootstrap CIPart 1 + demo
✓The canonical [2.71%, 3.69%] result; correct frequentist interpretationdemo + callouts
◐OpenIntro Statistics ch. 5-7 - inference for means & proportionstheory spine; free PDF
◐365 Data Science - Confidence Intervalsapplied module
◐Google "The Power of Statistics" - constructing CIs in Pythonmodule 4

Builder Session 7 cheat sheet · pin this

z vs tKnow sigma → z. Estimate sigma (the usual case) → t, with fatter tails. Big n → they agree.
t-intervalstats.t.interval(0.95, df=n-1, loc=mean, scale=s/sqrt(n)).
Wald vs Wilsonproportion_confint(k, n, method='normal' | 'wilson'). Small n*p → use Wilson.
Canonical CI3.2% at n=5000 → 95% CI [2.71%, 3.69%]. Variant 3.6% sits inside it.
Correct readingThe procedure covers the truth 95% of the time. NOT "95% chance the truth is here."
Bridge to experimentsOverlap at n=5000 is why A/B tests need ~32,000/arm to separate 3.2% from 3.6%.