|
| 1 | +--- |
| 2 | +title: "Bioequivalence Tests in a Parallel Trial Design" |
| 3 | +author: "Thomas Debray" |
| 4 | +date: "`r format(Sys.time(), '%B %d, %Y')`" |
| 5 | +output: |
| 6 | + html_document: |
| 7 | + fig_caption: yes |
| 8 | + fig_width: 9 |
| 9 | + fig_height: 6 |
| 10 | +vignette: > |
| 11 | + %\VignetteIndexEntry{Bioequivalence Tests in a Parallel Trial Design} |
| 12 | + %\VignetteEngine{knitr::rmarkdown} |
| 13 | + %\VignetteEncoding{UTF-8} |
| 14 | +bibliography: 'references.bib' |
| 15 | +link-citations: yes |
| 16 | +--- |
| 17 | + |
| 18 | +```{r setup, include=FALSE, message = FALSE, warning = FALSE} |
| 19 | +knitr::opts_chunk$set(echo = TRUE) |
| 20 | +knitr::opts_chunk$set(comment = "#>", collapse = TRUE) |
| 21 | +options(rmarkdown.html_vignette.check_title = FALSE) #title of doc does not match vignette title |
| 22 | +doc.cache <- T #for cran; change to F |
| 23 | +
|
| 24 | +library(dplyr) |
| 25 | +``` |
| 26 | + |
| 27 | + |
| 28 | +In the examples below, we demonstrate the use of **SimTOST** for parallel trial designs. To begin, we first load the package. |
| 29 | + |
| 30 | +```{r, echo = T, message=F} |
| 31 | +library(SimTOST) |
| 32 | +``` |
| 33 | + |
| 34 | +# Multiple Independent Co-Primary Endpoints |
| 35 | +We here consider a bio-equivalence trial with 2 treatment arms and $m=5$ endpoints. The sample size is calculated to ensure that the test and reference products are equivalent with respect to all 5 endpoints. The true ratio between the test and reference products is assumed to be 1.05. It is assumed that the standard deviation of the log-transformed response variable is $\sigma = 0.3$, and that all tests are independent ($\rho = 0$). The equivalence limits are set at 0.80 and 1.25. The significance level is 0.05. The sample size is determined at a power of 0.8. |
| 36 | + |
| 37 | +This example is adapted from @mielke_sample_2018, who employed a difference-of-means test on the log scale. The sample size calculation can be conducted using two approaches, both of which are illustrated below. |
| 38 | + |
| 39 | +## Approach 1: Using sampleSize_Mielke |
| 40 | +In the first approach, we calculate the required sample size for 80% power using the [sampleSize_Mielke()](../reference/sampleSize_Mielke.html) function. This method directly follows the approach described in @mielke_sample_2018, assuming a difference-of-means test on the log-transformed scale with specified parameters. |
| 41 | + |
| 42 | +```{r, eval = TRUE} |
| 43 | +ssMielke <- sampleSize_Mielke(power = 0.8, Nmax = 1000, m = 5, k = 5, rho = 0, |
| 44 | + sigma = 0.3, true.diff = log(1.05), |
| 45 | + equi.tol = log(1.25), design = "parallel", |
| 46 | + alpha = 0.05, adjust = "no", seed = 1234, |
| 47 | + nsim = 10000) |
| 48 | +ssMielke |
| 49 | +``` |
| 50 | +For 80\% power, `r ssMielke["SS"]` subjects per sequence (`r ssMielke["SS"] * 2` in total) would have been required. |
| 51 | + |
| 52 | +## Approach 2: Using sampleSize |
| 53 | +Alternatively, the sample size calculation can be performed using the [sampleSize()](../reference/sampleSize.html) function. This method assumes that effect sizes are normally distributed on the log scale and uses a difference-of-means test (`ctype = "DOM"`) with user-specified values for `mu_list` and `sigma_list`. Unlike the first approach, this method allows for greater flexibility in specifying parameter distributions. |
| 54 | + |
| 55 | +```{r, eval = TRUE} |
| 56 | +mu_r <- setNames(rep(log(1.00), 5), paste0("y", 1:5)) |
| 57 | +mu_t <- setNames(rep(log(1.05), 5), paste0("y", 1:5)) |
| 58 | +sigma <- setNames(rep(0.3, 5), paste0("y", 1:5)) |
| 59 | +lequi_lower <- setNames(rep(log(0.8), 5), paste0("y", 1:5)) |
| 60 | +lequi_upper <- setNames(rep(log(1.25), 5), paste0("y", 1:5)) |
| 61 | +
|
| 62 | +ss <- sampleSize(power = 0.8, alpha = 0.05, |
| 63 | + mu_list = list("R" = mu_r, "T" = mu_t), |
| 64 | + sigma_list = list("R" = sigma, "T" = sigma), |
| 65 | + list_comparator = list(c("R", "T")), |
| 66 | + list_lequi.tol = list("T_vs_R" = lequi_lower), |
| 67 | + list_uequi.tol = list("T_vs_R" = lequi_upper), |
| 68 | + dtype = "parallel", ctype = "DOM", lognorm = FALSE, |
| 69 | + adjust = "no", ncores = 1, nsim = 10000, seed = 1234) |
| 70 | +ss |
| 71 | +``` |
| 72 | +For 80\% power, a total of `r ss$response %>% pull(n_total)` would be required. |
| 73 | + |
| 74 | + |
| 75 | +# Multiple Correlated Co-Primary Endpoints |
| 76 | +In the second example, we have $k=m=5$, $\sigma = 0.3$ and $\rho = 0.8$. Again, we can estimate the sample size using the functions provided by @mielke_sample_2018: |
| 77 | + |
| 78 | +```{r, eval = TRUE} |
| 79 | +ssMielke <- sampleSize_Mielke(power = 0.8, Nmax = 1000, m = 5, k = 5, rho = 0.8, |
| 80 | + sigma = 0.3, true.diff = log(1.05), |
| 81 | + equi.tol = log(1.25), design = "parallel", |
| 82 | + alpha = 0.05, adjust = "no", seed = 1234, |
| 83 | + nsim = 10000) |
| 84 | +ssMielke |
| 85 | +``` |
| 86 | +For 80\% power, `r ssMielke["SS"]` subjects per sequence (`r ssMielke["SS"] * 2` in total) would have been required. |
| 87 | + |
| 88 | +We can perform the same analysis using [sampleSize()](../reference/sampleSize.html). In this case, we provide estimates for $\mu$ and $\sigma$ on the original scale, assuming they follow a normal distribution on the log scale (`lognorm = TRUE`). Instead of testing the difference of log-transformed means, we now test the ratio of the (untransformed) means. |
| 89 | + |
| 90 | +```{r, eval = TRUE} |
| 91 | +ss <- sampleSize(power = 0.8, alpha = 0.05, |
| 92 | + mu_list = list("R" = rep(1.00, 5), |
| 93 | + "T" = rep(1.05, 5)), |
| 94 | + sigma_list = list("R" = rep(0.3, 5), |
| 95 | + "T" = rep(0.3, 5)), |
| 96 | + rho = 0.8, # high correlation between the endpoints |
| 97 | + lequi.tol = rep(0.8, 5), |
| 98 | + uequi.tol = rep(1.25, 5), |
| 99 | + dtype = "parallel", ctype = "ROM", lognorm = TRUE, |
| 100 | + adjust = "no", ncores = 1, k = 5, nsim = 10000, seed = 1234) |
| 101 | +ss |
| 102 | +``` |
| 103 | + |
| 104 | +# References |
0 commit comments