Lecture 30 Do the Standard Tests Work?

This is the R session for week 10. We perform a tt-test for a real data set and derive the Bayesian answer for the same question. Then we consider three cases where a correct test can still be misleading: selective reporting of results, repeated testing and when the model assumption is violated. As before, appendix B contains a written reference for the material covered in the session.

30.1 Real Data, Two Answers

The data set sleep, built into R, contains the amount of extra sleep (in hours) for ten patients, when treated with two different drugs. The two measurements for each patient are paired, and thus we can combine the two measurements into one pair difference as shown in the third row of table 29.1 and then perform a one-sample tt-test. The command t.test can be used to perform the test which example 29.2 performed manually. Before you try the command, look at the ten differences and consider whether the mean of these differences is positive, and whether the two-sided pp-value is likely to be less than 0.050.05.

head(sleep, 3)
d <- with(sleep, extra[group == 2] - extra[group == 1])
d
t.test(d)
## extra group ID
## 1 0.7 1 1
## 2 -1.6 1 2
## 3 -0.2 1 3
## [1] 1.2 2.4 1.3 1.3 0.0 1.0 1.8 0.8 4.6 1.4
##
## One Sample t-test
##
## data: d
## t = 4.0621, df = 9, p-value = 0.002833
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## 0.7001142 2.4598858
## sample estimates:
## mean of x
## 1.58

Every number in the output is a number we have seen before: the statistic t=10⋅1.58/1.23=4.06t=\sqrt{10}\cdot 1.58/1.23=4.06 is compared with the t9t_{9} distribution, the pp-value 0.00280.0028 is 2⁢ℙ⁢(T≥4.06)2\,\mathbb{P}(T\geq 4.06) as in definition 20.8, and the interval [0.70,2.46][0.70,2.46] is the tt-interval from example 25.4.

The Bayesian answer. We can model the differences as N⁢(θ,σ2)N(\theta,\sigma^{2}). To apply proposition 26.6 we pretend that σ\sigma is known and equal to s=1.23s=1.23, i.e. we ignore the uncertainty in σ\sigma as in a zz-test; a prior distribution on σ\sigma is beyond the scope of this module. Here we assume that the prior distribution is N⁢(0,102)N(0,10^{2}). This prior is large enough to be overridden by ten observations. Before we execute the code, predict the posterior probability of H0:θ≤0H_{0}\colon\theta\leq 0: is it larger or smaller than the one-sided pp-value 0.00140.0014, output by t.test with alternative = "greater" (see the script R/S30-sleep.R to compute the probability)?

n <- length(d)
dbar <- mean(d)
s <- sd(d)
m <- 0
tau <- 10
prec <- 1 / tau^2 + n / s^2
m_n <- (m / tau^2 + n * dbar / s^2) / prec
tau_n <- sqrt(1 / prec)
c(mean = m_n, sd = tau_n)
pnorm(0, mean = m_n, sd = tau_n)
1 - pnorm(1, mean = m_n, sd = tau_n)
1 - pnorm(sqrt(n) * dbar / s)
## mean sd
## 1.5776132 0.3886648
## [1] 2.46355e-05
## [1] 0.9313799
## [1] 2.431373e-05

The posterior distribution is N⁢(1.578,0.3892)N(1.578,0.389^{2}), and the probability ℙ⁢(θ≤0|d)\mathbb{P}(\theta\leq 0\mskip 1.0mu|\mskip 1.0mud) is 2.5⋅10−52.5\cdot 10^{-5}, as found in example 28.5. The final line, the one-sided pp-value of the zz-test with σ=s\sigma=s assumed known, coincides with the posterior probability ℙ⁢(θ≤0|d)\mathbb{P}(\theta\leq 0\mskip 1.0mu|\mskip 1.0mud) to two significant figures, as discussed in section 28.3 for the case of nearly flat prior. The tt-test’s pp-value is sixty times larger, reflecting the fact that the t9t_{9} tail is heavier than the normal tail. This is the cost of not knowing σ\sigma. The middle line of the output answers a question which cannot be answered by the pp-value: what is the posterior probability that the effect is larger than one hour of sleep? The answer is 0.930.93.

30.2 The Replication Crisis in Miniature

A test at level 0.050.05 has a 5%5\% chance of rejecting a true null hypothesis, which is harmless for one test but not when many tests are run and only the significant ones are reported. We simulate twenty tt-tests, each on a sample of size 2020 from N⁢(0,1)N(0,1), so that every null hypothesis is true. By exercise 20.4 each pp-value is uniform on (0,1)(0,1) under H0H_{0}. Before you run the code, predict how many of the twenty pp-values will be less than or equal to 0.050.05.

n <- 20
m <- 20
set.seed(2026)
p <- replicate(m, t.test(rnorm(n))$p.value)
round(p, 3)
sum(p < 0.05)
## [1] 0.083 0.151 0.194 0.959 0.594 0.601 0.070 0.447 0.656 0.275 0.362 0.820
## [13] 0.352 0.735 0.489 0.308 0.511 0.740 0.845 0.301
## [1] 0

