R/obd.select.R

Defines functions obd.select

Documented in obd.select

#' Optimal Biological Dose Selection
#'
#' @description
#' Selects the optimal biological dose (OBD) from a set of candidate doses by balancing
#' toxicity and efficacy considerations using various utility-based methods. The OBD is
#' the dose that optimizes the risk-benefit trade-off, typically used in oncology trials
#' where both safety and therapeutic effect must be considered simultaneously.
#'
#' @details
#' The function implements four different methods for OBD selection:
#'
#' \strong{Method 1: "utility.weighted"}
#' Uses a weighted utility function that combines toxicity and efficacy probabilities
#' with user-specified weights. Higher weight on w1 emphasizes efficacy, while higher
#' weight on w2 penalizes toxicity more heavily.
#'
#' \strong{Method 2: "utility.truncated.linear"}
#' Employs piecewise linear utility functions with truncation points for both toxicity
#' and efficacy. This method allows for more flexible modeling of the utility surface
#' with different slopes in different probability ranges.
#'
#' \strong{Method 3: "utility.scoring"}
#' Uses a discrete scoring system based on four possible outcomes: (no toxicity, no efficacy),
#' (no toxicity, efficacy), (toxicity, no efficacy), and (toxicity, efficacy).
#'
#' \strong{Method 4: "max.effprob"}
#' Maximizes efficacy probability among doses with acceptable toxicity profiles.
#' First identifies the maximum tolerated dose (MTD), then selects the dose with
#' highest efficacy probability among acceptable doses.
#'
#' The function only considers doses that meet both toxicity and efficacy stopping
#' criteria (controlled by stopT and stopE parameters).
#'
#' @usage
#' obd.select(
#'   probt, probe, method,
#'   phi, phi1, phi2,
#'   delta, delta1,
#'   tterm, eterm, stopT, stopE,
#'   w1, w2,
#'   plow.ast, pupp.ast,
#'   qlow.ast, qupp.ast,
#'   psi00, psi11)
#'
#' @param probt Numeric vector of estimated toxicity probabilities for each dose level.
#'   Values should be between 0 and 1.
#' @param probe Numeric vector of estimated efficacy probabilities for each dose level.
#'   Values should be between 0 and 1. Must have same length as \code{probt}.
#' @param method Character string specifying the method for OBD selection. Must be one of:
#'   \code{"utility.weighted"}, \code{"utility.truncated.linear"},
#'   \code{"utility.scoring"}, or \code{"max.effprob"}.
#' @param phi Target toxicity probability (used in "max.effprob" method).
#' @param phi1 Lower bound of acceptable toxicity probability range.
#'   Used for defining toxicity intervals in some methods.
#' @param phi2 Upper bound of acceptable toxicity probability range.
#'   Used as toxicity threshold in "utility.weighted" method.
#' @param delta Target efficacy probability.
#' @param delta1 Lower bound of acceptable efficacy probability range.
#' @param tterm Numeric vector of probabilities of meeting toxicity stopping criteria
#'   for each dose. Same length as \code{probt}.
#' @param eterm Numeric vector of probabilities of meeting efficacy stopping criteria
#'   for each dose. Same length as \code{probe}.
#' @param stopT Toxicity stopping threshold. Doses are excluded if
#'   \code{tterm < (1 - stopT)}.
#' @param stopE Efficacy stopping threshold. Doses are excluded if
#'   \code{eterm < (1 - stopE)}.
#' @param w1 Weight parameter for toxicity-efficacy trade-off in "utility.weighted" method.
#'   Higher values emphasize efficacy more heavily.
#' @param w2 Weight parameter for penalty imposed on toxic doses in "utility.weighted" method.
#'   Higher values penalize toxicity more heavily.
#' @param plow.ast Lower threshold of toxicity for the linear truncated utility function
#'   (used in "utility.truncated.linear" method).
#' @param pupp.ast Upper threshold of toxicity for the linear truncated utility function
#'   (used in "utility.truncated.linear" method).
#' @param qlow.ast Lower threshold of efficacy for the linear truncated utility function
#'   (used in "utility.truncated.linear" method).
#' @param qupp.ast Upper threshold of efficacy for the linear truncated utility function
#'   (used in "utility.truncated.linear" method).
#' @param psi00 Utility score for the outcome (toxicity=FALSE, efficacy=FALSE)
#'   in "utility.scoring" method.
#' @param psi11 Utility score for the outcome (toxicity=TRUE, efficacy=TRUE)
#'   in "utility.scoring" method.
#'
#' @return Integer value representing the index of the selected optimal biological dose.
#'   The returned value corresponds to the position in the input vectors \code{probt}
#'   and \code{probe}. If no dose meets the stopping criteria, the function behavior
#'   depends on the implementation of the underlying utility functions.
#'
#' @note
#' \itemize{
#'   \item All probability vectors (\code{probt}, \code{probe}, \code{tterm}, \code{eterm})
#'         must have the same length.
#'   \item Only doses that satisfy both \code{tterm >= (1-stopT)} and
#'         \code{eterm >= (1-stopE)} are considered as candidates.
#'   \item If multiple doses have the same maximum utility, the function returns
#'         the lowest dose index among them.
#'   \item Required parameters vary by method - ensure appropriate parameters are
#'         provided for the selected method.
#' }
#'
#' @examples
#' # Example 1: Using utility.weighted method
#' probt <- c(0.05, 0.15, 0.30, 0.50, 0.65)
#' probe <- c(0.10, 0.25, 0.45, 0.60, 0.55)
#' tterm <- c(0.95, 0.90, 0.85, 0.75, 0.60)
#' eterm <- c(0.90, 0.85, 0.80, 0.85, 0.70)
#'
#' obd_weighted <- obd.select(
#'   probt = probt, probe = probe,
#'   method = "utility.weighted",
#'   phi2 = 0.35, w1 = 1.0, w2 = 0.5,
#'   tterm = tterm, eterm = eterm,
#'   stopT = 0.10, stopE = 0.10
#' )
#'
#' # Example 2: Using max.effprob method
#' obd_maxeff <- obd.select(
#'   probt = probt, probe = probe,
#'   method = "max.effprob",
#'   phi = 0.30,
#'   tterm = tterm, eterm = eterm,
#'   stopT = 0.10, stopE = 0.10
#' )
#'
#' # Example 3: Using utility.scoring method
#' obd_scoring <- obd.select(
#'   probt = probt, probe = probe,
#'   method = "utility.scoring",
#'   psi00 = 0, psi11 = 60,
#'   tterm = tterm, eterm = eterm,
#'   stopT = 0.10, stopE = 0.10
#' )
#'
#' @references
#' \itemize{
#'   \item Thall, P. F., & Cook, J. D. (2004). Dose-finding based on efficacy-toxicity
#'         trade-offs. \emph{Biometrics}, 60(3), 684-693.
#'   \item Yin, G., Li, Y., & Ji, Y. (2006). Bayesian dose-finding in phase I/II
#'         clinical trials using toxicity and efficacy odds ratios.
#'         \emph{Biometrics}, 62(3), 777-784.
#'   \item Houede, N., Thall, P. F., Nguyen, H., Paoletti, X., & Kramar, A. (2010).
#'         Utility-based optimization of combination therapy using ordinal toxicity
#'         and efficacy in phase I/II trials. \emph{Biometrics}, 66(2), 532-540.
#'   \item Zhou, H., Murray, T. A., Pan, H., & Yuan, Y. (2018). Comparative review of
#'         novel model-assisted designs for phase I clinical trials.
#'         \emph{Statistics in Medicine}, 37(20), 2892-2922.
#' }
#'
#' @seealso
#' \code{\link{utility.weighted}}, \code{\link{utility.truncated.linear}},
#' \code{\link{utility.scoring}} for the underlying utility functions.
#'
#' @export

