Lecture 30 Do the Standard Tests Work?
This is the R session for week 10. We perform a -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 -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 -value is likely to be less than .
## 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.58Every number in the output is a number we have seen before: the statistic is compared with the distribution, the -value is as in definition 20.8, and the interval is the -interval from example 25.4.
The Bayesian answer. We can model the differences as . To apply proposition 26.6 we pretend that is known and equal to , i.e. we ignore the uncertainty in as in a -test; a prior distribution on is beyond the scope of this module. Here we assume that the prior distribution is . This prior is large enough to be overridden by ten observations. Before we execute the code, predict the posterior probability of : is it larger or smaller than the one-sided -value , output by t.test with alternative = "greater" (see the script R/S30-sleep.R to compute the probability)?
The posterior distribution is , and the probability is , as found in example 28.5. The final line, the one-sided -value of the -test with assumed known, coincides with the posterior probability to two significant figures, as discussed in section 28.3 for the case of nearly flat prior. The -test’s -value is sixty times larger, reflecting the fact that the tail is heavier than the normal tail. This is the cost of not knowing . The middle line of the output answers a question which cannot be answered by the -value: what is the posterior probability that the effect is larger than one hour of sleep? The answer is .
30.2 The Replication Crisis in Miniature
A test at level has a 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 -tests, each on a sample of size from , so that every null hypothesis is true. By exercise 20.4 each -value is uniform on under . Before you run the code, predict how many of the twenty -values will be less than or equal to .
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 . Predict it, then check by simulation over studies.
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 is tested after every ten observations, and the procedure is continued until : 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 .
Ten looks at the same data raise the type I error rate from to : each single test has level , but the procedure formed by choosing when to stop does not, and the reported -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 , so that the family-wise error rate is at most . 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 -values in order , and then finding the largest such that . The smallest -values are then rejected. The p.adjust function can be used to perform either correction, and returns the adjusted -values for comparison with . The following box contains a script for a study with tests, with a true null hypothesis and with a real effect . 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 <- 20m <- 200set.seed(2026)effect <- theta > 0table(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 table from lecture 20. Before you try the reconstructed script, predict the number of false discoveries among the true null hypotheses in the first table, and predict which of the two methods, Bonferroni or Benjamini–Hochberg, will result in more of the real effects being retained.
Without correction there are discoveries, of which 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 of the real effects. The Benjamini–Hochberg method also eliminates all ten false discoveries but manages to keep 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 smallest -values and their rank.

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 , and the last point which is below, the seventeenth, is just before the open circles start at rank .
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 -test for contaminated normal data, i.e. data which consist of with probability and with probability . 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 and has variance . Before you try the code, consider the following questions: at and level , does the size of the -test stay close to ? What happens to the power of the test at ?
rcontam <- function(n, theta, eps = 0.05) { big <- rbinom(n, size = 1, prob = eps) == 1 theta + rnorm(n, sd = ifelse(big, 10, 1))}n <- 20decide <- 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)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 -test is preserved under contamination, and in fact decreases to : a single ‘wild’ observation has the effect of inflating more than would, and thus the test statistic is reduced. The power columns tell a different story: for the -test on clean data rejects of the samples, whereas the -test on contaminated data rejects only of them. In contrast, the Wilcoxon test, which does not lose much power for clean data, still retains . The -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.

The contaminated -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 and and in two sentences state the change in the posterior probability of . Then modify the function peek to test every five observations and estimate the rejection rate for from runs. Compare the estimated rate with . Finally, repeat the stress test with and a contamination probability of and create a table of sizes and powers for the test. Write a short paragraph about whether a larger sample size would improve the -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.
-
•
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 -value and the -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 the -value is uniformly distributed, i.e. twenty independent tests will produce a false discovery with probability . Similarly, repeatedly testing the same growing data set at ten points has a rejection probability of . 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 -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.