Solutions
Hypothesis Logic — Worked Solutions ✅
Five challenges, each verified in code.
⚙️ Setup¶
In [1]:
import numpy as np, pandas as pd, matplotlib.pyplot as plt
from scipy import stats
IND="#4f46e5"; DEEP="#4338ca"; LIGHT="#818cf8"; INK="#1a2138"; GRID="#e6e9f2"; GREEN="#059669"; RED="#ef4444"
BASE="https://raw.githubusercontent.com/johnfisher-ai/Statistics-Data-Science-AI-Visual-Book/main/data/"
rng = np.random.default_rng(74)
rng = np.random.default_rng(740)
CHALLENGE 1
State the hypotheses
A coach claims a drill raises free-throw % above 70%. Write H0 and H1 and say which is one-sided.
In [2]:
print("H0: p = 0.70 (no improvement)")
print("H1: p > 0.70 (the drill helps) -> ONE-SIDED, upper tail")
print("We reject H0 only if the data fall far enough into the upper tail.")
H0: p = 0.70 (no improvement) H1: p > 0.70 (the drill helps) -> ONE-SIDED, upper tail We reject H0 only if the data fall far enough into the upper tail.
CHALLENGE 2
Build a null distribution
Simulate 50 fair coins, 20,000 times, and find the 2.5th/97.5th percentiles of the heads-fraction.
In [3]:
sim = rng.binomial(50, 0.5, 20000)/50
lo,hi = np.percentile(sim,[2.5,97.5])
print(f"under H0, 95% of fair-coin results fall in [{lo:.2f}, {hi:.2f}]; outside that is surprising")
under H0, 95% of fair-coin results fall in [0.36, 0.64]; outside that is surprising
CHALLENGE 3
Compute a p-value by simulation
Observed 33 heads in 50 tosses. Two-sided p-value under a fair coin, by simulation.
In [4]:
phat=33/50; sim=rng.binomial(50,0.5,200000)/50
p=np.mean(np.abs(sim-0.5) >= abs(phat-0.5))
print(f"phat={phat:.2f}, two-sided p-value = {p:.4f}")
print("reject H0 at 5%" if p<0.05 else "fail to reject H0 at 5%")
phat=0.66, two-sided p-value = 0.0243 reject H0 at 5%
CHALLENGE 4
One-sample t-test from scratch
For a sample of 40 with mean 51.2 and sd 9, test H0: mu=50 (two-sided). Compute t and the p-value, then check with scipy.
In [5]:
xbar,s,n,mu0=51.2,9.0,40,50.0; se=s/np.sqrt(n); t=(xbar-mu0)/se
p=2*(1-stats.t.cdf(abs(t),n-1))
print(f"t={t:.3f}, df={n-1}, p={p:.4f}")
x=rng.normal(51.2,9,40); x=(x-x.mean())/x.std(ddof=1)*9+51.2 # force mean/sd
print(f"scipy on a matched sample: t={stats.ttest_1samp(x,50).statistic:.3f}, p={stats.ttest_1samp(x,50).pvalue:.4f}")
t=0.843, df=39, p=0.4042 scipy on a matched sample: t=0.843, p=0.4042
CHALLENGE 5
Real data: the short-fill test
Load the-logic-of-hypothesis-testing--factory_fills.xlsx and run the one-sided one-sample t-test of H0: mu = 500.
In [6]:
try: fills = pd.read_excel("../../data/the-logic-of-hypothesis-testing--factory_fills.xlsx", sheet_name="Fills")
except FileNotFoundError: fills = pd.read_excel(BASE+"the-logic-of-hypothesis-testing--factory_fills.xlsx", sheet_name="Fills")
x=fills.fill_ml; res=stats.ttest_1samp(x,500)
print(f"n={len(x)}, mean={x.mean():.2f}, t={res.statistic:.2f}, one-sided p={res.pvalue/2:.4f}")
print("reject H0: the line under-fills" if res.pvalue/2<0.05 else "fail to reject")
n=45, mean=498.07, t=-2.57, one-sided p=0.0069 reject H0: the line under-fills
Statistics, Data Science and AI: A Visual Handbook · © 2026 John Fisher