When data are numerical, inference targets the mean. The t-distribution replaces the normal when the population standard deviation is unknown. ANOVA generalizes the two-sample test to any number of groups.
One-sample t-test
Test whether a sample mean differs from a hypothesized value. The t-statistic replaces Z when sigma is unknown: t = (x-bar - mu0) / (s / sqrt(n)). It follows a t-distribution with n - 1 degrees of freedom. As n grows, t approaches Z.
Scheme
; One-sample t-test; H0: mu = 100, sample mean = 105, s = 15, n = 25
(define x-bar 105)
(define mu0 100)
(define s 15)
(define n 25)
(define se (/ s (sqrt n)))
(define t-stat (/ (- x-bar mu0) se))
(display "x-bar = ") (display x-bar) (newline)
(display "SE = ") (display se) (newline)
(display "t = ") (display t-stat) (newline)
(display "df = ") (display (- n 1)) (newline)
; Critical t at alpha=0.05, df=24, two-tailed: ~2.064
(display "Reject H0? ")
(display (if (> (abs t-stat) 2.064) "Yes""No"))
Python
# One-sample t-testimportmath
x_bar, mu0, s, n = 105, 100, 15, 25
se = s / math.sqrt(n)
t = (x_bar - mu0) / se
print(f"t = {t:.3f}, df = {n-1}")
print(f"Critical t (alpha=0.05, df=24) = 2.064")
print(f"Reject H0? {'Yes' if abs(t) > 2.064 else 'No'}")
Two-sample t-test
Compare means from two independent groups. The standard error combines variability from both samples: SE = sqrt(s1^2/n1 + s2^2/n2). Degrees of freedom come from Welch's approximation when variances are unequal.
# Two-sample t-testimportmath
x1, s1, n1 = 82, 10, 30
x2, s2, n2 = 78, 12, 35
se = math.sqrt(s1**2/n1 + s2**2/n2)
t = (x1 - x2) / se
print(f"t = {t:.3f}, SE = {se:.3f}")
print(f"Reject H0? {'Yes' if abs(t) > 2.045 else 'No'}")
Paired t-test
When observations come in pairs (before/after, matched subjects), compute the differences and run a one-sample t-test on those differences. This controls for subject-level variation that would inflate the two-sample SE.
Scheme
; Paired t-test; Before: (85 90 78 92 88), After: (90 94 80 96 91); Test H0: mean difference = 0
(define before (list 8590789288))
(define after (list 9094809691))
(define diffs (map - after before))
(display "Differences: ") (display diffs) (newline)
(define n (length diffs))
(define d-bar (/ (apply + diffs) n))
; Sample SD of differences
(define ss (apply + (map (lambda (d) (* (- d d-bar) (- d d-bar))) diffs)))
(define s-d (sqrt (/ ss (- n 1))))
(define se (/ s-d (sqrt n)))
(define t-stat (/ (exact->inexact d-bar) se))
(display "d-bar = ") (display (exact->inexact d-bar)) (newline)
(display "s_d = ") (display s-d) (newline)
(display "t = ") (display t-stat) (newline)
(display "df = ") (display (- n 1)) (newline)
; Critical t at alpha=0.05, df=4: ~2.776
(display "Reject H0? ")
(display (if (> (abs t-stat) 2.776) "Yes""No"))
Python
# Paired t-testimportmath
before = [85, 90, 78, 92, 88]
after = [90, 94, 80, 96, 91]
diffs = [a - b for a, b inzip(after, before)]
n = len(diffs)
d_bar = sum(diffs) / n
s_d = math.sqrt(sum((d - d_bar)**2for d in diffs) / (n - 1))
t = d_bar / (s_d / math.sqrt(n))
print(f"Diffs: {diffs}")
print(f"d_bar = {d_bar}, s_d = {s_d:.3f}")
print(f"t = {t:.3f}, df = {n-1}")
print(f"Reject H0? {'Yes' if abs(t) > 2.776 else 'No'}")
ANOVA and the F-distribution
Analysis of variance tests whether any group mean differs among k groups. It decomposes total variation into between-group (MSG) and within-group (MSE) components. The F-statistic = MSG / MSE follows an F-distribution with (k-1, n-k) degrees of freedom. A large F means the group means spread more than within-group noise would predict.