STAT-151 · Lesson 8 of 12 · 27 min

Comparing two groups: t tests and nonparametric alternatives

Many experiments compare two conditions: treated and control plots, before and after an intervention, two populations of a species, two analytical methods. This lesson covers the main tools for comparing two groups on a numerical outcome. The key distinction is between independent samples, where different units are measured in each group, and paired samples, where the same or matched units are measured twice. Choosing the wrong analysis for the design is one of the most common errors in scientific papers. The lesson also covers the assumptions of t tests, how to check them, and the rank-based nonparametric alternatives for data that violate them.

Paired designs

In a paired design each observation in one group has a natural partner in the other: the same patient before and after treatment, the left and right leaves of the same plant, two methods applied to the same water sample. Pairing removes the variation between units, which is often large, and so can make comparisons much more precise. The analysis reduces to a one-sample problem: compute the difference for each pair, then apply the one-sample t procedures of lessons 6 and 7 to the differences.

\[t = \frac{\bar{d}}{s_d/\sqrt{n}}, \qquad \text{df} = n - 1\]

Here d̄ is the mean difference, s_d the standard deviation of the differences, and n the number of pairs.

Worked problem

Two methods for measuring phosphate

An invented laboratory compares a new colorimetric kit with the reference method on 10 lake water samples, measuring each sample both ways. The differences (kit minus reference, µg/L) have mean 1.8 and SD 2.1. Is there evidence of a systematic difference?

Show solution
  1. H₀: mean difference = 0; H₁: mean difference ≠ 0.
  2. SE = 2.1/√10 = 0.664; t = 1.8/0.664 = 2.71, df = 9.
  3. Two-sided p ≈ 0.024.
  4. 95 percent CI for the mean difference: 1.8 ± 2.262 × 0.664 = 0.30 to 3.30 µg/L.

Answer: The kit reads systematically higher than the reference method, by about 0.3 to 3.3 µg/L. Whether that bias matters depends on the concentrations of interest; a lab might apply a correction or recalibrate.

Analyzing paired data correctly, and the cost of ignoring the pairing
import numpy as np
from scipy import stats
rng = np.random.default_rng(1519)

# Invented phosphate (µg/L) for 10 lake samples, each measured by two methods
reference = np.round(rng.uniform(8, 40, 10), 1)
kit = np.round(reference + rng.normal(1.8, 2.1, 10), 1)

paired = stats.ttest_rel(kit, reference)
indep = stats.ttest_ind(kit, reference)
d = kit - reference
print("differences:", d)
print(f"mean difference = {d.mean():.2f}, SD of differences = {d.std(ddof=1):.2f}")
print(f"paired t test:      t = {paired.statistic:.2f}, p = {paired.pvalue:.4f}")
print(f"(wrong) independent: t = {indep.statistic:.2f}, p = {indep.pvalue:.4f}")
Output
differences: [ 1.9  1.8  3.3  4.7  0.9  3.9  6.2 -0.6  6.   1.1]
mean difference = 2.92, SD of differences = 2.28
paired t test:      t = 4.06, p = 0.0028
(wrong) independent: t = 0.65, p = 0.5241

The simulated samples happen to show a larger bias (2.9 µg/L) than the summary figures in the worked example, but the lesson is the same. Analyzing the same data as if the two sets of measurements were independent gives a far larger p-value, because the large differences between lake samples swamp the small, consistent difference between methods. The pairing is part of the design and must be part of the analysis.

For method comparisons in particular, a test of the mean difference is only part of the story. A Bland–Altman plot, which graphs the difference against the mean of the two measurements for each sample, shows whether the bias is constant or changes with concentration and gives limits of agreement (mean difference ± 1.96 SD of differences) within which most individual differences fall. CHEM-213 discusses method validation in more depth.

A Bland–Altman plot with the mean of two methods on the horizontal axis and their difference on the vertical axis. Points scatter around a horizontal line at the mean difference of about 1.8, with dashed lines at the upper and lower limits of agreement.
Bland–Altman plot for comparing two measurement methods (invented data)

Independent samples: the two-sample t test

When the two groups contain different, unrelated units, the comparison is of two independent means. The test statistic divides the difference in sample means by its standard error. There are two versions. The pooled (Student) t test assumes the two populations have equal variances and combines the sample variances into one estimate. Welch’s t test does not assume equal variances; it uses each group’s own variance and an adjusted number of degrees of freedom.