obd.select <- function(
                probt, probe, method,
                phi, phi1, phi2, delta, delta1,
                tterm, eterm, stopT, stopE,
                w1, w2,
                plow.ast, pupp.ast, qlow.ast, qupp.ast,
                psi00, psi11)
{
  candose <- which((tterm>=(1-stopT))&(eterm>=(1-stopE)))

  if(method=="utility.weighted"){
    fu <- utility.weighted(probt=probt,probe=probe,
                           w1=w1,w2=w2,tox.upper=phi2)
    fu.max <- which(fu==max(fu[candose]))
    re     <- min(intersect(fu.max,candose))

  }else if(method=="utility.truncated.linear"){
    fu <- utility.truncated.linear(probt=probt,probe=probe,
                                   tlow=plow.ast,tupp=pupp.ast,
                                   elow=qlow.ast,eupp=qupp.ast)
    fu.max <- which(fu==max(fu[candose]))
    re     <- min(intersect(fu.max,candose))

  }else if(method=="utility.scoring"){
    fu <- utility.scoring(probt=probt,probe=probe,
                          psi00=psi00,psi11=psi11)
    fu.max <- which(fu==max(fu[candose]))
    re     <- min(intersect(fu.max,candose))

  }else if(method=="max.effprob"){
    mdif <- min(abs(probt[candose]-phi))
    mtd  <- max(which(abs(probt-phi)==mdif))
    meff <- max(probe[intersect(candose,1:mtd)])
    deff <- which(probe==meff)
    re   <- min(intersect(candose,deff))
  }

  return(re)
}

Try the boinet package in your browser

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

boinet documentation built on June 27, 2025, 1:08 a.m.