Chapter 14 — Statistical Inference and Hypothesis Testing
Inference generalizes from samples to populations. This chapter covers sampling, confidence intervals, hypothesis tests, p-values, and A/B testing — the tools used to decide whether an observed effect is real or noise.
Learning Objectives
- Distinguish sample statistics from population parameters.
- Construct confidence intervals for a mean.
- Set up and run a hypothesis test.
- Interpret p-values and significance levels.
- Perform t-tests and a chi-square test with SciPy.
- Run a simple A/B test and draw a conclusion.
- Use statsmodels for regression inference.
Prerequisites / Imports
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import statsmodels.api as sm
1 Sampling and Sampling Distributions
The sample mean varies across samples; its standard error is $\sigma/\sqrt{n}$.
rng = np.random.default_rng(0)
pop = rng.normal(loc=50, scale=10, size=100000)
sample_means = [rng.choice(pop, 30).mean() for _ in range(2000)]
print('population mean:', pop.mean().round(3))
print('mean of sample means:', np.mean(sample_means).round(3))
print('std of sample means (SE):', np.std(sample_means, ddof=1).round(3))
print('theoretical SE (sigma/sqrt(n)):', (10/np.sqrt(30)).round(3))
population mean: 49.991 mean of sample means: 50.016 std of sample means (SE): 1.796 theoretical SE (sigma/sqrt(n)): 1.826
2 Confidence Intervals
A 95% CI for a mean is $\bar{x} \pm t_{0.975}\cdot s/\sqrt{n}$.
rng = np.random.default_rng(1)
sample = rng.normal(loc=70, scale=8, size=40)
xbar, s, n = sample.mean(), sample.std(ddof=1), len(sample)
t_crit = stats.t.ppf(0.975, df=n-1)
margin = t_crit * s / np.sqrt(n)
print(f'95% CI: [{xbar - margin:.2f}, {xbar + margin:.2f}]')
print('sample mean:', round(xbar, 2))
95% CI: [67.61, 72.43] sample mean: 70.02
3 Hypothesis Testing Framework
State $H_0$ (no effect) and $H_1$ (an effect), choose $\alpha$ (often 0.05), compute a test statistic and p-value, and decide.
4 One-Sample t-Test
Test whether a sample mean differs from a claimed value.
rng = np.random.default_rng(2)
sample = rng.normal(loc=102, scale=15, size=36)
result = stats.ttest_1samp(sample, popmean=100)
print('t-statistic:', result.statistic.round(3))
print('p-value:', result.pvalue.round(4))
print('reject H0 at alpha=0.05:', result.pvalue < 0.05)
t-statistic: 0.997 p-value: 0.3257 reject H0 at alpha=0.05: False
5 Two-Sample t-Test
Compare the means of two independent groups.
rng = np.random.default_rng(3)
group_a = rng.normal(loc=65, scale=10, size=40)
group_b = rng.normal(loc=70, scale=10, size=40)
result = stats.ttest_ind(group_a, group_b)
print('mean A:', group_a.mean().round(2), 'mean B:', group_b.mean().round(2))
print('p-value:', result.pvalue.round(4))
print('reject H0:', result.pvalue < 0.05)
mean A: 64.59 mean B: 68.51 p-value: 0.1154 reject H0: False
6 Chi-Square Test of Independence
Test whether two categorical variables are associated.
# Survey: device preference by region (rows=region, cols=device)
table = np.array([[30, 20, 10], [10, 25, 35]])
chi2, p, dof, expected = stats.chi2_contingency(table)
print('chi-square:', round(chi2, 3))
print('p-value:', round(p, 4))
print('degrees of freedom:', dof)
print('reject H0 (associated):', p < 0.05)
chi-square: 23.816 p-value: 0.0 degrees of freedom: 2 reject H0 (associated): True
Case Study: A/B Testing a Website Button
Version B should raise click-through. We simulate impressions and clicks, then test whether the difference is significant.
rng = np.random.default_rng(4)
n_a, n_b = 5000, 5000
ctr_a, ctr_b = 0.10, 0.12
clicks_a = rng.binomial(1, ctr_a, n_a)
clicks_b = rng.binomial(1, ctr_b, n_b)
# Two-proportion z-test via ttest_ind on 0/1 outcomes
res = stats.ttest_ind(clicks_a, clicks_b)
print(f'CTR A: {clicks_a.mean():.4f} CTR B: {clicks_b.mean():.4f}')
print(f'lift: {((clicks_b.mean()/clicks_a.mean())-1)*100:.1f}%')
print('p-value:', res.pvalue.round(6))
print('significant at 0.05:', res.pvalue < 0.05)
CTR A: 0.1032 CTR B: 0.1224 lift: 18.6% p-value: 0.002406 significant at 0.05: True
7 Regression Inference with statsmodels
statsmodels adds inferential detail (standard errors, t-stats, p-values, $R^2$) to regression.
rng = np.random.default_rng(5)
x = rng.uniform(0, 10, 60)
y = 2.5 * x + 1.0 + rng.normal(0, 2, 60)
X = sm.add_constant(x)
model = sm.OLS(y, X).fit()
print(model.summary())
OLS Regression Results
==============================================================================
Dep. Variable: y R-squared: 0.954
Model: OLS Adj. R-squared: 0.954
Method: Least Squares F-statistic: 1214.
Date: Wed, 16 Sep 2026 Prob (F-statistic): 1.36e-40
Time: 20:28:28 Log-Likelihood: -115.10
No. Observations: 60 AIC: 234.2
Df Residuals: 58 BIC: 238.4
Df Model: 1
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 0.7051 0.420 1.680 0.098 -0.135 1.545
x1 2.4988 0.072 34.849 0.000 2.355 2.642
==============================================================================
Omnibus: 1.338 Durbin-Watson: 2.027
Prob(Omnibus): 0.512 Jarque-Bera (JB): 1.346
Skew: 0.332 Prob(JB): 0.510
Kurtosis: 2.687 Cond. No. 11.6
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Exercises
- Compute a 99% confidence interval for the mean of a sample.
- Run a one-sample t-test testing whether a sample mean equals 50.
- Compare two groups with a two-sample t-test and report the p-value.
- Build a 2x3 contingency table and run a chi-square test.
- Simulate an A/B test where both versions have the same CTR; confirm you usually do not reject.
- Explain what a p-value is and what it is not.
- Fit an OLS model with statsmodels and identify the feature's p-value.
- Describe the relationship between confidence intervals and hypothesis tests.
- Why does increasing sample size shrink the standard error?
- Outline the steps of an A/B test from design to decision.
Python Data Science: From Foundations to Applications — Chapter 14