Bioequivalence Tests for 2x2 Cross-Over Trial Designs with Log-Normal Data

knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(comment = "#>", collapse = TRUE)
options(rmarkdown.html_vignette.check_title = FALSE) #title of doc does not match vignette title
doc.cache <- T #for cran; change to F

In the example below, we illustrate the use of SimTOST for 2x2 crossover trials. This randomized two-period, two-sequence design and its standard subject/period/treatment model are described by Chow and Liu [@chow_liu_bioequivalence_2008]. The design is also used in bioequivalence studies [@CHMP2010]. As a first step, we load the package.

library(SimTOST)

Bioequivalence tests for AUC and Cmax

We consider Example 1 from the PASS Sample Size Software Chapter 351 [@PASSch351]. We aim to estimate the sample size required to demonstrate bioequivalence between a test and reference product for two pharmacokinetic parameters: the area under the curve (AUC) and the maximum concentration (Cmax). We assume a 2x2 cross-over design. The true ratio of the test to the reference product is assumed to be 1.02 for AUC and 1.03 for Cmax. Based on previous studies, it is assumed that the standard deviation for log(AUC) = 0.25 and the standard deviation for log(Cmax = 0.3). The equivalence limits for the ratio of means are set at 0.80 and 1.25.

The significance level is set to 5\%, and the sample size is calculated to achieve 80\% power. Additionally, the correlation between AUC and Cmax is assumed to be 0.25. A difference-of-means test on the log scale is employed to determine bioequivalence.

In SimTOST, we can estimate the sample size using the sampleSize() function.

mu_r <- c(AUC = log(1.00), Cmax = log(1.00))
mu_t <- c(AUC = log(1.02), Cmax = log(1.03))
sigma <- c(AUC = 0.25, Cmax = 0.3)
lequi_lower <- c(AUC = log(0.80), Cmax = log(0.80))
lequi_upper <- c(AUC = log(1.25), Cmax = log(1.25))

(ss <- sampleSize(power = 0.8, alpha = 0.05,
                 mu_list = list("R" = mu_r, "T" = mu_t),
                 sigma_list = list("R" = sigma, "T" = sigma),
                 list_comparator = list("T_vs_R" = c("R", "T")),
                 rho = 0.25,
                 list_lequi.tol = list("T_vs_R" = lequi_lower),
                 list_uequi.tol = list("T_vs_R" = lequi_upper),
                 dtype = "2x2", ctype = "DOM", distribution = "norm",
                 adjust = "none", nsim = 1000, seed = 1234))

The total sample size required is r ss$response$n_total subjects, which corresponds to the estimate obtained using the PASS software (n=37).

Estimating power at a fixed sample size

The unified simPower() function can also evaluate a prespecified crossover sample size. For this continuous 2x2 example, n is the total number of subjects across the two sequences, so we evaluate 40 subjects.

fixed_power <- simPower(
  n = 40,
  distribution = "norm",
  mu_list = list(R = mu_r, T = mu_t),
  sigma_list = list(R = sigma, T = sigma),
  list_comparator = list(T_vs_R = c("R", "T")),
  list_y_comparator = list(T_vs_R = c("AUC", "Cmax")),
  rho = 0.25,
  list_lequi.tol = list(T_vs_R = lequi_lower),
  list_uequi.tol = list(T_vs_R = lequi_upper),
  dtype = "2x2", ctype = "DOM", nsim = 1000, seed = 1234
)
fixed_power

The result reports the estimated power and its Monte Carlo confidence interval through fixed_power$power, fixed_power$power_LCI, and fixed_power$power_UCI.

References



Try the SimTOST package in your browser

Any scripts or data that you put into this service are public.

SimTOST documentation built on Oct. 9, 2026, 5:07 p.m.