Paired T-Test — Before/After and Matched Pairs Analysis

Hypothesis TestingParametric TestsFree Lesson

Advertisement

Paired T-Test

The paired t-test (dependent samples t-test) tests whether the mean difference between paired observations is zero.

H0:μd=0,t=dˉsd/n,df=n1H_0: \mu_d = 0, \quad t = \frac{\bar{d}}{s_d / \sqrt{n}}, \quad df = n-1

where d = x₁ - x₂ for each pair.


When to Use Paired T-Test

DesignExample
Before-After (same subjects)Blood pressure before and after treatment
Matched pairsTwins assigned to different diets
Cross-over trialsSame subject receives both treatments (in sequence)
Repeated measurementsSame patient measured at two time points

Key: Each observation in group 1 has a natural partner in group 2.


Why Pairing Increases Power

By analyzing differences, we remove between-subject variability (e.g., people who are generally heavier vs. lighter). Only within-subject changes remain.

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

np.random.seed(42)

n = 25
# Simulate blood pressure data (paired design)
# Between-subject effect: people have different baseline BPs
individual_baselines = np.random.normal(130, 15, n)  # true individual means
true_treatment_effect = -8  # drug reduces by 8 mmHg

before = individual_baselines + np.random.normal(0, 5, n)
after = individual_baselines + true_treatment_effect + np.random.normal(0, 5, n)
differences = before - after

print("=== PAIRED T-TEST ===")
print(f"n = {n} patients measured before and after treatment")
print(f"\nBefore: mean={before.mean():.2f}, SD={before.std(ddof=1):.2f}")
print(f"After:  mean={after.mean():.2f}, SD={after.std(ddof=1):.2f}")
print(f"Diff (before−after): mean={differences.mean():.2f}, SD={differences.std(ddof=1):.2f}")

# Paired t-test: t-test on differences
t_paired, p_paired = stats.ttest_rel(before, after)
print(f"\nPaired t({n-1}) = {t_paired:.4f}, p = {p_paired:.6f}")

# Compare with (incorrect) independent t-test
t_indep, p_indep = stats.ttest_ind(before, after)
print(f"Independent t = {t_indep:.4f}, p = {p_indep:.4f} (would miss the effect!)")

print(f"\nThe paired test is {p_indep/p_paired:.0f}× more powerful here")
print("because it removes between-person variability")

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")

Effect Size for Paired T-Test

# Cohen's d for paired design
d_paired = differences.mean() / differences.std(ddof=1)
print(f"Cohen's d (paired) = {d_paired:.4f}")
print(f"Effect size: {'small' if abs(d_paired)<0.5 else 'medium' if abs(d_paired)<0.8 else 'large'}")

# Compared to independent Cohen's d (would be smaller)
sp = np.sqrt(((n-1)*before.std()**2 + (n-1)*after.std()**2) / (2*n-2))
d_indep = (before.mean() - after.mean()) / sp
print(f"Cohen's d (independent, for comparison) = {d_indep:.4f}")

Key Takeaways

  1. Paired t-test = one-sample t-test on the differences
  2. Use it when observations are naturally linked (before-after, matched, cross-over)
  3. More powerful than independent t-test when pairing is appropriate, because between-subject noise is removed
  4. Check normality of differences (not of the original groups)
  5. scipy.stats.ttest_rel(a, b) performs the paired t-test
  6. Report: mean difference, SD of differences, t, df, p, 95% CI for difference, Cohen's d

Advertisement

Need Expert Statistics Help?

Get personalized tutoring, dissertation support, or statistical consulting.

Advertisement