Sample size and power [RSABE / ABEL]
following this thread some remarks.
The idea behind Scaled Average Bioequivalence (SABE) is to avoid extreme sample sizes required for Average Bioequivalence (ABE) while preserving power independent from the CV.
With the -script at the end you can reproduce the plots below. All with sample sizes for at least 80% power in a two-sequence four-period full replicate design. Since Average Bioequivalence with Expanding Limits (ABEL) is less permissive than Reference-Scaled Average Bioequivalence (RSABE) we require larger sample sizes (28–36 vs 22–32).
- Evaluation based on ABE (if \(\small{\widehat{CV}_\textrm{wR}\leq30\%}\)) or ABEL (if \(\small{\widehat{CV}_\textrm{wR}>30\%}\)).
- If by ABEL,
- limit the expansion at \(\small{\widehat{CV}_\textrm{wR}=50\%}\) and
- impose the PE-constraint {80.00–125.00%}.
- Evaluation based on ABE (if \(\small{\widehat{s}_\textrm{wR}<0.294}\)) or RSABE (if \(\small{\widehat{s}_\textrm{wR}\ge0.294}\)). Note that \(\small{\widehat{s}_\textrm{wR}=0.294\Rightarrow \widehat{CV}_\textrm{wR}\approx 30.0469\%}\).
- If by RSABE, impose the PE-constraint {80.00–125.00%}.
If \(\small{\widehat{CV}_\textrm{wR}}\) is relatively small, the ABE-component is relevant. At \(\small{\widehat{CV}_\textrm{wR}\sim 30\%}\) the chance to assess the study by ABE is \(\small{\sim 50\%}\). When \(\small{\widehat{CV}_\textrm{wR}}\) gets larger, the PE-constraint gets increasingly important. That’s more pronounced for RSABE because in ABEL the upper cap of scaling is relevant as well.
library(PowerTOST)
design <- "2x2x4"
target <- 0.8
theta0 <- 0.9
CV <- seq(0.3, 0.65, 0.0005)
# Cave: Long runtime with such a small step size
# 'Pure' SABE for comparison: No upper cup of scaling, no PE constraint
# ABEL modified
pure1 <- reg_const("USER", r_const = 0.76, CVswitch = 0.3, CVcap = Inf)
# RSABE modified
pure2 <- reg_const("USER", r_const = log(1.25)/0.25, CVswitch = 0.3, CVcap = Inf)
pure1$pe_constr <- pure2$pe_constr <- FALSE
pure1$est_method <- "ANOVA"
pure2$est_method <- "ISC"
# Defaults: theta0 = 0.9, targetpower = 0.8
res1 <- res2 <- data.frame(CV = CV, n = NA_integer_, overall = NA_real_,
PE = NA_real_, ABE = NA_real_, SABE = NA_real_)
pb <- txtProgressBar(0, 1, 0, char = "\u2588", width = NA, style = 3)
for (i in seq_along(CV)) {
tmp <- sampleN.scABEL(CV = CV[i], design = design,
theta0 = theta0, targetpower = target,
details = FALSE, print = FALSE)
res1[i, 2] <- tmp[["Sample size"]]
res1[i, 3] <- tmp[["Achieved power"]]
tmp <- sampleN.RSABE(CV = CV[i], design = design,
theta0 = theta0, targetpower = target,
details = FALSE, print = FALSE)
res2[i, 2] <- tmp[["Sample size"]]
res2[i, 3] <- tmp[["Achieved power"]]
# Component of ABEL and RSABE: PE within constraints
res1[i, 4] <- suppressMessages(
power.scABEL(CV = CV[i], design = design,
theta0 = theta0, n = res1$n[i],
details = TRUE)[3])
res2[i, 4] <- suppressMessages(
power.RSABE(CV = CV[i], design = design,
theta0 = theta0, n = res2$n[i],
details = TRUE)[3])
# Component of ABEL and RSABE: ABE
res1[i, 5] <- power.TOST(CV = CV[i], design = design,
theta0 = theta0, n = res1$n[i])
res2[i, 5] <- power.TOST(CV = CV[i], design = design,
theta0 = 0.9, n = res2$n[i])
# unconstrained SABE
res1[i, 6] <- power.scABEL(CV = CV[i], design = design,
theta0 = theta0, n = res1$n[i],
regulator = pure1)
res2[i, 6] <- power.RSABE(CV = CV[i], design = design,
theta0 = theta0, n = res2$n[i],
regulator = pure2)
setTxtProgressBar(pb, i / length(CV))
}
close(pb)
# Plots
clr <- c("#0000AA80", "magenta", "#11AA10", "#CC000080")
leg <- c("PE constraint alone", "ABE", "Unconstrained SABE")
dev.new(width = 4.5, height = 4.5)
op <- par(no.readonly = TRUE)
par(mar = c(3, 2.8, 0, 0), mgp = c(2, 0.5, 0), cex.axis = 0.9)
# ABEL
plot(CV, res1$n, type = "n", ylim = range(c(res1[, 3:6], res2[, 3:6])),
xlab = expression(italic(CV)[wR]), ylab = "power", las = 1)
abline(v = c(0.3, 0.5), lty = 2)
grid(); box()
lines(CV, res1$overall, lwd = 2, col = clr[1])
lines(CV, res1$PE, lwd = 2, col = clr[2])
lines(CV, res1$ABE, lwd = 2, col = clr[3])
lines(CV, res1$SABE, lwd = 2, col = clr[4])
legend("right", bg = "white", box.lty = 0, seg.len = 3, inset = 0.02, col = clr,
legend = c("ABEL", leg), lwd = 2, y.intersp = 1.2, cex = 0.9)
# RSABE
plot(CV, res2$n, type = "n", ylim = range(c(res1[, 3:6], res2[, 3:6])),
xlab = expression(italic(CV)[wR]), ylab = "power", las = 1)
abline(v = 0.3, lty = 2)
grid(); box()
lines(CV, res2$overall, lwd = 2, col = clr[1])
lines(CV, res2$PE, lwd = 2, col = clr[2])
lines(CV, res2$ABE, lwd = 2, col = clr[3])
lines(CV, res2$SABE, lwd = 2, col = clr[4])
legend("right", bg = "white", box.lty = 0, seg.len = 3, inset = 0.02, col = clr,
legend = c("RSABE", leg), lwd = 2, y.intersp = 1.2, cex = 0.9)
par(op)
Dif-tor heh smusma 🖖🏼 Довге життя Україна!
Helmut Schütz
The quality of responses received is directly proportional to the quality of the question asked. 🚮
Science Quotes
Complete thread:
- Sample size and powerHelmut 2022-03-28 13:39 [RSABE / ABEL]