Comment on “Bayesian additional evidence for decision making under small sample uncertainty”

 ## reproduce BAE calculations from Sondhi et al. (2021)

## -----------------------------------------------------------------------------

## data from Sondhi et al. (2021)
hr <- 0.42
hrCI <- c(0.14, 1.23)
x <- log(hr)
za <- qnorm(p = 0.975)
se <- (log(hrCI)[2] - log(hrCI)[1])/(2*za)
p <- 2*pnorm(q = abs(x)/se, lower.tail = FALSE)

## prior evidence from Innocenti et al. (2019)
hr2 <- 0.13
hr2CI <- c(0.06, 0.30)
x2 <- log(0.13)
se2 <- (log(hr2CI[2]) - log(hr2CI[1]))/(2*za)

## Bayesian additional evidence based on effect size and standard error
baeES <- function(x, se, alpha) {
    bae <- sign(x)*sqrt(2)*qnorm(p = 1 - alpha/2)*se - x
    return(bae)
}

## Bayesian additional evidence based on confidence interval limits
baeCI <- function(L, U) {
    x <- mean(c(L, U))
    bae <- (sign(x)*sqrt(2)*(U - L) - (U + L))/2
    return(bae)
}

## compute BAE for data from Sondhi et al. (2021)
bae <- baeCI(L = log(hrCI)[1], U = log(hrCI)[2])


## functions to compute prior parameters of the 3 reverse-Bayes approaches
## -----------------------------------------------------------------------------

## determine advocacy prior such that (1 - alpha) posterior credible interval
## fixed to zero and prior coefficient of variation fixed to 1/z_(alpha/2)
advPriorCV <- function(x, se, alpha) {
    ## x: data
    ## se: standard error of data
    ## alpha: significance level
    z <- x/se
    za <- qnorm(p = 1 - alpha/2)
    if (abs(z) >= za) {
        out <- out <- c("mean" = NaN, "var" = NaN)
    } else {
        meanPrior <- 2*x/(1 - z^2/za^2)
        varPrior <- meanPrior^2/za^2
        out <- c("mean" = meanPrior, "var" = varPrior)
    }
    return(out)
}

## determine advocacy prior such that (1 - alpha) posterior credible interval
## fixed to zero and prior variance fixed to variance of data
advPriorVar <- function(x, se, alpha) {
    ## x: data
    ## se: standard error of data
    ## alpha: significance level
    z <- x/se
    za <- qnorm(p = 1 - alpha/2)
    if (abs(z) >= za) {
        out <- out <- c("mean" = NaN, "var" = NaN)
    } else {
        meanPrior <- sign(x)*sqrt(2)*za*se - x
        varPrior <- se^2
        out <- c("mean" = meanPrior, "var" = varPrior)
    }
    return(out)
}

## determine advocacy prior such that (1 - alpha) posterior credible interval
## fixed to zero and prior mean fixed to value of data
advPriorMean <- function(x, se, alpha) {
    ## x: data
    ## se: standard error of data
    ## alpha: significance level
    z <- x/se
    za <- qnorm(p = 1 - alpha/2)
    if (abs(z) >= za) {
        out <- out <- c("mean" = NaN, "var" = NaN)
    } else {
        meanPrior <- x
        varPrior <- se^2/(za^2/z^2 - 1)
        out <- c("mean" = meanPrior, "var" = varPrior)
    }
    return(out)
}

## compute posterior credible interval to check correctness of functions
postCrI <- function(x, se, meanPrior, varPrior, alpha) {
    postVar <- 1/(1/se^2 + 1/varPrior)
    postMean <- (x/se^2 + meanPrior/varPrior)*postVar
    za <- qnorm(p = 1 - alpha/2)
    crI <- postMean + c(-1, 1)*za*sqrt(postVar)
    names(crI) <- c("lower", "upper")
    return(crI)
}


## create similar figure as in Sondhi et al. (2021)
## -----------------------------------------------------------------------------

## significance level
alpha <- 0.05

## bae
bae <- baeES(x = x, se = se, alpha = alpha)
bae <- baeCI(L = log(hrCI[1]), U = log(hrCI[2]))
baePost <- postCrI(x = x, se = se, meanPrior = bae, varPrior = se^2,
                   alpha = alpha)

## AnCred
anCredPars <- advPriorCV(x = x, se = se, alpha = alpha)
anCredPost <- postCrI(x = x, se = se, meanPrior = anCredPars[1],
                      varPrior = anCredPars[2], alpha = alpha)

## fixed mean
fmPars <- advPriorMean(x = x, se = se, alpha = alpha)
fmPost <- postCrI(x = x, se = se, meanPrior = fmPars[1],
                  varPrior = fmPars[2], alpha = alpha)

## CIs
lower <- c(x - za*se,
           bae - za*se, anCredPars[1] - za*sqrt(anCredPars[2]),
           fmPars[1] - za*sqrt(fmPars[2]),
           baePost[1], anCredPost[1], fmPost[1],
           x2 - za*se2)
est <- c(x,
         bae, anCredPars[1], fmPars[1],
         mean(baePost), mean(anCredPost), mean(fmPost),
         x2)
