AUC (by ABE) and Cmax (by ABEL): power [RSABE / ABEL]
❝ Am I correct in understanding that if in the literature, for example, CV = 50% for AUC is found as the largest (for Cmax it is also more than 30%, but less than for AUC), … then it will not be correct to use sampleN.scABEL to estimate the sample size?
Uncommon that CV of AUC > CV of Cmax though it happens sometimes. You know the answer already:
❝ … then it will not be correct to use sampleN.scABEL to estimate the sample size?
Yes, use
sampleN.TOST()
.❝ There will be no equivalence limits scaling for this parameter.
Generally not (exceptions: partial AUCs for MR, AUC for the WHO).
When I presented this ‘side effect’ (slide 25) at the 2nd GBHI conference (Rockville, September 2015) staff of the FDA shook their heads in disbelief and we had this conversation: “This is just a draft guideline, right?” – “No, it’s final and in force for five years.” – “What‽”
At the 3rd GBHI conference (Amsterdam, April 2018) I raised the issue again and a member of the EMA’s PK Working Party replied that he doesn’t see a problem. Other members of the PKWP agreed.
❝ And on the other hand, for the parameter Cmax there will be conditions of forced bioequivalence, since the sample size will be at least twice the allowable size calculated by the AUC parameter.
Technically speaking the members of the PKWP were right because there is no “forced bioequivalence”. In any passing study – even if the T/R-ratio is far from 100% – the patient’s risk is still ≤5% (leaving the well-known inflation of type I error in reference-scaling aside).
❝ How to proceed?
According to the current guidelines (suggesting ABEL not for all PK metrics), accept it. Fortunately the WHO allows ABEL for AUC since 2018 (full replicate designs only). Let’s hope that other jurisdictions will follow.
Result of an example (-script at the end):
design = 2x2x4
theta0 = 0.9 (AUC), 0.9 (Cmax)
targetpower = 80% (both PK metrics)
AUC (ABE: 80.00–125.00%)
CVw : 50% (T = R)
n : 100
power: 80.03% for theta0 0.9000 and 1.1111
Cmax (ABEL: 77.23–129.48%)
CVw : 35% (T = R)
n : 34
power: 81.18% for theta0 0.9000 and 1.1111
The sample size is driven by AUC.
Cmax with n = 100:
power: 80.00% for theta0 0.8441 and 1.1847
design = 2x2x4
theta0 = 0.9 (AUC), 0.9 (Cmax)
targetpower = 80% (both PK metrics)
AUC (ABEL: 69.84–143.19%)
CVw : 50% (T = R)
n : 28
power: 81.43% for theta0 0.9000 and 1.1111
Cmax (ABEL: 77.23–129.48%)
CVw : 35% (T = R)
n : 34
power: 81.18% for theta0 0.9000 and 1.1111
The sample size is driven by Cmax.
AUC with n = 34:
power: 80.00% for theta0 0.8749 and 1.1430
On the other hand, is anybody worried if we plan a study for ABE based on the PK metric with higher variability and get an ‘incentive’ for the other one? Rather not. Here an example where the sample sizes differ more than twofold:
design = 2x2x2
theta0 = 0.95 (AUC), 0.95 (Cmax)
targetpower = 80% (both PK metrics)
AUC (ABE: 80.00–125.00%)
CVw : 15%
n : 12
power: 83.05% for theta0 0.9500 and 1.0526
Cmax (ABE: 80.00–125.00%)
CVw : 25%
n : 28
power: 80.74% for theta0 0.9500 and 1.0526
The sample size is driven by Cmax.
AUC with n = 28:
power: 80.00% for theta0 0.8857 and 1.1290
A funny one:
design = parallel
theta0 = 0.95 (AUC), 0.95 (Cmax)
targetpower = 80% (both PK metrics)
AUC (ABE: 80.00–125.00%)
CV : 40%
n : 130
power: 80.35% for theta0 0.9500 and 1.0526
Cmax (ABE: 80.00–125.00%)
CV : 60%
n : 266
power: 80.08% for theta0 0.9500 and 1.0526
The sample size is driven by Cmax.
AUC with n = 266:
power: 80.00% for theta0 0.9000 and 1.1111
library(PowerTOST)
##############
# input area #
##############
design <- "2x2x4"
targetpower <- 0.80
theta0.Cmax <- 0.90
theta0.AUC <- 0.90
# In replicate designs CV can be a 2-element vector
# 1st element CVw of T, 2nd element CVw of R
CV.Cmax <- 0.35
CV.AUC <- 0.50
method.Cmax <- "ABEL" # If clinically justified, otherwise "ABE".
method.AUC <- "ABE" # In most jurisdictions, can be "ABEL" for the WHO.
sdsims <- FALSE # Set to TRUE only if "2x3x3" design and
# heteroscedasticity when CV[1] > CV[2].
# Patience - will be slow!
# Don’t change below unless you know what you are doing.
repl <- TRUE
designs <- known.designs()[, c(2, 9)]
replicates <- designs[grep("replicate", designs$name), 1]
if (!design %in% replicates) {
repl <- FALSE
if (length(CV.Cmax) == 2) CV.Cmax <- signif(mse2CV(sum(CV2mse(CV.Cmax))/2), 4)
if (length(CV.AUC) == 2) CV.AUC <- signif(mse2CV(sum(CV2mse(CV.AUC))/2), 4)
} else {
CV.Cmax.T <- CV.Cmax[1]
CV.Cmax.R <- CV.Cmax[length(CV.Cmax)]
CV.AUC.T <- CV.AUC[1]
CV.AUC.R <- CV.AUC[length(CV.AUC)]
}
opt <- function(x) { # function to find minimum theta0 for targetpower
if (metric == "Cmax") {
if (method.Cmax == "ABE") {
power.TOST(CV = CV.Cmax, theta0 = x, n = n.AUC,
design = design) - targetpower
} else {
if (sdsims) {
power.scABEL.sds(CV = CV.Cmax, theta0 = x, n = n.AUC,
design = design, progress = FALSE) - targetpower
} else {
power.scABEL(CV = CV.Cmax, theta0 = x, n = n.AUC,
design = design) - targetpower
}
}
} else {
if (method.AUC == "ABE") {
power.TOST(CV = CV.AUC, theta0 = x, n = n.Cmax,
design = design) - targetpower
} else {
if (sdsims) {
power.scABEL.sds(CV = CV.AUC, theta0 = x, n = n.Cmax,
design = design, progress = FALSE) - targetpower
} else {
power.scABEL(CV = CV.AUC, theta0 = x, n = n.Cmax,
design = design) - targetpower
}
}
}
}
# Sample sizes, powers, lower/upper limits
if (method.AUC %in% c("ABE", "ABEL")) {
if (method.AUC == "ABE") {
x <- sampleN.TOST(CV = CV.AUC, theta0 = theta0.AUC, design = design,
targetpower = targetpower, details = FALSE, print = FALSE)
LU.AUC <- c(lower = 80, upper = 125)
} else {
if (sdsims) {
x <- sampleN.scABEL.sds(CV = CV.AUC, theta0 = theta0.AUC, design = design,
targetpower = targetpower, details = FALSE,
print = FALSE, progress = FALSE)
} else {
x <- sampleN.scABEL(CV = CV.AUC, theta0 = theta0.AUC, design = design,
targetpower = targetpower, details = FALSE, print = FALSE)
}
LU.AUC <- 100*scABEL(CV = CV.AUC[length(CV.AUC)])
}
}
n.AUC <- x[["Sample size"]]
power.AUC <- x[["Achieved power"]]
if (method.Cmax %in% c("ABE", "ABEL")) {
if (method.Cmax == "ABE") {
x <- sampleN.TOST(CV = CV.Cmax, theta0 = theta0.Cmax, design = design,
targetpower = targetpower, details = FALSE, print = FALSE)
LU.Cmax <- c(lower = 80, upper = 125)
} else {
if (sdsims) {
x <- sampleN.scABEL.sds(CV = CV.Cmax, theta0 = theta0.Cmax, design = design,
targetpower = targetpower, details = FALSE,
print = FALSE, progress = FALSE)
} else {
x <- sampleN.scABEL(CV = CV.Cmax, theta0 = theta0.Cmax, design = design,
targetpower = targetpower, details = FALSE, print = FALSE)
}
LU.Cmax <- 100*scABEL(CV = CV.Cmax[length(CV.Cmax)])
}
}
n.Cmax <- x[["Sample size"]]
power.Cmax <- x[["Achieved power"]]
n <- c(AUC = n.AUC, Cmax = n.Cmax)
# Find most deviating theta0 for the metric with lower sample size.
if (n[["AUC"]] >= n[["Cmax"]]) {
metric <- "Cmax"
x <- uniroot(opt, interval = c(LU.Cmax[[1]]/100, theta0.Cmax), tol = 1e-8)
theta0.Cmax1 <- x$root
power.Cmax1 <- x$f.root + targetpower
} else {
metric <- "AUC"
x <- uniroot(opt, interval = c(LU.AUC[[1]]/100, theta0.AUC), tol = 1e-8)
theta0.AUC1 <- x$root
power.AUC1 <- x$f.root + targetpower
}
# Aggregate results for output.
txt <- paste0("\ndesign = ", design,
"\ntheta0 = ", theta0.AUC, " (AUC), ", theta0.Cmax, " (Cmax)",
"\ntargetpower = ", 100*targetpower, "% (both PK metrics)",
"\nAUC (", method.AUC, ": ",
sprintf("%.2f\u2013%.2f%%)", LU.AUC[["lower"]], LU.AUC[["upper"]]))
if (repl) {
if (identical(CV.AUC.T, CV.AUC.R)) {
txt <- paste0(txt, "\n CVw : ", 100*CV.AUC, "% (T = R)")
} else {
txt <- paste0(txt, "\n CVw : ", 100*CV.AUC.T, "% (T), ",
100*CV.AUC.R, "% (R)")
}
} else {
if (design %in% c("2x2x2", "paired")) {
txt <- paste0(txt, "\n CVw : ", 100*CV.AUC, "%")
} else {
txt <- paste0(txt, "\n CV : ", 100*CV.AUC, "%")
}
}
txt <- paste0(txt, "\n n : ", n.AUC,
"\n power: ", sprintf("%.2f%%", 100*power.AUC), " for theta0 ",
sprintf("%.4f", theta0.AUC), " and ", sprintf("%.4f", 1/theta0.AUC),
"\nCmax (", method.Cmax, ": ",
sprintf("%.2f\u2013%.2f%%)", LU.Cmax[["lower"]], LU.Cmax[["upper"]]))
if (repl) {
if (identical(CV.Cmax.T, CV.Cmax.R)) {
txt <- paste0(txt, "\n CVw : ", 100*CV.Cmax, "% (T = R)")
} else {
txt <- paste0(txt, "\n CVw : ", 100*CV.Cmax.T, "% (T), ",
100*CV.Cmax.R, "% (R)")
}
} else {
if (design %in% c("2x2x2", "paired")) {
txt <- paste0(txt, "\n CVw : ", 100*CV.Cmax, "%")
} else {
txt <- paste0(txt, "\n CV : ", 100*CV.Cmax, "%")
}
}
txt <- paste0(txt, "\n n : ", n.Cmax,
"\n power: ", sprintf("%.2f%%", 100*power.Cmax), " for theta0 ",
sprintf("%.4f", theta0.Cmax), " and ", sprintf("%.4f", 1/theta0.Cmax), "\n")
if (n[["AUC"]] != n[["Cmax"]]) {
txt <- paste0(txt, "The sample size is driven by ",
names(n)[which(n == max(n))], ".\n")
if (n[["AUC"]] > n[["Cmax"]]) {
txt <- paste0(txt, "Cmax with n = ", n.AUC, ":\n power: ",
sprintf("%.2f%%", 100*power.Cmax1), " for theta0 ",
sprintf("%.4f", theta0.Cmax1), " and ",
sprintf("%.4f", 1/theta0.Cmax1), "\n\n")
} else {
txt <- paste0(txt, "AUC with n = ", n.Cmax, ":\n power: ",
sprintf("%.2f%%", 100*power.AUC1), " for theta0 ",
sprintf("%.4f", theta0.AUC1), " and ",
sprintf("%.4f", 1/theta0.AUC1), "\n\n")
}
}
cat(txt)
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:
- Estimate the sample size HVD Yura 2020-12-03 08:47 [RSABE / ABEL]
- AUC (by ABE) and Cmax (by ABEL): powerHelmut 2020-12-03 12:34
- AUC (by ABE) and Cmax (by ABEL): power Yura 2020-12-04 08:11
- AUC (by ABE) and Cmax (by ABEL): powerHelmut 2020-12-03 12:34