Rank-based tests
A brief review:
Note
✅ Key idea from last lecture
A t-test’s p-value is only trustworthy if its assumptions hold. Today is about how to tell whether they do — and what to do when they don’t.

Warning
⚠️ The trap we’re walking into first
It is tempting to treat non-parametric tests as the “safe” option you can always fall back on. We are going to start by showing why that instinct is wrong.
Today runs in two halves, and the order matters:
Note
✅ The question behind today
Not “which test is safest?” but “which test does this data deserve?”


# A tibble: 4 × 3
team shady sunny
<chr> <dbl> <dbl>
1 botany 16.1 17.3
2 grace_jace 15.4 15.5
3 one 13.2 14.2
4 waterbugs 11.7 13.8
📥 Pine needle data 📥 Lake trout data 📥 Sagebrush cricket data
Note
✅ Reminder
4 teams, 4 trees, both sides of each tree. The data are paired by design.
Last week these four trees gave us:
| Comparison | p |
|---|---|
| Unpaired two-sample t | 0.425 |
| Paired t | 0.072 |
Note
✅ Same two tools as last week
source() gives you theme_regular() and summary_stats() again. Nothing new to learn.
Note
🔮 Predict first: Before we run anything — do you expect needle length to be normally distributed with equal variance on the two sides of a tree? Commit to an answer.
Before reaching for anything non-parametric, we have to establish that we need it. Same two checks as last week.
Shapiro-Wilk normality test
data: pine_df$needle_length_mm[pine_df$side == "sunny"]
W = 0.96105, p-value = 0.2934
Shapiro-Wilk normality test
data: pine_df$needle_length_mm[pine_df$side == "shady"]
W = 0.98213, p-value = 0.8586
Important
⚠️ Everything passes
| Check | p |
|---|---|
| Shapiro-Wilk, sunny | 0.29 |
| Shapiro-Wilk, shady | 0.86 |
| Levene’s | 0.99 |
Remember the direction of these tests: H₀ is that the assumption holds. A large p means we have no evidence against it.
There is nothing wrong with this data. The paired t-test we ran last week was entitled to every bit of its p = 0.072.
Tip
🖐 So why are we here?
Because “my data are fine” is a conclusion you have to earn, not assume — and because plenty of students would have reached for a rank test anyway, thinking it was the cautious choice. Let’s find out what that caution would have cost.
Our paired t-test gave p = 0.072. The usual reflex is to call that “not significant” and stop.
But p never told us the one thing a reader actually wants to know: how much shorter are shady needles? A p-value answers “could this be noise?” — not “does this matter?”
Cohen’s d answers the second question. It is the difference between the groups expressed in standard deviations, so it has no units and can be compared across studies:
\[d = \frac{\bar{x}_1 - \bar{x}_2}{s}\]
For a paired design the difference is the paired difference, and s is the standard deviation of those differences:
Note
✅ Read the formula out loud
“The shady side averaged 1.36 standard deviations below the sunny side.”
That sentence stands on its own. It does not depend on how many trees we sampled, and it would mean the same thing to someone measuring a different pine species in a different forest.
Tip
🖐 Cohen’s rough conventions
| |d| | Label |
|---|---|
| 0.2 | small |
| 0.5 | medium |
| 0.8 | large |
Ours is 1.36. These are rules of thumb, not laws — what counts as “large” is a question about pine trees, not about statistics.
Here is the relationship that explains everything:
\[t = d \times \sqrt{n}\]
The t-statistic is the effect size multiplied by the square root of your sample size. So a p-value confounds two completely different things: how big the effect is, and how hard you looked.
Effect size separates them. That is why journals, and every reporting guideline written in the last twenty years, ask for it alongside the p-value.
Cohen's d | 95% CI
-------------------------
-1.36 | [-2.74, 0.10]
Important
⚠️ Look at that confidence interval: [-2.74, 0.10]
This is the honest summary of our four trees: the effect is probably large and probably negative, but with n = 4 we cannot rule out zero.
Notice that the CI crossing zero is the same fact as p > 0.05 — but it says it far more usefully. “No significant difference” sounds like we found nothing. “d = -1.36, CI [-2.74, 0.10]” says we found a large effect we could not pin down, which is a completely different message to a reader, and the correct one.
Tip
🖐 Where this goes next
Effect size is the input to a power analysis — given d, how many trees would we have needed? That is next week’s lecture.
Every non-parametric test today works the same way: throw away the measurements, keep only their order.
# A tibble: 8 × 4
team side needle_length_mm rank
<chr> <chr> <dbl> <dbl>
1 waterbugs shady 11.7 1
2 one shady 13.2 2
3 waterbugs sunny 13.8 3
4 one sunny 14.2 4
5 grace_jace shady 15.4 5
6 grace_jace sunny 15.5 6
7 botany shady 16.1 7
8 botany sunny 17.3 8
Note
✅ What just happened
The shortest tree-side average became rank 1, the longest rank 8. A needle that was 4 mm longer than its neighbour and one that was 0.1 mm longer are now exactly one rank apart.
That is the whole trade. You become immune to outliers and to any weird distribution — and in exchange you give up the magnitudes.
A rank-based test then asks: are the ranks of one group systematically higher than the other’s?
Tip
🖐 Watch for the statistic
Rank tests report W (Mann-Whitney) or V (signed-rank), never t. If you see a W, you are reading rank-based output.
Wilcoxon rank sum exact test
data: needle_length_mm by side
W = 5, p-value = 0.4857
alternative hypothesis: true location shift is not equal to 0
Tip
🖐 Notice V = 0
The signed-rank statistic is zero — meaning every single tree moved in the same direction. There is no arrangement of these four trees that would look more consistent.
Notice too that R says “exact test” for both. With no tied values it enumerates every possible arrangement rather than approximating. Hold on to that word — the next slide explains why it matters.
| Comparison | Parametric | Non-parametric |
|---|---|---|
| Unpaired (2 groups) | t-test p = 0.425 | Mann-Whitney p = 0.486 |
| Paired (4 trees) | paired t p = 0.072 | signed-rank p = 0.125 |
Both methods tell the same story:
But the rank test costs you. In both rows the non-parametric p-value is larger. That is the price of throwing away the actual measurements and keeping only their order — and we paid it on data that never needed it.
Important
⚠️ At n = 4, the signed-rank test hits a wall
With 4 pairs there are only 2⁴ = 16 possible arrangements of plus and minus signs. The most extreme possible result — all four the same way, which is exactly what we got — has a two-sided p of 2/16 = 0.125.
So with four trees, a Wilcoxon signed-rank test can never return a p-value below 0.125, no matter how enormous the effect.
The test did not fail to find our effect. It was arithmetically incapable of declaring it significant.
Note
✅ Key idea
Choose the test that matches your data’s distribution, not the one that feels safest. For our four trees, the parametric paired t-test — whose assumptions we just checked and could not reject — is the more honest and more informative choice.
Warning
⚠️ Where we go next
We have now seen what these tests cost. What we have not seen is what they buy you — because nothing about the pine data was broken.
For that we need a dataset that is a genuine mess.
# A tibble: 6 × 7
lake n mean sd se ci_lower ci_upper
<chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 I8 91 857. 727. 76.2 705. 1008.
2 Island Lake 10 3165 1617. 511. 2008. 4322.
3 N 01 674 659. 395. 15.2 629. 689.
4 NE 12 322 534. 520. 29.0 477. 591.
5 NE 14 139 848. 508. 43.1 763. 933.
6 Toolik 249 518. 373. 23.6 471. 564.
Tip
🖐 Notice the design
Six lakes of trout: sampling_site, species, length_mm, mass_g, lake.
Everything from here on uses mass_g. Look hard at the n and sd columns for NE 12 and Island Lake — 322 fish against 10, and very different spreads.
That imbalance is the rest of the lecture. Where the pine data was clean, this is everything that can go wrong at once.
T-tests are parametric tests.
Note
📖 Reference
Whitlock & Schluter, Analysis of Biological Data, Ch. 13 — Handling Violations of Assumptions, is the core reference for this whole lecture.


