R/CFO.oc.R

Defines functions CFO.oc

Documented in CFO.oc

#' Generate operating characteristics of phase I trials single-drug trials in multiple simulations
#' 
#' Based on the toxicity outcomes, this function is used to perform multiple simulations for phase I single-drug trials and obtain relevant operating characteristics.
#'
#' @usage CFO.oc(nsimu = 5000, design, target, p.true, init.level = 1, ncohort, cohortsize,
#'        assess.window = NA, tte.para = NA, accrual.rate = NA, accrual.dist = NA, 
#'        prior.para = list(alp.prior = target, bet.prior = 1 - target),
#'        cutoff.eli = 0.95, early.stop = 0.95, seeds = NULL)
#'
#' @param nsimu the total number of trials to be simulated. The default value is 5000.
#' @param design option for selecting different designs, which can be set as \code{'CFO'}, \code{'aCFO'}, \code{'rCFO'}, 
#'               \code{'TITE-CFO'}, \code{'TITE-aCFO'}, \code{'fCFO'}, \code{'f-aCFO'}, \code{'bCFO'}, 
#'               and \code{'b-aCFO'}. Specifically, \code{'bCFO'} refers to the benchmark CFO design, 
#'               and \code{'b-aCFO'} denotes the benchmark aCFO design.
#' @param target the target DLT rate.
#' @param p.true the true DLT rates under the different dose levels.
#' @param init.level the dose level assigned to the first cohort. The default value \code{init.level} is 1.
#' @param ncohort the total number of cohorts.
#' @param cohortsize the number of patients of each cohort. 
#' @param assess.window the maximal assessment window size. \code{NA} should be assigned if the design without late-oneset outcomes.
#' @param tte.para the parameter related with the distribution of the time to DLT events. The time to DLT is sampled from a Weibull 
#'                 distribution, with \code{tte.para} representing the proportion of DLTs occurring within the first half of the 
#'                 assessment window. \code{NA} should be assigned if the design without late-oneset outcomes.
#' @param accrual.rate the accrual rate, i.e., the number of patients accrued per unit time. \code{NA} should be assigned 
#'                if the design without late-onset outcomes.
#' @param accrual.dist the distribution of the arrival times of patients. When \code{accrual.dist = 'fix'}, it corresponds to all 
#'                     patients in each cohort arriving simultaneously at a given accrual rate. When \code{accrual.dist = 'unif'}, 
#'                     it corresponds to a uniform distribution, and when \code{accrual.dist = 'exp'}, it corresponds to an 
#'                     exponential distribution. \code{NA} should be assigned if the design without late-oneset outcomes.
#' @param prior.para the prior parameters for a beta distribution, where set as \code{list(alp.prior = target, bet.prior = 1 - target)} 
#'                  by default, \code{alp.prior} and \code{bet.prior} represent the parameters of the prior distribution for 
#'                  the true DLT rate at any dose level. This prior distribution is specified as Beta(\code{alpha.prior}, \code{beta.prior}).
#' @param cutoff.eli the cutoff to eliminate overly toxic doses for safety. We recommend
#'                    the default value of \code{cutoff.eli = 0.95} for general use.
#' @param early.stop the threshold value for early stopping. The default value \code{early.stop = 0.95}
#'                generally works well.
#' @param seeds a vector of random seeds for each simulation, for example, \code{seeds = 1:nsimu} (default is \code{NULL}).
#'
#' @note The operating characteristics are generated by simulating multiple single-drug trials under the 
#'          pre-specified true toxicity probabilities of the investigational doses. The choice of which design to execute 
#'          is determined by setting the \code{design} argument. Some time-related arguments (\code{assess.window}, \code{accrual.rate}, 
#'          \code{tte.para}, and \code{accrual.dist}) need to be set as values only when running a design that can handle late-onset 
#'          toxicities; otherwise, they default to \code{NA}.\cr
#'          Additionally, in the example, we set \code{nsimu = 5} for testing time considerations. In reality, \code{nsimu} 
#'          is typically set to 5000 to ensure the accuracy of the results.
#'
#' @return The \code{CFO.oc()} function returns basic setup of ($simu.setup) and the operating 
#'         characteristics of the design: \cr
#'         \itemize{
#' \item p.true: the true DLT rates under the different dose levels.
#' \item selpercent: the selection percentage at each dose level.
#' \item npatients: the averaged number of patients treated at each dose level in one simulation.
#' \item ntox: the averaged number of toxicity observed at each dose level in one simulation.
#' \item MTDsel: the percentage of correct selection of the MTD.
#' \item MTDallo: the percentage of patients allocated to the MTD.
#' \item oversel: the percentage of selecting a dose above the MTD.
#' \item overallo: the percentage of allocating patients at dose levels above the MTD.
#' \item averDLT: the percentage of the patients suffering DLT.
#' \item averdur: the average trial duration if trials with late-onset toxicities.
#' \item percentstop: the percentage of early stopping without selecting the MTD.
#' \item simu.setup: the parameters for the simulation set-up.
#' }
#' 
#' @author Jialu Fang, Ninghao Zhang, Wenliang Wang, and Guosheng Yin
#' 
#' @references Jin H, Yin G (2022). CFO: Calibration-free odds design for phase I/II clinical trials.
#'             \emph{Statistical Methods in Medical Research}, 31(6), 1051-1066. \cr
#'             Jin H, Yin G (2023). Time‐to‐event calibration‐free odds design: A robust efficient design for 
#'             phase I trials with late‐onset outcomes. \emph{Pharmaceutical Statistics}. 22(5), 773–783.\cr
#'             Yin G, Zheng S, Xu J (2013). Fractional dose-finding methods with late-onset toxicity in 
#'             phase I clinical trials. \emph{Journal of Biopharmaceutical Statistics}, 23(4), 856-870.\cr
#'             Fang J, Yin G (2024). Fractional accumulative calibration‐free odds (f‐aCFO) design for delayed toxicity 
#'             in phase I clinical trials. \emph{Statistics in Medicine}, 43(17), 3210-3226.
#' 
#' @importFrom dplyr transmute 
#' @import pbapply
#' @export
#'
#' @examples
#' ## setting
#' nsimu <- 5; target <- 0.2; ncohort <- 10; cohortsize <- 3; init.level <- 1
#' p.true <- c(0.01, 0.07, 0.20, 0.35, 0.50, 0.65, 0.80)
#' prior.para = list(alp.prior = target, bet.prior = 1 - target)
#' assess.window <- 3; accrual.rate <- 2; tte.para <- 0.5; accrual.dist <- 'unif'
#' 
#' 
#' ## get the operating characteristics for 5 simulations using the f-aCFO design
#' faCFOoc <- CFO.oc (nsimu, design='f-aCFO', target, p.true, init.level, ncohort, cohortsize,
#'         assess.window, tte.para, accrual.rate, accrual.dist, seeds = 1:nsimu)
#' summary(faCFOoc)
#' plot(faCFOoc)
#' 
#' \donttest{
#' # This test may take longer than 5 seconds to run
#' # It is provided for illustration purposes only
#' # Users can run this code directly
#' 
#' ## get the operating characteristics for 5 simulations using the CFO design
#' CFOoc <- CFO.oc (nsimu, design = 'CFO', target, p.true, init.level, ncohort, cohortsize,
#'          assess.window = NA, tte.para = NA, accrual.rate = NA, accrual.dist = NA, seeds = 1:nsimu)
#' summary(CFOoc)
#' plot(CFOoc)
#' 
#' ## get the operating characteristics for 5 simulations using the aCFO design
#' aCFOoc <- CFO.oc (nsimu, design = 'aCFO', target, p.true, init.level, ncohort, cohortsize,
#'        assess.window = NA, tte.para = NA, accrual.rate = NA, accrual.dist = NA, seeds = 1:nsimu)
#' summary(aCFOoc)
#' plot(aCFOoc)
#' 
#' ## get the operating characteristics for 5 simulations using the rCFO design
#' rCFOoc <- CFO.oc (nsimu, design = 'rCFO', target, p.true, init.level, ncohort, cohortsize,
#'        assess.window = NA, tte.para = NA, accrual.rate = NA, accrual.dist = NA, seeds = 1:nsimu)
#' summary(rCFOoc)
#' plot(rCFOoc)
#' 
#' ## get the operating characteristics for 5 simulations using the pCFO design
#' pCFOoc <- CFO.oc (nsimu, design = 'pCFO', target, p.true, init.level, ncohort, cohortsize,
#'        assess.window = NA, tte.para = NA, accrual.rate = NA, accrual.dist = NA, seeds = 1:nsimu)
#' summary(pCFOoc)
#' plot(pCFOoc)
#' 
#' ## get the operating characteristics for 5 simulations using the TITE-CFO design
#' TITECFOoc <- CFO.oc (nsimu, design = 'TITE-CFO', target, p.true, init.level, ncohort, cohortsize,
#'         assess.window, tte.para, accrual.rate, accrual.dist, seeds = 1:nsimu)
#' summary(TITECFOoc)
#' plot(TITECFOoc)
#' ## get the operating characteristics for 5 simulations using the TITE-aCFO design
#' TITEaCFOoc <- CFO.oc (nsimu, design = 'TITE-aCFO', target, p.true, init.level, ncohort, cohortsize,
#'         assess.window, tte.para, accrual.rate, accrual.dist, seeds = 1:nsimu)
#' summary(TITEaCFOoc)
#' plot(TITEaCFOoc)
#' ## get the operating characteristics for 5 simulations using the fCFO design
#' fCFOoc <- CFO.oc (nsimu, design = 'fCFO', target, p.true, init.level, ncohort, cohortsize,
#'         assess.window, tte.para, accrual.rate, accrual.dist, seeds = 1:nsimu)
#' summary(fCFOoc)
#' plot(fCFOoc)
#' }
CFO.oc <- function(nsimu=5000, design, target, p.true, init.level=1, ncohort, cohortsize,
                   assess.window=NA, tte.para=NA, accrual.rate=NA, accrual.dist=NA, 
                   prior.para=list(alp.prior=target, bet.prior=1-target), 
                   cutoff.eli=0.95, early.stop=0.95, seeds = NULL){
  ###############################################################################
  ###############define the functions used for main function#####################
  ###############################################################################
  
  MTD.level <- function(phi, p.true){
    if (p.true[1]>phi+0.1){
      MTD <- 99
      return(MTD)
    }
    MTD <- which.min(abs(phi - p.true))
    return(MTD)
  }
  
  ###############################################################################
  ############################MAIN DUNCTION######################################
  ###############################################################################  
  
  run.fn <- function(i){
    if (design == 'CFO' || design == 'aCFO' || design == 'rCFO' || design == 'pCFO'){
      res <- CFO.simu(design, target, p.true, init.level, ncohort, cohortsize, prior.para, 
                      cutoff.eli, early.stop, seed = seeds[i])
    }else{
      res <- lateonset.simu(design, target, p.true, init.level, ncohort, cohortsize, 
                            assess.window, tte.para, accrual.rate, accrual.dist, prior.para = prior.para, 
                            cutoff.eli = cutoff.eli, early.stop = early.stop, seed = seeds[i])
      
    }
    ress <- list(
      res=res,
      paras=list(p.true=p.true, 
                 mtd=tmtd, 
                 prior.para=prior.para,
                 target=target,
                 ncohort=ncohort,
                 cohortsize=cohortsize)
    )
    return(ress)
  }
  
  tmtd <- MTD.level(target, p.true)
  
  
  results <- pblapply(1:nsimu, run.fn)
  results_nopara <- lapply(1:nsimu, function(i)results[[i]]$res)

  ndose <- length(results_nopara[[1]]$npatients)
  
  Perc <- rep(0, ndose)
  nPatients <- rep(0, ndose); nTox <- rep(0, ndose)
  sumPatients <- 0; sumTox <- 0
  nonErrStops <- 0
  MTDsel <- 0; MTDallo <- 0; oversel <- 0; overallo <- 0
  totaltime <- 0
  for (res in results_nopara){
    if (res$MTD != 99){
      nonErrStops <- nonErrStops + 1
      Perc[res$MTD] <- Perc[res$MTD] + 1
      oversel <- oversel + sum(res$MTD>tmtd)
      if (tmtd==ndose){
        overallo <- overallo
      }else{
        overallo <- overallo + sum(res$npatients[(tmtd+1):ndose])
      }
    }
    
    if (!is.null(res$totaltime)){
      totaltime <- totaltime + res$totaltime
    }
    
    MTDsel <- MTDsel + sum(res$MTD==tmtd)
    MTDallo <- MTDallo + res$npatients[tmtd]
    sumTox <- sumTox + sum(res$ntox)
    sumPatients <- sumPatients + sum(res$npatients)
    nPatients <- nPatients + res$npatients
    nTox <- res$ntox + nTox
  }
  selpercent <- Perc/nsimu
  MTDsel <- MTDsel/nsimu
  MTDallo <- MTDallo/sumPatients
  oversel <- oversel/nsimu
  overallo <- overallo/sumPatients
  averDLT <- sumTox/sumPatients
  errStop <- nsimu-nonErrStops
  
  if (design == 'CFO' || design == 'aCFO' || design == 'rCFO' ){
    out <- list(p.true=p.true, selpercent=selpercent, npatients=nPatients/nsimu, ntox=nTox/nsimu, 
                MTDsel=MTDsel, MTDallo=MTDallo, oversel=oversel, 
                overallo=overallo, averDLT=averDLT, percentstop=errStop/nsimu,
                simu.setup = data.frame(target = target,ncohort = ncohort, 
                                        cohortsize = cohortsize, design = design, nsimu = nsimu))
  }else{
    out <- list(p.true=p.true, selpercent=selpercent, npatients=nPatients/nsimu, ntox=nTox/nsimu, 
                MTDsel=MTDsel, MTDallo=MTDallo, oversel=oversel,
                overallo=overallo, averDLT=averDLT, averdur = totaltime/nsimu, percentstop=errStop/nsimu,
                simu.setup = data.frame(target = target,ncohort = ncohort, 
                                        cohortsize = cohortsize, design = design, nsimu = nsimu))
  }
  class(out) <- c("cfo_oc","cfo")
  return(out)
}

Try the CFO package in your browser

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

CFO documentation built on April 4, 2025, 2:34 a.m.