Context:FIT2086_MOC · Studio 4 additional question · the empirical audit of Confidence Intervals — does a 95% procedure actually cover 95% of the time? · same simulate-and-count skeleton as Monte Carlo Estimator Comparison, but the thing counted is coverage, not bias/variance
Problem it solves: measure how badly an approximate interval misses its advertised confidence level, and find the sample size at which the approximation becomes safe.
Quick Revision
🎯 Trigger: “is this interval really 95%”, “how bad is the plug-in approximation”, “at what n does it become acceptable” ➔ loop: generate from a known θ → build the interval → test containment → tally.
⚠️ Key Constraint: the simulation only works because you chose the population — pop.mu/pop.lambda is the truth the interval is checked against, and it can never be estimated from the data inside the loop.
🔧 Minimal Working Example
testCIknownSigma2 <- function(pop.mu, pop.sigma2, n, niter) { retval = list(); retval$coverage = 0 for (i in 1:niter) { y = rnorm(n, pop.mu, sqrt(pop.sigma2)) # rnorm takes sd, NOT variance mu.hat = mean(y) CI = mu.hat + c(-1.96*sqrt(pop.sigma2)/sqrt(n), 1.96*sqrt(pop.sigma2)/sqrt(n)) if (pop.mu >= CI[1] && pop.mu <= CI[2]) { # containment test retval$coverage = retval$coverage + 1 } } retval$coverage = retval$coverage/niter # count ➔ proportion return(retval)}testCIknownSigma2(pop.mu=0, pop.sigma2=1, n=5, niter=1e4)
Expected output:$coverage≈0.95 — and it stays ≈0.95 for (μ,σ2,n)=(2,1,5), (2,5,5), (0,1,25). Nothing moves it, because the known-variance interval is exact at every n and every parameter value: the pivot σ/nμ^−μ is exactly N(0,1) regardless.
🔀 Variations
1. Plug-in z vs exact t — where the approximation breaks
Estimate σ^u2 inside the loop and build both intervals from the same sample:
se = sqrt(var(y))/sqrt(n)CI.approx = mu.hat + c(-1.96*se, 1.96*se) # z with a plugged-in sigmaCI.t = mu.hat + c(-qt(1-0.05/2, n-1)*se, # exact, unknown-variance qt(1-0.05/2, n-1)*se)
then sweep for (n in 3:100) and plot both coverage curves against n.
Expected output: the t curve sits flat on 0.95 for everyn; the plug-in z curve starts well below0.95 at n=3–5 and climbs toward it, the two becoming indistinguishable as n grows.
Reading ➔ substituting σ^u for σ ignores the uncertainty in that estimate, so the interval is too narrow and undercovers; the wider tα/2,n−1 multiplier is exactly the repair (Student-t Distribution).
2. Poisson approximate interval — a two-dimensional grid
λ^ML=Yˉ, and the approximate interval is λ^ML±1.96λ^ML/n (variance v(λ)=λ plugged in). Sweep both parameters with a nested loop into a matrix:
M = matrix(NA, 4, 5) # 4 lambdas x 5 sample sizes, prefilled NAL = c(1,5,10,50); N = c(5,10,25,50,100)for (i in 1:4) for (j in 1:5) M[i,j] = testCIlambda(L[i], N[j], 1e5)$coverageresults = data.frame(M)row.names(results) <- paste0("lambda=", L)names(results) <- paste0("n=", N)
Expected output: coverage is essentially 0.95 across the grid except at (λ=1,n=5), where it is clearly poor, and (λ=5,n=5), where it is a little low.
Reading ➔ for a Poisson the CLT works twice — the approximation improves as n grows and as λ grows (a Pois(λ) is itself a sum of λ unit-rate pieces), so only the small-λ, small-n corner fails.
When It Flips: exactness is a property of the pivot, not the sample size. Case 1 (known σ2) and the t interval are exact at n=3; every interval that plugs an estimate into the variance — σ^u for σ, λ^ for λ, θ^ for θ(1−θ) — is asymptotic and undercovers at small n.
✍️ Practice
Practice 1: from a blank editor, write the loop body that tallies coverage for the Poisson interval, given pop.lambda, n, niter.
Reference solution
for (i in 1:niter) { y = rpois(n, pop.lambda) lambda.hat = mean(y) se = sqrt(lambda.hat)/sqrt(n) # v(lambda.hat) = lambda.hat CI = lambda.hat + c(-1.96*se, 1.96*se) if (pop.lambda >= CI[1] && pop.lambda <= CI[2]) retval$coverage = retval$coverage + 1}retval$coverage = retval$coverage/niter
Key move: the standard error is λ^/n, notvar(y)/n — for a Poisson the variance is the mean, so the family assumption supplies it for free.
Practice 2: your plug-in z simulation returns coverage 0.87 at n=3. Is this a bug or the expected answer, and how would you tell?
Reference solution
Key move:expected — validate by running the known-variance version on the same n; if that returns ≈0.95 the loop mechanics are correct and the shortfall is genuine undercoverage from the plugged-in σ^u. Always audit new simulation code against a case with a known exact answer before trusting the case you actually care about.
⚠️ Common Mistakes
💡 Passing the variance to rnorm ➔ rnorm(n, mu, sigma2) silently generates from the wrong population; the third argument is the sd, hence sqrt(pop.sigma2).
💡 Checking containment against μ^ ➔ the test is pop.mu >= CI[1] && pop.mu <= CI[2]; the interval is built aroundμ^, so testing μ^ returns coverage 1 every time and measures nothing.
💡 Reading a 0.95 result as “my interval is correct” ➔ coverage at one (μ,σ2,n) proves nothing about others; the whole point of the sweep is that the failure lives in a corner of the parameter grid.
💡 Too few iterations ➔ coverage is itself an estimated proportion with se=0.95⋅0.05/niter; at niter=100 that is ≈0.02, wide enough to hide the effect being measured. The studio uses 104–105.
💡 Growing the results vector inside the sweep ➔ preallocate with rep.int(0, 98) or matrix(NA, 4, 5); c() in a loop is O(k2).