\[t = \frac{\bar{x}_1 - \bar{x}_2}{\sqrt{\dfrac{s_1^2}{n_1} + \dfrac{s_2^2}{n_2}}} \quad \text{(Welch)}\]

Welch’s test performs almost as well as the pooled test when variances are equal and much better when they are not, especially when the group sizes also differ. Many statisticians therefore recommend Welch’s test as the default for comparing two independent means. In scipy, stats.ttest_ind uses the pooled test unless equal_var=False is given.

Welch and pooled two-sample t tests for snail shell length
import numpy as np
from scipy import stats
rng = np.random.default_rng(1520)

# Invented: shell length (mm) of snails from a sheltered and an exposed shore
sheltered = np.round(rng.normal(24.0, 2.0, 18), 1)
exposed = np.round(rng.normal(21.5, 4.0, 12), 1)

for name, x in [("sheltered", sheltered), ("exposed", exposed)]:
    print(f"{name:9s}: n = {x.size}, mean = {x.mean():.2f}, SD = {x.std(ddof=1):.2f}")

w = stats.ttest_ind(sheltered, exposed, equal_var=False)
p = stats.ttest_ind(sheltered, exposed, equal_var=True)
ci = w.confidence_interval(0.95)
print(f"Welch:  t = {w.statistic:.2f}, df = {w.df:.1f}, p = {w.pvalue:.4f}, "
      f"95% CI {ci.low:.2f} to {ci.high:.2f} mm")
print(f"Pooled: t = {p.statistic:.2f}, df = {p.df:.0f}, p = {p.pvalue:.4f}")
Output
sheltered: n = 18, mean = 24.60, SD = 1.98
exposed  : n = 12, mean = 21.62, SD = 4.40
Welch:  t = 2.20, df = 14.0, p = 0.0453, 95% CI 0.07 to 5.88 mm
Pooled: t = 2.52, df = 28, p = 0.0175

The two tests disagree noticeably here. The exposed shore has both the smaller sample and the larger spread, and the pooled test, by averaging the two variances, understates the uncertainty in the exposed-shore mean, so its p-value is too small. Welch’s test gives weaker but more honest evidence of a difference, with an interval that barely excludes zero.

The simulation below shows why Welch’s test is the safer default. When the smaller group has the larger variance and there is no true difference, the pooled test rejects H₀ far more often than the nominal 5 percent.

With unequal variances and sizes, the pooled test gives too many false positives
import numpy as np
from scipy import stats
rng = np.random.default_rng(15109)
reps = 10_000
rej_pooled = rej_welch = 0
for _ in range(reps):
    a = rng.normal(0, 1, 30)     # larger group, smaller SD
    b = rng.normal(0, 3, 10)     # smaller group, larger SD; same mean
    rej_pooled += stats.ttest_ind(a, b).pvalue < 0.05
    rej_welch += stats.ttest_ind(a, b, equal_var=False).pvalue < 0.05
print(f"false positive rate, pooled t: {rej_pooled/reps:.3f}")
print(f"false positive rate, Welch t:  {rej_welch/reps:.3f}")
Output
false positive rate, pooled t: 0.204
false positive rate, Welch t:  0.051

Checking assumptions

The t procedures assume that observations are independent within and between groups, and that each population is approximately normal. Independence comes from the design, through random sampling or random assignment, and cannot be checked from the data alone; violations, such as pseudoreplication, are the most serious. Normality matters less as samples grow, because of the central limit theorem. A practical guide:

Practical guidance on the normality assumption for t procedures
Sample size per groupWhat to checkAdvice
Under about 15Plot the data: dot plot, box plot or normal Q–Q plotUse t only if the data look roughly symmetric with no outliers
About 15 to 40Look for strong skewness and outlierst is usually fine unless skewness is severe
Over about 40Check for extreme outliers and dependencet is robust to non-normality; outliers still matter

Formal tests of normality, such as the Shapiro–Wilk test, are of limited use: in small samples they lack power to detect important departures, and in large samples they flag trivial ones that do not matter. Graphs are more informative. A normal quantile–quantile (Q–Q) plot compares the ordered data with the values expected from a normal distribution; normal data fall close to a straight line, while curvature indicates skewness and points far from the line at the ends indicate heavy tails or outliers.