Basic assumptions of parametric t-tests: random sampling, normality, equal variance, no outliers.
Random sampling:

Note
🔮 Predict first: We’re about to look at histogram, dotplot, boxplot, and QQ-plot views of the same NE 12 mass data. Which of the four do you expect to be most convincing about normality?
Let’s test normality for one lake — NE 12 — as if we were going to run a one-sample t-test. We need a new data frame with only NE 12 data, called ne12_data.
Normality: samples from a normally distributed population

H₀ is that the data ARE normally distributed — so a small p-value rejects normality.
Shapiro-Wilk normality test
data: ne12_data$mass_g
W = 0.85148, p-value < 2.2e-16
Warning
⚠️ p prints as < 2.2e-16
That is R telling you the p-value is smaller than it will bother to print. Not a borderline call — NE 12 mass is strongly non-normal, exactly as the histogram and QQ-plot showed. This is the case the rest of the lecture is for.
Equal variance: samples are from populations with a similar degree of variability.
Equal variance is about two groups, so here we bring in the comparison we will use for the rest of the lecture: NE 12 vs. Island Lake.

Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 1 25.997 5.775e-07 ***
330
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
p is far below 0.05 — the variances are not equal.
No outliers: no “extreme” values very different from the rest of the sample.

Warning
⚠️ Watch out!
Outliers are a problem for non-parametric tests as well — switching to a rank-based test doesn’t make an outlier stop mattering.
What if t-test assumptions fail? Alternative tests, with more relaxed assumptions, are available. In which case would you use each?

