Posterior predictive checking of normal model for light data

ggplot2 and bayesplot are used for plotting tidyr is used for manipulating data frames posterior is used to handle posterior draws

library(ggplot2)
library(bayesplot)
theme_set(bayesplot::theme_default(base_family = "sans"))
library(posterior)
library(tidyr)
library(latex2exp)
library(rprojroot)
root<-has_file(".BDA_R_demos_root")$make_fix_file()

Data

y <- read.table(root("demos_ch6","light.txt"))$V1

Sufficient statistics

n <- length(y)
s <- sd(y)
my <- mean(y)

Create 9 random replicate data sets from the posterior predictive density. Each set has same number of virtual observations as the original data set.

yrep <- replicate(9, rt(n, n-1)*sqrt(1+1/n)*s+my)

Replace one of the replicates with observed data. If you can spot which one has been replaced, it means that the replicates do not resemble the original data and thus the model has a defect

ind <- sample(9, 1)
yrep_df <- yrep %>%
  as.data.frame() %>%
  replace(ind, y) %>% # replace one of the yreps
  setNames(1:9) %>%   # rename to hide which one is the real data
  pivot_longer(everything()) # use the long format for plotting

ggplot(data = yrep_df) +
  geom_histogram(aes(x = value), fill = 'steelblue',
                 color = 'black', binwidth = 4) +
  facet_wrap(~name, nrow = 3) +
  coord_cartesian(xlim = c(-55, 65)) +
  labs(x = '', y = '') +
  scale_y_continuous(breaks=NULL) +
  theme(strip.background = element_blank())

Generate 1000 replicate data sets and compute test statistic. The test statistic here is the smallest observation in the data or in the replicated data.

yrep1000 <- replicate(1000, rt(n, n-1)*sqrt(1+1/n)*s+my) %>%
  as.data.frame()
# the minimum value over 1000 replicates
minvals <- data.frame(x = sapply(yrep1000, min))

Plot test statistic for the data and the replicated data sets

title1 <- 'Smallest observation in the replicated
data (hist.) vs in the original data (vertical line)'
ggplot(data = minvals) +
  geom_histogram(aes(x = x), fill = 'steelblue',
                 color = 'black', binwidth = 2) +
  geom_vline(aes(xintercept = min(x)), data = data.frame(x = y),
             color = 'red') +
  coord_cartesian(xlim = c(-50, 20)) +
  labs(x = TeX('Minimum of \\textit{y} and \\textit{y}$^{rep}$'),
       y = '', title = title1) +
  scale_y_continuous(breaks=NULL)

Posterior predictive checks provided by bayesplot
https://mc-stan.org/bayesplot/reference/PPC-distributions.html

For convenience switch to use draws object

rownames(yrep1000) <- paste0("yrep[", 1:66, "]")
yrep_draws <- as_draws_matrix(t(yrep1000))

Histogram of y + 8 yrep histograms

ppc_hist(y, yrep_draws[1:8,])
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Kernel density estimate of y + 100 yrep kernel density estimates

ppc_dens_overlay(y, yrep_draws[1:100,])

ECDF of y + 100 yrep ECDFs

ppc_ecdf_overlay(y, yrep_draws[1:100,])

Scatterplot of yrep vs y

ppc_scatter(y, yrep_draws[1:4,])+geom_abline()

The distribution of a (test) statistic T(yrep) compared to the observed value T(y) computed from the data y. The default test statistic mean is a bad statistic as the model has a parameter for the mean.

ppc_stat(y, yrep_draws)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

The distribution of a (test) statistic T(yrep) compared to the observed value T(y) computed from the data y. Min and max are often good test statistics for continuous outcomes.

ppc_stat(y, yrep_draws, stat="min")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ppc_stat(y, yrep_draws, stat="max")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Show 2 test statistics in one plot

color_scheme_set("brewer-Paired")
ppc_stat_2d(y, yrep_draws, stat=c("min","max"))

