11-03: Paired t-Test (Dependent Samples)¶
When the two measurements come from the same subject — before and after, left and right, twin and twin — the samples are dependent. Analysing the differences removes all the between-subject variation and gives a far more powerful test than the independent-samples version of 11-02.
When Data Is Paired¶
| Design | Example |
|---|---|
| Before / after | Blood pressure before and after a drug |
| Repeated measures | The same students tested in week 1 and week 10 |
| Matched pairs | Each treated patient matched to a control of the same age and sex |
| Two measurements on one unit | Two lab methods run on the same specimen |
| Natural pairs | Twins, left/right eye, husband/wife |
The give-away: n₁ must equal n₂, and each row of your data holds one subject with two numbers. If you cannot pair the rows, the samples are independent.
The Method: Collapse to One Column¶
For each pair: d = x₁ − x₂ (keep the direction consistent!)
Then run a ONE-SAMPLE t-test on the d values, against μ_d = 0.
d̄ − 0
t = ──────────────── df = n − 1 (n = number of PAIRS)
s_d / √n
d̄ = mean of the differences
s_d = standard deviation of the differences
Hypotheses¶
H₀: μ_d = 0 no average difference
H₁: μ_d ≠ 0 two-tailed — "there is a change"
μ_d > 0 right-tailed — "x₁ is larger" (e.g. the treatment lowered it)
μ_d < 0 left-tailed
Conditions¶
1. Pairs are randomly selected and INDEPENDENT OF EACH OTHER
2. The DIFFERENCES are approximately normal, OR n ≥ 30
Only the differences need to be normal — the original measurements can be anything.
Why Pairing Is More Powerful¶
Independent test: the noise is between-subject variation + treatment effect
Paired test: each subject is their own control, so between-subject
variation CANCELS and only the effect remains.
The cost is degrees of freedom: n − 1 instead of n₁ + n₂ − 2. When the pairing is real, the drop in s swamps the loss of df, and the paired test wins decisively.
Worked Example¶
Ten participants follow a training programme. Reaction time (ms) is measured before and after.
| Subject | Before | After | d = Before − After |
|---|---|---|---|
| 1 | 285 | 271 | 14 |
| 2 | 310 | 302 | 8 |
| 3 | 264 | 260 | 4 |
| 4 | 298 | 285 | 13 |
| 5 | 322 | 305 | 17 |
| 6 | 276 | 279 | −3 |
| 7 | 301 | 290 | 11 |
| 8 | 289 | 281 | 8 |
| 9 | 315 | 300 | 15 |
| 10 | 293 | 288 | 5 |
STEP 1 H₀: μ_d = 0 H₁: μ_d > 0 (training REDUCES reaction time,
so Before − After should be > 0)
α = 0.05
STEP 2 Paired design ✓ differences look roughly symmetric ✓ df = 9
STEP 3 Σd = 92 d̄ = 92 / 10 = 9.2
Σ(d − d̄)² = (4.8)² + (−1.2)² + (−5.2)² + (3.8)² + (7.8)²
+ (−12.2)² + (1.8)² + (−1.2)² + (5.8)² + (−4.2)²
= 23.04 + 1.44 + 27.04 + 14.44 + 60.84
+ 148.84 + 3.24 + 1.44 + 33.64 + 17.64
= 331.60
s_d² = 331.60 / 9 = 36.844 s_d = 6.070
SE = 6.070 / √10 = 1.9195
9.2
t = ──────── = 4.793 df = 9
1.9195
STEP 4 Critical value: t(0.05, 9) = 1.833
4.793 > 1.833 → REJECT H₀
p-value: P(T₉ > 4.793) = 0.00049 ≤ 0.05 → REJECT H₀
STEP 5 There is very strong evidence at the 5% level that the training
programme reduces mean reaction time.
Confidence interval for the mean difference (95%):
The interval excludes 0, and it says the average improvement is between about 5 and 14 milliseconds.
Effect size:
What the independent test would have given. Treating the 20 numbers as two unrelated groups: x̄₁ = 295.3, s₁ = 18.0, x̄₂ = 286.1, s₂ = 14.0, t ≈ 1.28, one-tailed p ≈ 0.11 — not significant. The between-subject spread (people simply differ in baseline reaction time) buries a real effect. That contrast is the whole argument for pairing.
Excel¶
' ══ SETUP: Before in B2:B11, After in C2:C11 ════════════════════════
=B2-C2 ' D2: differences, fill down
=COUNT(D2:D11) ' n (pairs) -> 10
=AVERAGE(D2:D11) ' d̄ -> 9.2
=STDEV.S(D2:D11) ' s_d -> 6.0700
=STDEV.S(D2:D11)/SQRT(COUNT(D2:D11))' SE -> 1.9195
=COUNT(D2:D11)-1 ' df -> 9
' ══ TEST STATISTIC AND p-VALUE ══════════════════════════════════════
=AVERAGE(D2:D11)/(STDEV.S(D2:D11)/SQRT(COUNT(D2:D11))) ' t -> 4.79288
=T.DIST.RT(G7, 9) ' right-tailed p -> 0.00049
=T.DIST.2T(ABS(G7), 9) ' two-tailed p -> 0.00098
=T.INV(0.95, 9) ' right critical t -> 1.83311
=T.INV.2T(0.05, 9) ' two-tailed critical-> 2.26216
' ══ ONE-STEP: T.TEST WITH type = 1 (PAIRED) ═════════════════════════
=T.TEST(B2:B11, C2:C11, 1, 1) ' 1 tail, paired -> 0.00049
=T.TEST(B2:B11, C2:C11, 2, 1) ' 2 tails, paired -> 0.00098
' ══ ANALYSIS TOOLPAK ════════════════════════════════════════════════
' Data ▸ Data Analysis ▸ t-Test: Paired Two Sample for Means
' Variable 1 Range = B1:B11, Variable 2 Range = C1:C11,
' Hypothesized Mean Difference = 0, tick Labels, Alpha = 0.05
' → Pearson Correlation, df, t Stat, P(T<=t) one- and two-tail, t Critical
' ══ CONFIDENCE INTERVAL FOR THE MEAN DIFFERENCE ═════════════════════
=AVERAGE(D2:D11)-T.INV.2T(0.05,9)*STDEV.S(D2:D11)/SQRT(10) ' -> 4.8578
=AVERAGE(D2:D11)+T.INV.2T(0.05,9)*STDEV.S(D2:D11)/SQRT(10) ' -> 13.5422
=CONFIDENCE.T(0.05, STDEV.S(D2:D11), 10) ' E -> 4.3422
' ══ EFFECT SIZE ═════════════════════════════════════════════════════
=AVERAGE(D2:D11)/STDEV.S(D2:D11) ' Cohen's d -> 1.5157
' ══ CONTRAST: THE WRONG (INDEPENDENT) TEST ══════════════════════════
=T.TEST(B2:B11, C2:C11, 1, 3) ' Welch, ignoring the pairing -> 0.1088
R¶
before <- c(285, 310, 264, 298, 322, 276, 301, 289, 315, 293)
after <- c(271, 302, 260, 285, 305, 279, 290, 281, 300, 288)
# ══ THE TEST ════════════════════════════════════════════════════════
t.test(before, after, paired = TRUE) # two-sided
# t = 4.7929, df = 9, p-value = 0.0009716
# 95 percent confidence interval: 4.8578 13.5422
# mean difference: 9.2
t.test(before, after, paired = TRUE, alternative = "greater")
# p-value = 0.0004858 (half the two-sided value; one-sided CI: 4.68 to Inf)
# Identical to a one-sample test on the differences:
d <- before - after
t.test(d, mu = 0, alternative = "greater")
c(n = length(d), mean = mean(d), sd = sd(d), se = sd(d)/sqrt(length(d)))
# n mean sd se
# 10.00 9.20 6.070 1.919
# ══ CHECKING THE ASSUMPTION (on the DIFFERENCES) ════════════════════
par(mfrow = c(1, 3))
hist(d, breaks = 6, col = "#8A5FBF", border = "white", main = "Differences")
boxplot(d, col = "#0FA3A3", main = "Boxplot of d"); abline(h = 0, lty = 2)
qqnorm(d, pch = 19); qqline(d, col = "#0FA3A3", lwd = 2)
par(mfrow = c(1, 1))
shapiro.test(d)
# ══ VISUALISING THE PAIRING ═════════════════════════════════════════
matplot(rbind(before, after), type = "b", pch = 19, lty = 1,
col = "#5B2A86", xaxt = "n",
xlab = "", ylab = "Reaction time (ms)",
main = "Each line is one participant")
axis(1, at = 1:2, labels = c("Before", "After"))
# ══ EFFECT SIZE ═════════════════════════════════════════════════════
mean(d) / sd(d) # Cohen's d -> 1.5157
# library(effectsize); cohens_d(before, after, paired = TRUE)
# ══ THE WRONG TEST, FOR CONTRAST ════════════════════════════════════
t.test(before, after) # independent, two-sided -> p = 0.218
t.test(before, after, alternative = "greater") # one-sided -> p = 0.109
cor(before, after) # 0.95 — strong pairing,
# which is exactly why it matters
# ══ NONPARAMETRIC ALTERNATIVE ═══════════════════════════════════════
wilcox.test(before, after, paired = TRUE) # Wilcoxon signed-rank
Python¶
import numpy as np
from scipy import stats
before = np.array([285, 310, 264, 298, 322, 276, 301, 289, 315, 293])
after = np.array([271, 302, 260, 285, 305, 279, 290, 281, 300, 288])
# ══ THE TEST ════════════════════════════════════════════════════════
res = stats.ttest_rel(before, after) # two-sided
res.statistic, res.pvalue, res.df # 4.7929, 0.00097, 9
res.confidence_interval(confidence_level=0.95) # (4.858, 13.542)
stats.ttest_rel(before, after, alternative="greater") # p = 0.000486
# Identical to a one-sample test on the differences
d = before - after
stats.ttest_1samp(d, popmean=0, alternative="greater")
d.mean(), d.std(ddof=1), stats.sem(d) # 9.2, 6.0700, 1.9195
# ══ CHECKING THE ASSUMPTION ═════════════════════════════════════════
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(12, 3))
axes[0].hist(d, bins=6, color="#8A5FBF", edgecolor="white")
axes[1].boxplot(d); axes[1].axhline(0, ls="--", color="grey")
stats.probplot(d, dist="norm", plot=axes[2])
plt.tight_layout()
stats.shapiro(d)
# ══ VISUALISING THE PAIRING ═════════════════════════════════════════
fig, ax = plt.subplots()
for b, a in zip(before, after):
ax.plot([0, 1], [b, a], "o-", color="#5B2A86", alpha=0.7)
ax.set(xticks=[0, 1], xticklabels=["Before", "After"],
ylabel="Reaction time (ms)", title="Each line is one participant")
# ══ EFFECT SIZE ═════════════════════════════════════════════════════
d.mean() / d.std(ddof=1) # 1.5157
# ══ THE WRONG TEST, FOR CONTRAST ════════════════════════════════════
stats.ttest_ind(before, after, equal_var=False) # two-sided p ≈ 0.218
np.corrcoef(before, after)[0, 1] # 0.95
# ══ NONPARAMETRIC ALTERNATIVE ═══════════════════════════════════════
stats.wilcoxon(before, after)
Quick Reference¶
| Task | Excel | R | Python |
|---|---|---|---|
| Build the differences | =B2-C2 |
d <- x - y |
d = x - y |
| Paired t-test | ToolPak ▸ Paired; T.TEST(x,y,tails,1) |
t.test(x, y, paired=TRUE) |
ttest_rel(x, y) |
| Equivalent one-sample | T.TEST(d, zeros, tails, 1) |
t.test(d, mu = 0) |
ttest_1samp(d, 0) |
| Mean difference | AVERAGE(D:D) |
mean(d) |
d.mean() |
| SD of differences | STDEV.S(D:D) |
sd(d) |
d.std(ddof=1) |
CI for μ_d |
d̄ ± T.INV.2T(α,n-1)*SE |
t.test(...)$conf.int |
res.confidence_interval() |
Normality of d |
Q-Q scatter of d |
shapiro.test(d) |
stats.shapiro(d) |
| Effect size | AVERAGE(d)/STDEV.S(d) |
mean(d)/sd(d) |
d.mean()/d.std(ddof=1) |
| Nonparametric | — | wilcox.test(x,y,paired=TRUE) |
stats.wilcoxon(x, y) |
Common Mistakes¶
- Running an independent test on paired data. It is not wrong in the sense of being invalid, but it is badly under-powered — the example above goes from
p = 0.0005top = 0.11. - Running a paired test on independent groups. This one is invalid — there is no meaningful pairing.
- Inconsistent subtraction direction, which flips the sign of
tand reverses the one-tailed conclusion. - Testing normality of
beforeandafterinstead of the differences. - Using
df = 2n − 2. For paired datadf = n − 1, wherenis the number of pairs. - Dropping a pair when one of its two values is missing but keeping the other — pairs are all-or-nothing.
Exercises: 11-03: Exercises — Paired t-Test
⬅️ Previous: 11-02: Two-Sample t-Test ➡️ Next: 12-01: Chi-Square Goodness-of-Fit Test