Scene 6 · What each framework promises

For after class

Frequentist methods make promises about procedures: if you follow the plan, you will wrongly claim an effect in at most α of the studies where none exists. Bayesian methods make statements about this dataset under this model and prior. Here is what happens when researchers peek at their data every 10 participants per group and stop when they like the result. R/simulate_data.R ran 2,000 studies per scenario (a minute or two); we load the summary.

  • False-positive rate: among studies where the true effect is zero, the share that claim an effect. With p < .05 and one planned analysis, it is 5%.
  • Power: among studies where the effect is real, the share that detect it (here, reach p < .05).
  • Peeking (optional stopping): checking results repeatedly as data come in and stopping once they look good. Each extra look is another chance for noise to cross the threshold.
  • Sequential design: a plan that builds in repeated looks, with thresholds chosen in advance so error rates stay controlled.
peek <- read.csv("data/sim_peeking_summary.csv")
knitr::kable(peek[peek$n_per_group %in% c(20, 50, 100), c("true_delta", "n_per_group", "fixed_p05", "peek_p05",
                                                          "fixed_bf10_gt3", "peek_bf10_gt3", "fixed_bf01_gt3")],
             col.names = c("True δ", "n / group", "p < .05 (one look)", "p < .05 (peeking)",
                           "BF10 > 3 (one look)", "BF10 > 3 (peeking)", "BF01 > 3 (one look)"),
             row.names = FALSE, digits = 3)
True δ n / group p < .05 (one look) p < .05 (peeking) BF10 > 3 (one look) BF10 > 3 (peeking) BF01 > 3 (one look)
0.0 20 0.054 0.084 0.023 0.035 0.347
0.0 50 0.050 0.143 0.015 0.056 0.669
0.0 100 0.053 0.200 0.015 0.075 0.782
0.3 20 0.144 0.192 0.072 0.097 0.232
0.3 50 0.307 0.411 0.170 0.244 0.318
0.3 100 0.539 0.661 0.330 0.458 0.220
Code
h0 <- peek[peek$true_delta == 0, ]
a <- rbind(data.frame(n = h0$n_per_group, rate = h0$fixed_p05, rule = "p < .05, one look"),
           data.frame(n = h0$n_per_group, rate = h0$peek_p05, rule = "p < .05, peeking"),
           data.frame(n = h0$n_per_group, rate = h0$peek_bf10_gt3, rule = "BF10 > 3, peeking"),
           data.frame(n = h0$n_per_group, rate = h0$fixed_bf10_gt3, rule = "BF10 > 3, one look"))
h1 <- peek[peek$true_delta == 0.3, ]
b <- rbind(data.frame(n = h1$n_per_group, rate = h1$fixed_p05, rule = "p < .05 (power)"),
           data.frame(n = h1$n_per_group, rate = h1$fixed_bf10_gt3, rule = "BF10 > 3"),
           data.frame(n = h1$n_per_group, rate = h1$fixed_bf01_gt3, rule = "BF01 > 3 (misleading)"))
cA <- c("p < .05, one look" = pal[["ink2"]], "p < .05, peeking" = pal[["orange"]], "BF10 > 3, peeking" = pal[["blue"]], "BF10 > 3, one look" = pal[["blue"]])
cB <- c("p < .05 (power)" = pal[["ink2"]], "BF10 > 3" = pal[["blue"]], "BF01 > 3 (misleading)" = pal[["orange"]])
lA <- a[a$n == 100, ]; lB <- b[b$n == 100, ]
lA$rate <- lA$rate + ifelse(lA$rule == "BF10 > 3, peeking", 0.012, ifelse(lA$rule == "p < .05, one look", 0.009, ifelse(lA$rule == "BF10 > 3, one look", -0.004, 0)))
pa <- ggplot(a, aes(n, rate, color = rule, linetype = grepl("one look", rule))) +
  geom_hline(yintercept = 0.05, linetype = "dashed", color = pal[["gray"]]) +
  geom_line(linewidth = 1.2) + geom_point(size = 2) +
  scale_linetype_manual(values = c(`TRUE` = "22", `FALSE` = "solid")) +
  geom_text(data = lA, aes(label = rule), hjust = 0, nudge_x = 3, size = 3.8, fontface = "bold") +
  scale_x_continuous(breaks = c(25, 50, 75, 100), limits = c(10, 165)) +
  scale_color_manual(values = cA) + scale_y_continuous(labels = scales::percent, limits = c(0, 0.25)) +
  labs(x = "Participants per group", y = "Studies claiming an effect", title = "No true effect") + theme_demo(14)
pb <- ggplot(b, aes(n, rate, color = rule)) +
  geom_line(linewidth = 1.2) + geom_point(size = 2) +
  geom_text(data = lB, aes(label = rule), hjust = 0, nudge_x = 3, size = 3.8, fontface = "bold") +
  scale_x_continuous(breaks = c(25, 50, 75, 100), limits = c(10, 170)) +
  scale_color_manual(values = cB) + scale_y_continuous(labels = scales::percent, limits = c(0, 0.6)) +
  labs(x = "Participants per group", y = "Share of studies", title = "True δ = 0.30, one look") + theme_demo(14)
pa + pb
Figure 1: Left: when there is no effect, how often each rule claims one (dashed lines: one look at n per group; solid: peeking every 10 per group). Right: when the true effect is small (δ = 0.30), how often a single look at n per group gives each result.
How to read this figure
  1. Left: studies where there is no true effect. The vertical axis is the share that claimed one; the horizontal axis is how many participants per group had been collected.
  2. Dashed lines: one planned analysis at that sample size. Solid lines: peeking every 10 participants per group and stopping at the first “success.”
  3. Right: studies of a real but small effect (δ = 0.30), each analyzed once at the given sample size. Gray is power; blue is the share with at least moderate evidence for the effect; orange is the share with moderate evidence against it, which is misleading here.

Theory-building takeaway. If your field needs claims that are rarely wrong in the long run, frequentist error control is the tool. If it needs graded evidence among specific theories, Bayes factors are. Either way, plan the analysis before seeing the data.