Paired T-Test
Hypothesis Testing
Measuring Change in Matched Pairs
The paired t-test analyzes before/after or matched data by focusing on within-pair differences. It eliminates confounding variables and provides more powerful inference than independent tests.
- Clinical Research — Measuring treatment effects using pre/post measurements
- Sports Science — Evaluating training programs with athlete performance data
- Business — Assessing intervention impact using matched customer pairs
When observations are linked, the paired t-test reveals what independent tests miss.
The paired t-test (dependent samples t-test) tests whether the mean difference between paired observations is zero.
where d = x₁ - x₂ for each pair.
When to Use Paired T-Test
| Design | Example |
|---|---|
| Before-After (same subjects) | Blood pressure before and after treatment |
| Matched pairs | Twins assigned to different diets |
| Cross-over trials | Same subject receives both treatments (in sequence) |
| Repeated measurements | Same patient measured at two time points |
Why Pairing Increases Power
Visualization
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# 1. Before-after connected plot
for i in range(min(25, n)):
color = 'green' if before[i] > after[i] else 'red'
axes[0].plot([0, 1], [before[i], after[i]], color=color, alpha=0.4, linewidth=1)
axes[0].plot([0, 1], [before.mean(), after.mean()], 'k-', linewidth=3, label='Mean')
axes[0].scatter([0]*n, before, s=20, color='steelblue', alpha=0.5)
axes[0].scatter([1]*n, after, s=20, color='coral', alpha=0.5)
axes[0].set_xticks([0, 1])
axes[0].set_xticklabels(['Before', 'After'])
axes[0].set_title('Paired Observations')
axes[0].set_ylabel('Blood Pressure (mmHg)')
# 2. Histogram of differences
axes[1].hist(differences, bins=12, edgecolor='black', color='steelblue', alpha=0.7)
axes[1].axvline(0, color='red', linewidth=2, linestyle='--', label='H₀: Δ=0')
axes[1].axvline(differences.mean(), color='green', linewidth=2, linestyle='-',
label=f'Δ̄={differences.mean():.2f}')
axes[1].set_title('Distribution of Differences')
axes[1].set_xlabel('Before − After')
axes[1].legend()
# 3. Q-Q plot of differences (check normality assumption)
stats.probplot(differences, dist='norm', plot=axes[2])
axes[2].set_title('Q-Q Plot of Differences\n(Should be approximately linear)')
plt.tight_layout()
plt.savefig('paired_t_test.png', dpi=150)
plt.show()
# Confidence interval for the mean difference
ci = stats.t.interval(0.95, df=n-1, loc=differences.mean(),
scale=stats.sem(differences))
print(f"\n95% CI for mean difference: ({ci[0]:.2f}, {ci[1]:.2f}) mmHg")
print(f"Interpretation: We are 95% confident the drug reduces BP by")
print(f"{ci[0]:.1f} to {ci[1]:.1f} mmHg on average")