Two normal Q–Q plots. The left, for approximately normal data, shows points close to a straight line. The right, for right-skewed data, shows points curving upward above the line at the high end.
Normal Q–Q plots for approximately normal data (left) and right-skewed data (right)

When data are right-skewed and positive, as with concentrations, a log transformation often makes them nearly normal, as lesson 4 showed. A t test on log-transformed data compares geometric means, and back-transforming the interval for the difference gives an interval for the ratio of geometric means, which is often the natural scale for concentrations: “the treated soils had 1.6 times the nitrate of the controls (95 percent CI 1.2 to 2.1)”.

Nonparametric alternatives

When data are clearly non-normal, contain outliers, are ordinal (ranked categories such as a pain score from 0 to 10), or come from very small samples, rank-based tests offer an alternative. They replace the data by their ranks and so are not affected by the exact values of extreme observations.

The Wilcoxon signed-rank test is the alternative to the paired t test. It ranks the absolute differences, attaches the signs, and asks whether positive and negative ranks are balanced. The Wilcoxon rank-sum test, also called the Mann–Whitney U test, is the alternative to the two-sample t test. It ranks all observations together and asks whether one group tends to have higher ranks than the other.

An outlier inflates the variance for the t test; the rank test is unaffected
import numpy as np
from scipy import stats

# Invented: days to recovery for mice on two treatments, with one extreme value
control = np.array([9, 11, 12, 12, 14, 15, 16, 18, 21, 45])
treated = np.array([6, 7, 8, 9, 9, 10, 11, 12, 13, 15])

t = stats.ttest_ind(control, treated, equal_var=False)
u = stats.mannwhitneyu(control, treated, alternative="two-sided")
print(f"means:   control {control.mean():.1f}, treated {treated.mean():.1f}")
print(f"medians: control {np.median(control):.1f}, treated {np.median(treated):.1f}")
print(f"Welch t test:   p = {t.pvalue:.4f}")
print(f"Mann-Whitney U: U = {u.statistic:.1f}, p = {u.pvalue:.4f}")

# Effect size for Mann-Whitney: probability a control value exceeds a treated value
ps = u.statistic / (control.size * treated.size)
print(f"P(control > treated) estimated as {ps:.2f}")
Output
means:   control 17.3, treated 10.0
medians: control 14.5, treated 9.5
Welch t test:   p = 0.0559
Mann-Whitney U: U = 84.0, p = 0.0110
P(control > treated) estimated as 0.84

The single recovery time of 45 days inflates the control group’s standard deviation, so the Welch test is less convincing, whereas the Mann–Whitney test, which only uses the ranks, gives stronger evidence. The natural effect size for the Mann–Whitney test is the probability of superiority: the chance that a randomly chosen value from one group exceeds one from the other. Here, a control mouse takes longer to recover than a treated mouse about 84 percent of the time.

Nonparametric tests are sometimes described as assumption-free, which is not true. They still require independent observations. The Mann–Whitney test compares whole distributions; it is a test of medians only if the two distributions have the same shape. And when the data really are normal, rank tests are slightly less powerful than t tests, though usually only by about 5 percent. Some researchers prefer to transform data, use bootstrap intervals (lesson 6), or use permutation tests, which shuffle group labels to build the null distribution directly.

A permutation test for the difference in medians
import numpy as np
from scipy import stats
rng = np.random.default_rng(1521)
control = np.array([9, 11, 12, 12, 14, 15, 16, 18, 21, 45])
treated = np.array([6, 7, 8, 9, 9, 10, 11, 12, 13, 15])

def diff_medians(x, y, axis=-1):
    return np.median(x, axis=axis) - np.median(y, axis=axis)

res = stats.permutation_test((control, treated), diff_medians, n_resamples=20_000,
                             alternative="two-sided", random_state=rng)
print(f"observed difference in medians = {res.statistic:.1f} days")
print(f"permutation p-value = {res.pvalue:.4f}")
Output
observed difference in medians = 5.0 days
permutation p-value = 0.0178

Choosing a method