QQ-plots: a tool for assessing normality.

In some cases, data can be mathematically “transformed” to meet the assumptions of parametric tests. This usually involves:

The log scale pulls the two spreads much closer together — but it does not fix them completely. Levene’s test on log10(mass_g) is still significant.

Log isn’t a trick you try at random — it fixes a specific shape of problem:
log10(x)log10(x)Note
✅ The mechanism, in one line
A trout that is twice as heavy as another isn’t “500 g more” — it’s “2×.” Log turns that multiplicative relationship into an additive one, which is what a t-test’s equal-spread assumption expects. It compresses the right tail: 100 g → 1000 g and 1000 g → 10,000 g become the same distance, one log unit.
Log is the one you will reach for most in ecology, but match the transform to the shape of the problem:
| Transformation | Use when… |
|---|---|
log10(x) |
right-skewed, multiplicative, ratio-scale data (mass, concentration, abundance) |
sqrt(x) |
count data, moderate right skew (Poisson-like variance) |
asin(sqrt(x)) |
proportions or percentages bounded 0–1 |
1/x |
rates, or extreme right skew that log doesn’t fix |
| Box-Cox | no obvious transform works — let the data pick the power |
None of these are guaranteed to work. You check afterward, every time — that’s Part 5 of the worksheet, and it’s the whole point of the take-home.
Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 1 11.77 0.0006784 ***
330
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Compare that to the Levene’s result on raw mass_g a few slides back — both fail. Normality improved with the log transform. Equal variance did not. Checking a transformation is not one pass/fail box — you re-test every assumption, separately, on the transformed variable.
Important
⚠️ This is the take-home’s whole trap
The sagebrush cricket data works the same way: a transformation can help one assumption without rescuing the other. There is no single “correct” test hiding in the data — there’s a best-justified one. Check every assumption on the raw data, then again on any transformed version, before you decide.
You met Welch’s last week: same t-statistic idea, separate variance estimates, fractional degrees of freedom. Nothing new to learn. What is new is this dataset, which shows you why it matters.
Two Sample t-test
data: mass_g by lake
t = 14.181, df = 330, p-value < 2.2e-16
alternative hypothesis: true difference in means between group Island Lake and group NE 12 is not equal to 0
95 percent confidence interval:
2266.304 2996.360
sample estimates:
mean in group Island Lake mean in group NE 12
3165.0000 533.6677
Welch Two Sample t-test
data: mass_g by lake
t = 5.1368, df = 9.0578, p-value = 0.0006016
alternative hypothesis: true difference in means between group Island Lake and group NE 12 is not equal to 0
95 percent confidence interval:
1473.676 3788.989
sample estimates:
mean in group Island Lake mean in group NE 12
3165.0000 533.6677
Important
⚠️ Same data. Same means. Sixty orders of magnitude apart.
| t | df | p | |
|---|---|---|---|
| Student’s | 14.18 | 330 | 5.7 × 10⁻³⁶ |
| Welch’s | 5.14 | 9.06 | 0.0006 |
(R only prints < 2.2e-16 for the first one — that is the smallest number it will show you. The true value is 5.7 × 10⁻³⁶.)
Look at the df. Student’s pooled 322 Island-sized fish with 10 NE 12-sized ones and claimed 330 degrees of freedom. Welch’s looked at the same data and said: you really only have about 9.
Both still reject H₀ — but Student’s was never entitled to that much confidence. With n = 322 vs 10 and Levene’s p ≈ 6 × 10⁻⁷, the pooled variance is meaningless.
Tip
🖐 The rule
Unequal n and unequal variance is exactly the combination Welch’s was built for. When in doubt, var.equal = FALSE is the safer default — it costs you almost nothing when variances really are equal.
Same two tests you ran on the pine needles in Part 1 — ranks instead of measurements, no assumption about the distribution.
Note
✅ The difference from Part 1
On the pine needles the rank test was a cost — we gave up power on data that was already well behaved.
Here, NE 12 mass is non-normal at p < 2.2 × 10⁻¹⁶ and full of outliers. There is nothing to give up. This is the case the test was built for.
Note
📖 Reference
Whitlock & Schluter, Ch. 13, covers the Mann-Whitney U test and Wilcoxon signed-rank test as the standard rank-based alternatives.
Tip
🖐 W again, and no “exact” this time
The statistic is W, not t — rank-based output, as in Part 1.
But notice what R doesn’t say: with 332 fish and plenty of tied masses it cannot enumerate every arrangement, so this is the normal approximation, not an exact test. At this sample size that is fine — the floor problem that crippled our four trees does not exist here.
perm package
Tip
🖐 For a graphical explanation
In R, using the perm package. Assumptions: both groups have a similar distribution; equal variance.
library(perm)
# Balance the design: take 10 NE 12 fish, keep all of Island Lake
set.seed(123)
perm_df <- bind_rows(
isl_ne12_df %>%
filter(lake == "NE 12") %>%
slice_sample(n = 10),
isl_ne12_df %>%
filter(lake == "Island Lake")
)
# The difference the test is about
perm_df %>% group_by(lake) %>% reframe(summary_stats(mass_g))# A tibble: 2 × 7
lake n mean sd se ci_lower ci_upper
<chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Island Lake 10 3165 1617. 511. 2008. 4322.
2 NE 12 10 645. 538. 170. 260. 1030.
Exact Permutation Test Estimated by Monte Carlo
data: mass_g by lake
p-value = 2e-04
alternative hypothesis: true mean lake=Island Lake - mean lake=NE 12 is not equal to 0
sample estimates:
mean lake=Island Lake - mean lake=NE 12
2519.9
p-value estimated from 10000 Monte Carlo replications
99 percent confidence interval on p-value:
0.000000000 0.001059383
Key assumptions:
Note
Assessing assumptions
Always report an effect size next to your p-value.
Note
✅ On our four trees
Paired d = −1.36, CI [−2.74, 0.10], p = 0.072. A large effect, imprecisely measured. Reporting only “p = 0.072, n.s.” would have thrown that away.
Tip
🖐 In a results sentence
Needles on the shady side were 1.09 mm shorter than on the sunny side (paired t₃ = −2.72, p = 0.072, d = −1.36, 95% CI [−2.74, 0.10]).
Every number there does work the others cannot.
When assumptions aren’t met, transformations may help normalize data:
log10(x) — useful for right-skewed data, multiplicative effectssqrt(x) — useful for count data, moderately right-skewed distributionsTip
🖐 Recall
We saw log10() shrink the gap between the NE 12 and Island Lake boxplots earlier — that’s the transformation working.
1. Standard t-test
Strengths:
Weaknesses:
2. Welch’s t-test
Covered in full in the T-Tests lecture; the one-slide refresher in Part 5 is all we needed today.
3. Mann-Whitney-Wilcoxon test
Strengths:
Weaknesses:
4. Permutation tests
Strengths:
Weaknesses:
Statistical tests have different strengths and assumptions. The choice should be guided by your data’s characteristics, not just convenience.
Always visualize your data before deciding on the appropriate test.
Note
✅ Key idea
Standard t-test → Welch’s → Mann-Whitney/Permutation isn’t a strict ladder of “better” tests — each answers a slightly different question (means vs. medians) under different assumptions. Pick based on what your data actually looks like.
Part 8 of the worksheet (sagebrush crickets, due before Study Design & Sampling) hands you a new dataset and asks you to run the whole decision process from today:
There is no lecture slide that tells you the answer this time.
Tip
🖐 Remember from today
log10() fixed normality for the lake trout but not equal variance. Expect the crickets to behave similarly — a transformation earning partial credit is a completely normal outcome, not a sign you did something wrong.