Skip to content

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%):

9.2 ± t(0.025, 9) × 1.9195 = 9.2 ± 2.262 × 1.9195 = 9.2 ± 4.342 = (4.86, 13.54) ms

The interval excludes 0, and it says the average improvement is between about 5 and 14 milliseconds.

Effect size:

Cohen's d = d̄ / s_d = 9.2 / 6.070 = 1.52        →  very large

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.11not 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.0005 to p = 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 t and reverses the one-tailed conclusion.
  • Testing normality of before and after instead of the differences.
  • Using df = 2n − 2. For paired data df = n − 1, where n is 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