Sample size and power [RSABE / ABEL]

posted by Helmut Homepage – Vienna, Austria, 2022-03-28 13:39 (60 d 04:29 ago) – Posting: # 22876
Views: 394

Dear all,

following this thread some remarks.

The idea behind Scaled Average Bioequivalence (SABE) is to avoid extreme sample sizes required for Average Bio­equi­va­lence (ABE) while preserving power independent from the CV.

With the [image]-script at the end you can reproduce the plots below. All with sample sizes for at least 80% power in a two-se­quence four-period full replicate design. Since Average Bio­equi­va­lence with Expanding Limits (ABEL) is less permissive than Re­fe­rence-Scaled Average Bioequivalence (RSABE) we require larger sample sizes (28–36 vs 22–32).

[image]

[image]


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 🖖 [image]
Helmut Schütz
[image]

The quality of responses received is directly proportional to the quality of the question asked. 🚮
Science Quotes

Complete thread:

UA Flag
Activity
 Admin contact
22,108 posts in 4,630 threads, 1,567 registered users;
online 7 (0 registered, 7 guests [including 4 identified bots]).
Forum time: Friday 18:09 CEST (Europe/Vienna)

We absolutely must leave room for doubt
or there is no progress and no learning.
There is no learning without having to pose a question.
And a question requires doubt.
People search for certainty.
But there is no certainty.    Richard Feynman

The Bioequivalence and Bioavailability Forum is hosted by
BEBAC Ing. Helmut Schütz
HTML5