The expected count is one, and this study happened to produce none. The number which matters is the family-wise error rate, the probability that a study of twenty tests reports at least one false discovery. Since the tests are independent, the exact value is 1−0.95201-0.95^{20}. Predict it, then check by simulation over 20002000 studies.

study <- function() any(replicate(m, t.test(rnorm(n))$p.value) < 0.05)
mean(replicate(2000, study()))
1 - 0.95^m
## [1] 0.6315
## [1] 0.6415141

Nearly two studies in three would find a significant result for nothing. This can happen either by testing twenty hypotheses at once, or by testing one hypothesis twenty times. The following function simulates a study where a sample of size 100100 is tested after every ten observations, and the procedure is continued until p<0.05p<0.05: this is the case of the researcher who “collects a few more data points and looks again”. Before you try the function, predict the rejection rate for this procedure under H0H_{0}.

peek <- function() {
x <- rnorm(100)
for (k in seq(10, 100, by = 10)) {
if (t.test(x[1:k])$p.value < 0.05) return(TRUE)
}
FALSE
}
mean(replicate(2000, peek()))
## [1] 0.206

Ten looks at the same data raise the type I error rate from 5%5\% to 21%21\%: each single test has level 0.050.05, but the procedure formed by choosing when to stop does not, and the reported pp-value does not know how it was chosen.

The solution to this problem is to adjust the significance level. The two most common corrections are named here for reference, but I won’t test your knowledge of these corrections in the exam. The Bonferroni correction is very simple: it just rejects p<α/mp<\alpha/m, so that the family-wise error rate is at most α\alpha. This method is very conservative. The Benjamini–Hochberg procedure controls the false discovery rate, i.e. the expected proportion of false rejections, by sorting the pp-values in order p(1)≤⋯≤p(m)p_{(1)}\leq\dots\leq p_{(m)}, and then finding the largest ii such that p(i)≤α⁢i/mp_{(i)}\leq\alpha i/m. The ii smallest pp-values are then rejected. The p.adjust function can be used to perform either correction, and returns the adjusted pp-values for comparison with α\alpha. The following box contains a script for a study with 200200 tests, 180180 with a true null hypothesis and 2020 with a real effect θ=0.8\theta=0.8. The lines are in random order, so you will need to arrange them to form a working script before you can try the code.

table(rejected = p.adjust(p, "BH") < 0.05, effect)
theta <- c(rep(0, 180), rep(0.8, 20))
p <- sapply(theta, function(th) t.test(rnorm(n, mean = th))$p.value)
table(rejected = p < 0.05, effect)
n <- 20
m <- 200
set.seed(2026)
effect <- theta > 0
table(rejected = p.adjust(p, "bonferroni") < 0.05, effect)

As in lectures 12 and 24, all names must be defined before they are used: n, m and theta must come first, effect depends on theta, the seed for the random draw in the sapply line must be set before the line itself, and the three tables can be given in any order. The command table can be used to cross-classify the decision with the truth. This will give the 2×22\times 2 table from lecture 20. Before you try the reconstructed script, predict the number of false discoveries among the 180180 true null hypotheses in the first table, and predict which of the two methods, Bonferroni or Benjamini–Hochberg, will result in more of the 2020 real effects being retained.

## effect
## rejected FALSE TRUE
## FALSE 170 0
## TRUE 10 20
## effect
## rejected FALSE TRUE
## FALSE 180 14
## TRUE 0 6
## effect
## rejected FALSE TRUE
## FALSE 180 3
## TRUE 0 17

Without correction there are 3030 discoveries, of which 1010 are false: one in three of the reported findings is wrong, despite all tests being performed correctly. The Bonferroni method eliminates all ten false discoveries, but also 1414 of the 2020 real effects. The Benjamini–Hochberg method also eliminates all ten false discoveries but manages to keep 1717 of the real effects: the guarantee is weaker, but the method finds many more results. Figure 30.1, generated by the script R/S30-forking.R, shows the 4040 smallest pp-values and their rank.

Sorted p-values on a logarithmic axis against rank
1 to 40. Filled circles, the real effects, fill the lowest ranks;
open circles, the true nulls, start at rank 19. Six points lie
below the dotted Bonferroni line, the first 17 below the rising
Benjamini-Hochberg curve and 30 below the dashed line at 0.05.
Figure 30.1: The 4040 smallest of 200200 pp-values against their rank on a logarithmic scale: real effects (filled circles) and true null hypotheses (open circles), with the raw level 0.050.05 (dashed), the Bonferroni level 0.05/2000.05/200 (dotted) and the Benjamini–Hochberg line 0.05⁢i/2000.05\,i/200 (solid).

In the figure only six of the real effects are below the Bonferroni line, which does not move with the rank; the Benjamini–Hochberg line rises with ii, and the last point which is below, the seventeenth, is just before the open circles start at rank 1919.

30.3 A Stress Test