upper <- c(x + za*se,
           bae + za*se, anCredPars[1] + za*sqrt(anCredPars[2]),
           fmPars[1] + za*sqrt(fmPars[2]),
           baePost[2], anCredPost[2], fmPost[2],
           x2 + za*se2)
colpalette <- c("#000000", "#1B9E77", "#D95F02", "#7570B3", "#000000")
cols <- colpalette[c(1, 2, 3, 4, 2, 3, 4, 5)]


xseq <- c(1, 2.8, 3, 3.2, 1.8, 2, 2.2, 4)
xbks <- c(1, 2, 3, 4)
xlab <- c("Data", "Posterior", "Advocacy Prior", "Additional data")
ybks <- c(log(10), log(1), log(1/10), log(1/100), log(1/1000))
plot(x = xseq, y = est, type = "n", ylim = c(min(lower), log(5)),
     xlim = c(0.8*min(xbks), 1.1*max(xbks)), xaxt = "n", yaxt = "n",
     xlab = "", ylab = "", las = 2)
mtext(side = 2, text = "Hazard Ratio", line = 3.3)
abline(h = 0, lty = 2, col = adjustcolor(col = 1, alpha = 0.5), lwd = 1.5)
axis(side = 2, at = ybks, labels = pCalibrate::formatBF(exp(ybks)), las = 2)
axis(side = 1, at = xbks, labels = xlab)
abline(h = ybks, lty = 1, col = adjustcolor(col = 1, alpha = 0.1))
box()
arrows(x0 = xseq, y0 = lower, y1 = upper, angle = 90, code = 3, length = 0.1,
       lwd = 1.2, col = cols)
points(x = xseq, y = est, pch = 20, cex = 1.2, col = cols)
legend("bottomright", c("BAE", "AnCred", "fixed mean"), pch = 20,
       col = colpalette[2:4], bg = "white")


## plot partition of the prior parameter space
## -----------------------------------------------------------------------------

## BAE for different prior variances than the one observed in the data
bae2 <- function(x, se, alpha, g) {
    ## g: the relative prior variance
    bae <- (sign(x)*qnorm(p = 1 - alpha/2)*se*sqrt(g/(1 + g)) - x*g/(1 + g))*(1 + g)
    return(bae)
}

## prior parameter space data
priorsDF <- data.frame(mean = c(anCredPars[1], bae, fmPars[1]),
                       var = c(anCredPars[2], se^2, fmPars[2]),
                       type = c("AnCred", "BAE", "fixed mean"),
                       color = colpalette[c(3, 2, 4)])

## plot prior parameter space
loggseq <- seq(log(0.01), log(25), length.out = 500)
gbks <- c(0.1, 0.2, 0.5, 1, 2, 5, 10, 20)
baeseq <- bae2(x = x, se = se, alpha = alpha, g = exp(loggseq))
plot(x = loggseq, y = baeseq/x, type = "l", las = 1, xaxt = "n",
     xlab = bquote("relative prior variance" ~ italic(g) == tau^2/sigma^2),
     ylab = "", xlim = log(c(0.1, 20)), ylim = c(0, 6), lwd = 1.8)
axis(side = 1, at = log(gbks), labels = gbks)
mtext(side = 2, text =bquote("relative prior mean" ~ mu/hat(theta)),
      line = 2)
## draw non-credibiliy region
polygon(x = c(head(loggseq)[1], loggseq, tail(loggseq)[1]),
        y = c(10, baeseq, 10)/x, density = 10,
        angle = 135, border = NA, col = adjustcolor(col = "black", alpha = 0.3),
        lty = 2, lwd = 1)
## draw credibiliy region
polygon(x = c(head(loggseq)[1], loggseq, tail(loggseq)[1]),
        y = c(-100, baeseq, -100)/x, density = 10,
        angle = 45, border = NA, col = adjustcolor(col = "black", alpha = 0.3),
        lty = 3, lwd = 1.5)
## draw significance region
lines(x = loggseq, y = -qnorm(p = 1 - alpha/2)*sqrt(exp(loggseq)*se^2)/x,
      col = adjustcolor(colpalette[3], alpha = 0.5))
## draw fixed mean/variance
abline(h = 1, col = adjustcolor(colpalette[4], alpha = 0.5))
abline(v = 0, col = adjustcolor(colpalette[2], alpha = 0.5))
## draw tipping points
points(x = log(priorsDF[,2]/se^2), y = priorsDF[,1]/x, col = priorsDF[,4],
       pch = 20, cex = 1.6)
legend("topleft", c("BAE", "AnCred", "fixed mean"), pch = 20,
       col = colpalette[2:4], bg = "white")
text(x = log(0.28), y = 1.5,
     labels = bquote("posterior credible at" ~ alpha == .(alpha)), cex = 1.1)
text(x = log(5), y = 0.5,
     labels = bquote("posterior not credible at" ~ alpha == .(alpha)), cex = 1.1)

留言

這個網誌中的熱門文章

可轉移性、普遍性、代表性和外部有效性

頻率學派 vs 貝氏學派

貝氏分析計算器