Nothing
#' Operating Characteristics for Single-agent Trials
#'
#' This function generates the operating characteristics of the Keyboard design for
#' single-agent trials.
#'
#' @details
#' The operating characteristics of the Keyboard design are generated by
#' simulating trials under the pre-specified true toxicity probabilities of the
#' investigational doses.
#'
#' The Keyboard design defines a series of equal-width dosing intervals (or
#' "keys") to present the potential locations of the true toxicity of a dose
#' and guide the dose escalation or de-escalation between cohorts of patients.
#' The Keyboard design starts by specifying a proper dosing interval, referred
#' to as the "target key", based on which it forms a series of equally wide
#' keys that span the remainder of the range from 0 to 1.
#'
#' The Keyboard design relies on the posterior distribution of the toxicity
#' probability to guide dosage. To make the decision of dose escalation or
#' de-escalation, given the observed data at the current dose, we identify the
#' key that has the highest posterior probability, referred to as the
#' "strongest key". This key represents where the true dose-limiting toxicity
#' (DLT) rate of the current dose is most likely located. If the strongest key
#' is to the left of the target key, then we escalate the dose because
#' the data suggest that the current dose is most
#' likely to underdose; if the strongest key is to the
#' right of the target key, then we de-escalate the dose because the observed data
#' suggest that the current dose is likely to overdose; and if the strongest
#' key is the target key, then we retain the current dose because the observed data
#' support the notion that the current dose is most likely to be in the proper dosing
#' interval.
#' Graphically, the strongest key is the one with the largest area under the
#' posterior distribution curve of the DLT rate of the current dose.
#'
#' An attractive feature of the Keyboard design is that its dose escalation and
#' de-escalation rule can be tabulated before the onset of the trial. Thus,
#' when conducting the trial, no calculation or model fitting is needed, and we
#'need to count only the number of DLTs observed at the current dose;
#' the decision to escalate or de-escalate the dose is based on the pre-tabulated
#' decision rules.
#'
#' Given all observed data, the Keyboard design uses an isotonic regression to obtain an efficient statistical estimate of
#' the maximum tolerated dose (MTD) by utilizing the fact that toxicity
#' presumably increases with the dose.
#'
#' For patient safety, the following dose-elimination rule is evaluated after
#' each cohort:
#' if at least 3 patients have been treated at the given dose and
#' the observed data indicate that there is more than a 95\% chance that the
#' current dose is above the maximum tolerated dose (MTD), then we eliminate this dose and beyond from the trial to prevent exposing future patients to these
#' overly toxic doses. The probability threshold for elimination can be
#' specified with \code{cutoff.eli}. When a dose is eliminated, the design
#' recommends the next lower dose for treating the next patient.
#'
#' The Keyboard design has two built-in stopping rules:\cr
#' (1) stop the trial if the lowest dose is eliminated due to toxicity, and no
#' dose should be selected as the MTD; and \cr
#' (2) stop the trial and select the MTD if the number of patients treated at
#' the current dose reaches \code{n.earlystop}.
#'
#' The first stopping rule is a safety rule to protect patients from being exposed to overly toxic doses. The rationale for the second stopping
#' rule is that when enough (i.e., \code{n.earlystop})
#' patients are assigned to a dose, then the dose-finding algorithm has
#' approximately converged. Thus, we can stop the trial early and select the
#' MTD to minimize the sample size and the trial duration.
#'
#' For some applications, investigators may prefer a stricter stopping rule
#' than rule (1) for extra safety when the lowest dose is overly toxic.
#' This can be achieved by setting \code{extrasafe=TRUE}, which imposes the
#' following, stricter, safety stopping rule:\cr
#' Stop the trial if \cr
#' (i) the number of patients treated at the lowest dose \eqn{\ge 3}, and \cr
#' (ii) \deqn{Pr((toxicity rate of the lowest dose > target) | data)
#' > cutoff.eli - offset}
#' As a tradeoff, the strong stopping rule will decrease the MTD selection
#' percentage when the lowest dose is the true MTD.
#'
#' @param target The target dose-limiting toxicity (DLT) rate.
#' @param p.true A vector containing the true toxicity probabilities of the
#' investigational dose levels.
#' @param ncohort A scalar specifying the total number of cohorts in the trial.
#' @param cohortsize The number of patients in the cohort.\cr
#' The default is 1.
#' @param n.earlystop The early stopping parameter. If the number of patients
#' treated at the current dose reaches \code{n.earlystop}, then
#' stop the trial and select the MTD based on the observed
#' data.\cr
#' The default value is 100.
#' @param startdose The starting dose level for the trial.\cr
#' The default is 1.
#' @param marginL The difference between the target and the lower limit of the
#' "target key" (proper dosing interval) to be defined.\cr
#' The default is 0.05.
#' @param marginR The difference between the target and the upper limit of the
#' "target key" (proper dosing interval) to be defined.\cr
#' The default is 0.05.
#' @param cutoff.eli The cutoff value to eliminate an overly toxic dose and all
#' higher doses for safety.\cr
#' The default value is 0.95.
#' @param extrasafe Set \code{extrasafe=TRUE} to impose a stricter
#' stopping rule.\cr
#' The default is FALSE.
#' @param offset A small positive number (between 0 and 0.5) to control how
#' strict the stopping rule is when \code{extrasafe=TRUE}. A
#' larger value leads to a stricter stopping rule.\cr
#' The default value of 0.05 generally works well.
#' @param ntrial The total number of trials to be simulated. \cr
#' The default value is 1000.
#'
#' @return The function returns the operating characteristics of the Keyboard
#' design (single-agent) as a list, which includes: \cr
#' \enumerate{
#' \item the selection percentage at each dose level (\code{$selpercent}),\cr
#' \item the average number of patients treated at each dose level (\code{$npatients}),\cr
#' \item the average number of toxicities observed at each dose level (\code{$ntox}),\cr
#' \item the average number of toxicities (\code{$totaltox}),\cr
#' \item the average number of patients (\code{$totaln}),\cr
#' \item the percentage of early stopping due to toxicity without selecting the MTD (\code{$percentstop}),\cr
#' \item the risk of overdosing 60\% or more of patients (\code{$overdose60}),\cr
#' \item the risk of overdosing 80\% or more of patients (\code{$overdose80}),\cr
#' \item a data.frame object containing simulation parameters, such as \code{target, p.true,} etc. (\code{$simu.setup}).
#' }
#'
#' @import Rcpp methods graphics stats
#' @export
#'
#' @author Xiaomeng Yuan, Chen Li, Hongying Sun, Li Tang and Haitao Pan
#' @examples
#' \donttest{
#' ### Single-agent trial ###
#'
#' oc <- get.oc.kb(target=0.3, p.true=c(0.05, 0.15, 0.3, 0.45, 0.6),
#' ncohort=20, cohortsize=3, ntrial=1000)
#'
#' oc
#' }
#'
#'
#' @section Uses:
#' This function uses \code{\link{get.boundary.kb}} and
#' \code{\link{select.mtd.kb}}.
#'
#' @family single-agent functions
#'
#' @references
#'
#' Yan F, Mandrekar SJ, Yuan Y. Keyboard: A Novel Bayesian Toxicity Probability
#' Interval Design for Phase I Clinical Trials.
#' \emph{Clinical Cancer Research}. 2017; 23:3994-4003.
#' http://clincancerres.aacrjournals.org/content/23/15/3994.full-text.pdf
get.oc.kb <- function (target, p.true, ncohort, cohortsize,
n.earlystop = 100, startdose = 1,
marginL = 0.05, marginR = 0.05, cutoff.eli = 0.95,
extrasafe = FALSE, offset = 0.05, ntrial = 1000) {
if (offset >= 0.5) {
warning("Error: the offset is too large! \n")
return()
}
if (n.earlystop <= 6) {
warning("Warning: the value of n.earlystop is too low to ensure good operating characteristics.",
"Recommend n.earlystop = 9 to 18 \n")
return()
}
set.seed(6)
ndose = length(p.true)
npts = ncohort * cohortsize
Y = matrix(rep(0, ndose * ntrial), ncol = ndose)
N = matrix(rep(0, ndose * ntrial), ncol = ndose)
dselect = rep(0, ntrial)
temp = get.boundary.kb(target, ncohort, cohortsize)$full_boundary_tab
b.e = temp[2, ]
b.d = temp[3, ]
b.elim = temp[4, ]
for (trial in 1:ntrial) {
y <- rep(0, ndose)
n <- rep(0, ndose)
earlystop = 0
d = startdose
elimi = rep(0, ndose)
for (i in 1:ncohort) {
y[d] = y[d] + sum(runif(cohortsize) < p.true[d])
n[d] = n[d] + cohortsize
if (n[d] >= n.earlystop) {
break
}
if (!is.na(b.elim[n[d]])) {
if (n[d]>=3 && y[d] >= b.elim[n[d]]) {
elimi[d:ndose] = 1
if (d == 1) {
earlystop = 1
break
}
}
if (extrasafe) {
if (d == 1 && n[1] >= 3) {
if (1 - pbeta(target, y[1] + 1, n[1] - y[1] + 1) > cutoff.eli - offset) {
earlystop = 1
break
}
}
}
}
if (y[d] <= b.e[n[d]] && d != ndose) {
if (elimi[d + 1] == 0) {
d = d + 1
}
}
else if (y[d] >= b.d[n[d]] && d != 1) {
d = d - 1
}
else {
d = d
}
}
Y[trial, ] = y
N[trial, ] = n
if (earlystop == 1) {
dselect[trial] = 99
}
else {
dselect[trial] = select.mtd.kb(target, n, y)$MTD
}
}
selpercent = rep(0, ndose)
nptsdose = apply(N, 2, mean)
ntoxdose = apply(Y, 2, mean)
for (i in 1:ndose) {
selpercent[i] = sum(dselect == i)/ntrial * 100
}
if (length(which(p.true == target)) > 0) {
nmtd=nptsdose[p.true==target]
pcs=selpercent[p.true==target]
poor_dosing=mean(N[, p.true == target] < npts/ndose) * 100
if (which(p.true==target)== ndose-1) {
overdosing60=mean(N[,p.true>target]>0.6*npts)*100
overdosing70=mean(N[,p.true>target]>0.7*npts)*100
overdosing80=mean(N[,p.true>target]>0.8*npts)*100
}
else {
overdosing60=mean(rowSums(N[,p.true>target])>0.6*npts) * 100
overdosing70=mean(rowSums(N[,p.true>target])>0.7*npts) * 100
overdosing80=mean(rowSums(N[,p.true>target])>0.8*npts) * 100
}
out = list(selpercent=selpercent, npatients=nptsdose, ntox=ntoxdose,
totaltox=sum(Y)/ntrial, totaln=sum(N)/ntrial, percentstop=sum(dselect == 99)/ntrial*100,
overdose60=overdosing60, overdose80=overdosing80,
# poorallocation=mean(N[, p.true==target]<npts/ndose)*100,
simu.setup=data.frame(target=target, p.true=p.true, ncohort=ncohort,
cohortsize = cohortsize, startdose = startdose,
marginL = marginL, marginR = marginR,
cutoff.eli = cutoff.eli, extrasafe = extrasafe, offset = offset,
ntrial = ntrial, dose=1:ndose));
}
else {
out = list(selpercent=selpercent, npatients=nptsdose, ntox=ntoxdose,
totaltox=sum(Y)/ntrial, totaln=sum(N)/ntrial, percentstop=sum(dselect == 99)/ntrial*100,
simu.setup=data.frame(target=target, p.true=p.true, ncohort=ncohort,
cohortsize = cohortsize, startdose = startdose,
marginL = marginL, marginR = marginR,
cutoff.eli = cutoff.eli, extrasafe = extrasafe, offset = offset,
ntrial = ntrial, dose=1:ndose));
}
return(out)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.