09-02: Exercises — Confidence Interval for a Proportion¶
Notes reference: 09-02: Confidence Interval for a Proportion
Q1: Basic proportion interval¶
In a random sample of 800 adults, 344 say they exercise at least three times a week.
Build a 95% confidence interval for the population proportion.
Solution
p̂ = 344/800 = 0.43 q̂ = 0.57
CHECK: n p̂ = 344 ≥ 5 ✓ n q̂ = 456 ≥ 5 ✓
SE = √(0.43 × 0.57 / 800) = √(0.2451/800) = √0.000306375 = 0.017504
z = 1.96
E = 1.96 × 0.017504 = 0.034308 → ±3.4 percentage points
CI = 0.43 ± 0.0343 = (0.3957, 0.4643) → (39.6%, 46.4%)
INTERPRETATION
"We are 95% confident that between 39.6% and 46.4% of all adults in
this population exercise at least three times a week."
Note the interval lies entirely BELOW 0.50, so we can also say the
evidence is against a majority exercising this often.
=344/800 ' 0.43
=SQRT(0.43*0.57/800) ' 0.017504
=NORM.S.INV(0.975)*SQRT(0.43*0.57/800) ' 0.034308
=0.43-0.034308 & " to " & 0.43+0.034308
prop.test(344, 800, correct = FALSE)$conf.int # Wilson: 0.3963 0.4643
binom.test(344, 800)$conf.int # exact: 0.3954 0.4652
from statsmodels.stats.proportion import proportion_confint
proportion_confint(344, 800, method="normal") # Wald
proportion_confint(344, 800, method="wilson")
Q2: Three confidence levels¶
Using the Q1 data, build 90%, 95%, and 99% intervals.
Solution
SE = 0.017504
90%: z = 1.6449 E = 0.02879 → (0.4012, 0.4588) width 0.0576
95%: z = 1.9600 E = 0.03431 → (0.3957, 0.4643) width 0.0686
99%: z = 2.5758 E = 0.04509 → (0.3849, 0.4751) width 0.0902
All three intervals lie entirely below 0.50 — the conclusion "fewer than half exercise this often" is robust to the confidence level chosen.
Q3: Sample size, no prior estimate¶
A pollster wants a margin of error of 2.5 percentage points at 95% confidence, with no prior information about p.
- Required
n - Required
nif a previous poll suggestedp ≈ 0.62 - Required
nforE = 0.01with no prior
Solution
( z )²
n = p̂ q̂ · ( ─── ) ALWAYS round UP
( E )
1. No prior → use p̂ = 0.5 (the most conservative choice, since p̂q̂
is maximized at 0.25):
n = 0.25 × (1.96/0.025)² = 0.25 × 6146.56 = 1536.64 → n = 1537
2. With p̂ = 0.62: p̂q̂ = 0.62 × 0.38 = 0.2356
n = 0.2356 × 6146.56 = 1448.13 → n = 1449
The prior estimate saves 88 respondents.
3. E = 0.01, no prior:
n = 0.25 × (1.96/0.01)² = 0.25 × 38416 = 9604 → n = 9604
Going from ±2.5% to ±1% costs 6× the sample.
=ROUNDUP(0.25*(NORM.S.INV(0.975)/0.025)^2, 0) ' 1537
=ROUNDUP(0.62*0.38*(NORM.S.INV(0.975)/0.025)^2, 0) ' 1449
=ROUNDUP(0.25*(NORM.S.INV(0.975)/0.01)^2, 0) ' 9604
n_prop <- function(E, p = 0.5, conf = 0.95)
ceiling(p*(1-p) * (qnorm(1-(1-conf)/2)/E)^2)
n_prop(0.025); n_prop(0.025, 0.62); n_prop(0.01)
Q4: Why polls use ~1,000 people¶
Show that a national poll of 1,000 gives roughly a ±3% margin of error, and explain why the country's population size is irrelevant.
Solution
Worst case p̂ = 0.5:
E = 1.96 × √(0.5 × 0.5 / 1000) = 1.96 × √0.00025 = 1.96 × 0.0158114
= 0.030990 → ±3.1 percentage points
WHY POPULATION SIZE DOES NOT MATTER
The formula E = z√(p̂q̂/n) contains NO N.
The finite population correction √((N − n)/(N − 1)) only matters when
n exceeds about 5% of N:
N = 30,000, n = 1000 → FPC = √(29000/29999) = 0.9832 (1.7% narrower)
N = 300 million, n = 1000 → FPC = 0.999998 (negligible)
So the same 1,000 respondents give essentially the same precision for a
town of 30,000 as for a country of 300 million. What matters is the SAMPLE
size, not the fraction of the population sampled.
Q5: A troublesome small sample¶
In a sample of 20 items, 1 is defective. Build a 95% CI for the defect rate three ways: Wald, plus-four, and exact.
Solution
p̂ = 1/20 = 0.05
CONDITION CHECK: n p̂ = 1 < 5 ✗
The Wald interval is NOT reliable here — but compute it anyway to see why.
WALD
SE = √(0.05 × 0.95 / 20) = √0.002375 = 0.048734
E = 1.96 × 0.048734 = 0.095519
CI = 0.05 ± 0.0955 = (−0.0455, 0.1455)
↑ A NEGATIVE PROBABILITY. The method has failed.
PLUS-FOUR (Agresti-Coull)
p̃ = (1 + 2)/(20 + 4) = 3/24 = 0.125
SE = √(0.125 × 0.875 / 24) = √0.0045573 = 0.067508
E = 1.96 × 0.067508 = 0.132316
CI = 0.125 ± 0.1323 = (−0.0073, 0.2573) → truncate to (0, 0.257)
EXACT (Clopper-Pearson)
CI = (0.00126, 0.24871) — always inside [0, 1] by construction
RECOMMENDATION: with np̂ < 5, report the EXACT (or Wilson) interval and
say which method you used.
binom.test(1, 20)$conf.int # 0.00126 0.24871
prop.test(1, 20, correct = FALSE)$conf.int # Wilson: 0.00890 0.23610
from statsmodels.stats.proportion import proportion_confint
proportion_confint(1, 20, method="beta") # exact
proportion_confint(1, 20, method="wilson")
proportion_confint(1, 20, method="agresti_coull")
Q6: Difference of two proportions¶
A drug trial: 45 of 150 in the treatment group improved; 30 of 150 in the control group improved.
Build a 95% CI for p₁ − p₂.
Solution
p̂₁ = 45/150 = 0.30 p̂₂ = 30/150 = 0.20
Point estimate: p̂₁ − p̂₂ = 0.10 (10 percentage points)
______________________
/ p̂₁q̂₁ p̂₂q̂₂
SE = √ ────── + ──────
n₁ n₂
= √(0.30 × 0.70/150 + 0.20 × 0.80/150)
= √(0.0014 + 0.0010667)
= √0.0024667
= 0.049666
E = 1.96 × 0.049666 = 0.097345
CI = 0.10 ± 0.0973 = (0.0027, 0.1973) → (0.3%, 19.7%)
INTERPRETATION
The interval EXCLUDES 0 (just barely), so the treatment's improvement
rate is significantly higher at the 5% level.
But look at the width: the true advantage could be as little as 0.3
percentage points or as much as 19.7. That is a huge range — the study
is under-powered. A larger trial would be needed before making any
clinical claim.
=45/150-30/150 ' 0.10
=SQRT(0.3*0.7/150 + 0.2*0.8/150) ' 0.049666
=0.10-NORM.S.INV(0.975)*0.049666 ' 0.002655
=0.10+NORM.S.INV(0.975)*0.049666 ' 0.197345
from statsmodels.stats.proportion import confint_proportions_2indep
confint_proportions_2indep(45, 150, 30, 150, method="wald")
Q7: Read a news report critically¶
A headline reads: "58% of Americans support the policy — poll of 1,004 adults, margin of error ±3%."
- Give the 95% confidence interval.
- Can the outlet claim "a majority supports the policy"?
- Name three sources of error the ±3% does not cover.
Solution
1. 58% ± 3% = (55%, 61%)
Verify: E = 1.96 × √(0.58 × 0.42/1004) = 1.96 × 0.015575 = 0.030527 ✓
2. YES — the ENTIRE interval lies above 50%, so a majority is supported
at the 95% confidence level.
Contrast: if the poll had found 51% ± 3%, the interval (48%, 54%)
would straddle 50% and the "majority" claim would NOT be justified.
That situation is usually reported as "a statistical tie".
3. THE MARGIN OF ERROR COVERS ONLY SAMPLING VARIABILITY. It does not
cover:
• NONRESPONSE BIAS — modern phone polls answer rates are often <10%
• COVERAGE BIAS — who is in the calling frame at all
• QUESTION WORDING — "support the policy" vs. "support the
controversial policy" can move results 10 points
• HOUSE EFFECTS — weighting and likely-voter models differ by pollster
• TIMING — opinion moves; the poll is a snapshot
A ±3% margin with a 7% response rate is precision theatre.
Q8: Verify the coverage claim¶
Show by simulation that a 95% Wald interval really does capture the true p about 95% of the time when n is large — and rather less often when n is small.
Solution
set.seed(11)
coverage <- function(n, p, reps = 20000, conf = 0.95) {
z <- qnorm(1 - (1 - conf)/2)
x <- rbinom(reps, n, p)
ph <- x / n
e <- z * sqrt(ph * (1 - ph) / n)
mean(ph - e <= p & p <= ph + e)
}
data.frame(
n = c(20, 20, 100, 100, 1000),
p = c(0.50, 0.05, 0.50, 0.05, 0.50),
coverage = mapply(coverage, c(20, 20, 100, 100, 1000),
c(0.50, 0.05, 0.50, 0.05, 0.50))
)
TYPICAL OUTPUT
n p coverage
20 0.50 0.959
20 0.05 0.641 ← disastrously below the promised 95%
100 0.50 0.943
100 0.05 0.876 ← still short
1000 0.50 0.951
CONCLUSION
The Wald interval delivers its promised coverage when n is large AND p
is near 0.5. When p is near 0 or 1, it under-covers badly — sometimes
catastrophically. That is precisely why R's prop.test and Python's
statsmodels default to the WILSON interval instead.
Rerun the simulation with method = "wilson" and the coverage stays
near 0.95 in every row.
rng = np.random.default_rng(11)
def coverage(n, p, reps=20000, conf=0.95):
z = stats.norm.ppf(1 - (1 - conf) / 2)
ph = rng.binomial(n, p, reps) / n
e = z * np.sqrt(ph * (1 - ph) / n)
return np.mean((ph - e <= p) & (p <= ph + e))
for n, p in [(20, .5), (20, .05), (100, .5), (100, .05), (1000, .5)]:
print(n, p, round(coverage(n, p), 3))
⬅️ Previous: 09-01: Exercises — Confidence Interval for a Mean ➡️ Next: 10-01: Exercises — Hypothesis Testing Fundamentals