color_scheme_set()
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vIDYuMSIKYXV0aG9yOiAiQWtpIFZlaHRhcmksIE1hcmt1cyBQYWFzaW5pZW1pIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCiMjIFBvc3RlcmlvciBwcmVkaWN0aXZlIGNoZWNraW5nIG9mIG5vcm1hbCBtb2RlbCBmb3IgbGlnaHQgZGF0YQoKZ2dwbG90MiBhbmQgYmF5ZXNwbG90IGFyZSB1c2VkIGZvciBwbG90dGluZwp0aWR5ciBpcyB1c2VkIGZvciBtYW5pcHVsYXRpbmcgZGF0YSBmcmFtZXMKcG9zdGVyaW9yIGlzIHVzZWQgdG8gaGFuZGxlIHBvc3RlcmlvciBkcmF3cwoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoYmF5ZXNwbG90KQp0aGVtZV9zZXQoYmF5ZXNwbG90Ojp0aGVtZV9kZWZhdWx0KGJhc2VfZmFtaWx5ID0gInNhbnMiKSkKbGlicmFyeShwb3N0ZXJpb3IpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkobGF0ZXgyZXhwKQpsaWJyYXJ5KHJwcm9qcm9vdCkKcm9vdDwtaGFzX2ZpbGUoIi5CREFfUl9kZW1vc19yb290IikkbWFrZV9maXhfZmlsZSgpCmBgYAoKRGF0YQoKYGBge3IgfQp5IDwtIHJlYWQudGFibGUocm9vdCgiZGVtb3NfY2g2IiwibGlnaHQudHh0IikpJFYxCmBgYAoKU3VmZmljaWVudCBzdGF0aXN0aWNzCgpgYGB7ciB9Cm4gPC0gbGVuZ3RoKHkpCnMgPC0gc2QoeSkKbXkgPC0gbWVhbih5KQpgYGAKCkNyZWF0ZSA5IHJhbmRvbSByZXBsaWNhdGUgZGF0YSBzZXRzIGZyb20gdGhlIHBvc3RlcmlvcgpwcmVkaWN0aXZlIGRlbnNpdHkuCkVhY2ggc2V0IGhhcyBzYW1lIG51bWJlciBvZiB2aXJ0dWFsIG9ic2VydmF0aW9ucyBhcyB0aGUKb3JpZ2luYWwgZGF0YSBzZXQuCgpgYGB7ciB9CnlyZXAgPC0gcmVwbGljYXRlKDksIHJ0KG4sIG4tMSkqc3FydCgxKzEvbikqcytteSkKYGBgCgpSZXBsYWNlIG9uZSBvZiB0aGUgcmVwbGljYXRlcyB3aXRoIG9ic2VydmVkIGRhdGEuCklmIHlvdSBjYW4gc3BvdCB3aGljaCBvbmUgaGFzIGJlZW4gcmVwbGFjZWQsIGl0IG1lYW5zCnRoYXQgdGhlIHJlcGxpY2F0ZXMgZG8gbm90IHJlc2VtYmxlIHRoZSBvcmlnaW5hbCBkYXRhCmFuZCB0aHVzIHRoZSBtb2RlbCBoYXMgYSBkZWZlY3QKCmBgYHtyIH0KaW5kIDwtIHNhbXBsZSg5LCAxKQp5cmVwX2RmIDwtIHlyZXAgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJlcGxhY2UoaW5kLCB5KSAlPiUgIyByZXBsYWNlIG9uZSBvZiB0aGUgeXJlcHMKICBzZXROYW1lcygxOjkpICU+JSAgICMgcmVuYW1lIHRvIGhpZGUgd2hpY2ggb25lIGlzIHRoZSByZWFsIGRhdGEKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpKSAjIHVzZSB0aGUgbG9uZyBmb3JtYXQgZm9yIHBsb3R0aW5nCgpnZ3Bsb3QoZGF0YSA9IHlyZXBfZGYpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHZhbHVlKSwgZmlsbCA9ICdzdGVlbGJsdWUnLAogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJywgYmlud2lkdGggPSA0KSArCiAgZmFjZXRfd3JhcCh+bmFtZSwgbnJvdyA9IDMpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTU1LCA2NSkpICsKICBsYWJzKHggPSAnJywgeSA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1OVUxMKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpHZW5lcmF0ZSAxMDAwIHJlcGxpY2F0ZSBkYXRhIHNldHMgYW5kIGNvbXB1dGUgdGVzdCBzdGF0aXN0aWMuIFRoZQp0ZXN0IHN0YXRpc3RpYyBoZXJlIGlzIHRoZSBzbWFsbGVzdCBvYnNlcnZhdGlvbiBpbiB0aGUgZGF0YSBvciBpbgp0aGUgcmVwbGljYXRlZCBkYXRhLgoKYGBge3IgfQp5cmVwMTAwMCA8LSByZXBsaWNhdGUoMTAwMCwgcnQobiwgbi0xKSpzcXJ0KDErMS9uKSpzK215KSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKIyB0aGUgbWluaW11bSB2YWx1ZSBvdmVyIDEwMDAgcmVwbGljYXRlcwptaW52YWxzIDwtIGRhdGEuZnJhbWUoeCA9IHNhcHBseSh5cmVwMTAwMCwgbWluKSkKYGBgCgpQbG90IHRlc3Qgc3RhdGlzdGljIGZvciB0aGUgZGF0YSBhbmQgdGhlIHJlcGxpY2F0ZWQgZGF0YSBzZXRzCgpgYGB7ciB9CnRpdGxlMSA8LSAnU21hbGxlc3Qgb2JzZXJ2YXRpb24gaW4gdGhlIHJlcGxpY2F0ZWQKZGF0YSAoaGlzdC4pIHZzIGluIHRoZSBvcmlnaW5hbCBkYXRhICh2ZXJ0aWNhbCBsaW5lKScKZ2dwbG90KGRhdGEgPSBtaW52YWxzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSB4KSwgZmlsbCA9ICdzdGVlbGJsdWUnLAogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJywgYmlud2lkdGggPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1pbih4KSksIGRhdGEgPSBkYXRhLmZyYW1lKHggPSB5KSwKICAgICAgICAgICAgIGNvbG9yID0gJ3JlZCcpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUwLCAyMCkpICsKICBsYWJzKHggPSBUZVgoJ01pbmltdW0gb2YgXFx0ZXh0aXR7eX0gYW5kIFxcdGV4dGl0e3l9JF57cmVwfSQnKSwKICAgICAgIHkgPSAnJywgdGl0bGUgPSB0aXRsZTEpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPU5VTEwpCmBgYAoKUG9zdGVyaW9yIHByZWRpY3RpdmUgY2hlY2tzIHByb3ZpZGVkIGJ5IGJheWVzcGxvdDxicj4KaHR0cHM6Ly9tYy1zdGFuLm9yZy9iYXllc3Bsb3QvcmVmZXJlbmNlL1BQQy1kaXN0cmlidXRpb25zLmh0bWwKCkZvciBjb252ZW5pZW5jZSBzd2l0Y2ggdG8gdXNlIGRyYXdzIG9iamVjdAoKYGBge3IgfQpyb3duYW1lcyh5cmVwMTAwMCkgPC0gcGFzdGUwKCJ5cmVwWyIsIDE6NjYsICJdIikKeXJlcF9kcmF3cyA8LSBhc19kcmF3c19tYXRyaXgodCh5cmVwMTAwMCkpCmBgYAoKSGlzdG9ncmFtIG9mIHkgKyA4IHlyZXAgaGlzdG9ncmFtcwoKYGBge3IgfQpwcGNfaGlzdCh5LCB5cmVwX2RyYXdzWzE6OCxdKQpgYGAKCktlcm5lbCBkZW5zaXR5IGVzdGltYXRlIG9mIHkgKyAxMDAgeXJlcCBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMKCmBgYHtyIH0KcHBjX2RlbnNfb3ZlcmxheSh5LCB5cmVwX2RyYXdzWzE6MTAwLF0pCmBgYAoKRUNERiBvZiB5ICsgMTAwIHlyZXAgRUNERnMKCmBgYHtyIH0KcHBjX2VjZGZfb3ZlcmxheSh5LCB5cmVwX2RyYXdzWzE6MTAwLF0pCmBgYAoKU2NhdHRlcnBsb3Qgb2YgeXJlcCB2cyB5CgpgYGB7ciB9CnBwY19zY2F0dGVyKHksIHlyZXBfZHJhd3NbMTo0LF0pK2dlb21fYWJsaW5lKCkKYGBgCgpUaGUgZGlzdHJpYnV0aW9uIG9mIGEgKHRlc3QpIHN0YXRpc3RpYyBUKHlyZXApIGNvbXBhcmVkIHRvIHRoZQpvYnNlcnZlZCB2YWx1ZSBUKHkpIGNvbXB1dGVkIGZyb20gdGhlIGRhdGEgeS4gVGhlIGRlZmF1bHQgdGVzdApzdGF0aXN0aWMgbWVhbiBpcyBhIGJhZCBzdGF0aXN0aWMgYXMgdGhlIG1vZGVsIGhhcyBhIHBhcmFtZXRlciBmb3IKdGhlIG1lYW4uCgpgYGB7ciB9CnBwY19zdGF0KHksIHlyZXBfZHJhd3MpCmBgYAoKVGhlIGRpc3RyaWJ1dGlvbiBvZiBhICh0ZXN0KSBzdGF0aXN0aWMgVCh5cmVwKSBjb21wYXJlZCB0byB0aGUKb2JzZXJ2ZWQgdmFsdWUgVCh5KSBjb21wdXRlZCBmcm9tIHRoZSBkYXRhIHkuIE1pbiBhbmQgbWF4IGFyZSBvZnRlbgpnb29kIHRlc3Qgc3RhdGlzdGljcyBmb3IgY29udGludW91cyBvdXRjb21lcy4KCmBgYHtyIH0KcHBjX3N0YXQoeSwgeXJlcF9kcmF3cywgc3RhdD0ibWluIikKcHBjX3N0YXQoeSwgeXJlcF9kcmF3cywgc3RhdD0ibWF4IikKYGBgCgpTaG93IDIgdGVzdCBzdGF0aXN0aWNzIGluIG9uZSBwbG90CgpgYGB7ciB9CmNvbG9yX3NjaGVtZV9zZXQoImJyZXdlci1QYWlyZWQiKQpwcGNfc3RhdF8yZCh5LCB5cmVwX2RyYXdzLCBzdGF0PWMoIm1pbiIsIm1heCIpKQpjb2xvcl9zY2hlbWVfc2V0KCkKYGBgCgo=