Every test in table 29.1 assumes that the data are normally distributed. In lecture 12 we have seen that for distributions with heavy tails the central limit theorem takes effect only slowly, and section 17.5 lists further situations in which large-sample approximations are unreliable. We now test the tt-test for contaminated normal data, i.e. data which consist of N⁢(θ,1)N(\theta,1) with probability 0.950.95 and N⁢(θ,100)N(\theta,100) with probability 0.050.05. This could, for example, happen if one in twenty of the measurements is made using a faulty instrument. The resulting distribution is still symmetric around θ\theta and has variance 5.955.95. Before you try the code, consider the following questions: at n=20n=20 and level 0.050.05, does the size of the tt-test stay close to 0.050.05? What happens to the power of the test at θ=0.5\theta=0.5?

rcontam <- function(n, theta, eps = 0.05) {
big <- rbinom(n, size = 1, prob = eps) == 1
theta + rnorm(n, sd = ifelse(big, 10, 1))
}
n <- 20
decide <- function(theta, contaminated) {
x <- if (contaminated) rcontam(n, theta) else rnorm(n, mean = theta)
c(t = t.test(x)$p.value < 0.05, wilcoxon = wilcox.test(x)$p.value < 0.05)
}
set.seed(2026)
rates <- sapply(c(0, 0.5, 1), function(theta) {
c(clean = rowMeans(replicate(5000, decide(theta, FALSE))),
contaminated = rowMeans(replicate(5000, decide(theta, TRUE))))
})
colnames(rates) <- c("theta = 0", "theta = 0.5", "theta = 1")
round(rates, 3)
## theta = 0 theta = 0.5 theta = 1
## clean.t 0.051 0.561 0.989
## clean.wilcoxon 0.049 0.536 0.987
## contaminated.t 0.033 0.330 0.698
## contaminated.wilcoxon 0.048 0.460 0.940

The function decide also performs the Wilcoxon signed-rank test, which uses only signs and ranks of the data and assumes symmetry instead of normality. This test is not part of the module under consideration here, but is included for comparison. The first column of the table shows that the size of the tt-test is preserved under contamination, and in fact decreases to 0.0330.033: a single ‘wild’ observation has the effect of inflating SS more than X¯\bar{X} would, and thus the test statistic is reduced. The power columns tell a different story: for θ=1\theta=1 the tt-test on clean data rejects 99%99\% of the samples, whereas the tt-test on contaminated data rejects only 70%70\% of them. In contrast, the Wilcoxon test, which does not lose much power for clean data, still retains 94%94\%. The tt-test is not lying about the null hypothesis, but is failing to detect the effects present in the data. Figure 30.2, generated by the script R/S30-stress.R, shows the three power curves as a function of the grid of alternatives.

Rejection rate as a function of theta, ranging
from 0 to 1.5. The filled circles start at 0.05 and increase
quickly to 1 as theta increases to 1. The crosses are slightly
below the circles. The open circles start at 0.03 and increase
slowly to 0.7 as theta increases to 1. The dashed horizontal line
marks 0.05.
Figure 30.2: Estimated power functions at level 0.050.05 for 20002000 samples of size n=20n=20, for the tt-test on clean, normally distributed data (filled circles), the tt-test on contaminated data (open circles), and the Wilcoxon signed-rank test on contaminated data (crosses).

The contaminated tt-test has the correct size but the wrong power curve; if the contamination was asymmetric, the size would change as well. The moral of the story, as in lecture 24, is that when we are not sure about the model, we should simulate using a plausible alternative model, and should consider both columns of the table.

30.4 A Task to Take Home

Seed the generator with your student ID. First repeat the Bayesian analysis of the sleep data with the priors N⁢(0,1)N(0,1) and N⁢(2,0.52)N(2,0.5^{2}) and in two sentences state the change in the posterior probability of θ≤0\theta\leq 0. Then modify the function peek to test every five observations and estimate the rejection rate for H0H_{0} from 20002000 runs. Compare the estimated rate with 0.2060.206. Finally, repeat the stress test with n=50n=50 and a contamination probability of 0.10.1 and create a table of sizes and powers for the test. Write a short paragraph about whether a larger sample size would improve the tt-test in this example.

Before the next session, you must first understand the commands used in this session. Type in the commands yourself, and make sure that you get the same results as shown in the text.

Summary.
  • •
    ​

    The written reference for the commands used in this session is appendix B.

  • •
    ​

    The command t.test can be used to get the test statistic, the pp-value and the tt-interval for the data from lectures 25 and 29. The Bayesian answer from proposition 26.6, using a weak prior, gives the same result. In addition, the Bayesian answer can be used to get the probability of an effect of a given size.

  • •
    ​

    Under H0H_{0} the pp-value is uniformly distributed, i.e. twenty independent tests will produce a false discovery with probability 0.640.64. Similarly, repeatedly testing the same growing data set at ten points has a rejection probability of 0.210.21. The Bonferroni method controls the family-wise error rate by reducing the significance level of each test, but this method will miss most of the real effects. The Benjamini–Hochberg method controls the false discovery rate and thus preserves most of the real effects. Both methods can be implemented using the function p.adjust.

  • •
    ​

    For contaminated normal data the tt-test maintains its size but loses a lot of power. A test which is not appropriate for the given data can fail in either of the two columns of the test output. Simulation under the alternative can be used to determine which of the two columns is more strongly affected.