Choosing a test for comparing groups on a numerical outcome
DesignData roughly normal or n largeSkewed, ordinal or outliers
Paired (same or matched units)Paired t test on differencesWilcoxon signed-rank test, or t on log ratios
Two independent groupsWelch two-sample t testMann–Whitney U test, log transform, or permutation test
More than two groupsOne-way ANOVA (lesson 12)Kruskal–Wallis test

Common mistakes

  • MistakeUse a two-sample t test for before-and-after measurements on the same subjects.

    InsteadBefore-and-after data are paired; analyze the differences with a paired t test or signed-rank test.

  • MistakeAlways use the pooled t test because it has more degrees of freedom.

    InsteadUse Welch’s test by default; the pooled test can give too many false positives when variances and sizes differ.

  • MistakeRun a normality test and switch methods if p < 0.05.

    InsteadJudge normality from plots and sample size; formal tests are underpowered for small n and oversensitive for large n.

  • MistakeNonparametric tests have no assumptions.

    InsteadThey still require independence, and the Mann–Whitney test compares medians only when the distributions have the same shape.

  • MistakeDelete outliers so that the t test works.

    InsteadInvestigate outliers; remove them only for documented errors, and otherwise use robust or rank methods and report both analyses.

Summary and key terms

  • Paired designs measure the same or matched units twice; analyze the differences.
  • Pairing removes between-unit variation and can greatly increase precision.
  • For independent groups, Welch’s t test is a safe default because it does not assume equal variances.
  • Independence is the most important assumption; normality matters less as n grows.
  • Use plots, such as Q–Q plots, rather than formal tests to judge normality.
  • Log transformations turn comparisons of skewed concentrations into comparisons of ratios of geometric means.
  • The Wilcoxon signed-rank and Mann–Whitney tests are rank-based alternatives; permutation tests build null distributions by shuffling labels.
paired design
A design in which observations come in natural pairs, such as before and after measurements on the same unit.
independent samples
Samples in which the units in one group are unrelated to those in the other.
Welch’s t test
A two-sample t test that does not assume equal population variances.
pooled variance
A weighted average of two sample variances used when variances are assumed equal.
Bland–Altman plot
A plot of the difference between two methods against their mean, used to assess agreement.
limits of agreement
Mean difference ± 1.96 SD of differences between two methods.
Q–Q plot
A plot of ordered data against theoretical quantiles, used to check a distributional assumption.
Wilcoxon signed-rank test
A rank-based test for paired data.
Mann–Whitney U test
A rank-based test comparing two independent groups; also called the Wilcoxon rank-sum test.
permutation test
A test that builds the null distribution by repeatedly shuffling group labels.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Blood glucose is measured in 12 patients before and after a diet. Which test is appropriate?

    Show answer

    A paired t test on the differences, or the Wilcoxon signed-rank test if the differences are skewed.

  2. Ten pairs give mean difference 3.0 and SD of differences 4.0. Compute t.

    Show answer

    t = 3.0/(4.0/√10) = 2.37, df = 9.

  3. Why does pairing usually increase power?

    Show answer

    It removes variation between units, so the SD of the differences is smaller than the SD of the raw values.

  4. Group A: n = 20, mean 15.0, SD 3.0. Group B: n = 20, mean 12.0, SD 3.0. Compute the Welch t statistic.

    Show answer

    SE = √(0.45 + 0.45) = 0.949; t = 3.0/0.949 ≈ 3.16.

  5. When does the pooled t test give misleading results?

    Show answer

    When variances differ, especially if the smaller group has the larger variance.

  6. What does a curved pattern in a normal Q–Q plot suggest?

    Show answer

    Skewness; the data are not normal.

  7. A t test on log concentrations gives a 95 percent CI for the difference of 0.10 to 0.45. Interpret it.

    Show answer

    Back-transform: the ratio of geometric means is between e^0.10 = 1.11 and e^0.45 = 1.57.

  8. Pain is scored 0 to 10 by two independent groups. Which test is suitable?

    Show answer

    Mann–Whitney U test, because the outcome is ordinal.

  9. Is the Mann–Whitney test always a test of medians?

    Show answer

    No; only when the two distributions have the same shape.

  10. Describe a permutation test for two groups.

    Show answer

    Compute the observed statistic, shuffle the group labels many times, recompute the statistic each time, and find the proportion of shuffles at least as extreme as observed.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz