The escalation package by Kristian Brock. Documentation is hosted at https://brockk.github.io/escalation/

Introduction

The toxicity probability interval (TPI) design was introduced by Ji, Li, and Bekele (2007). It is one of a series of dose-finding trial designs that works by partitioning the probability of toxicity into a set of intervals. These designs make dose-selection decisions that are determined by the interval in which the probability of toxicity for the current dose is believed to reside.

Summary of the TPI Design

Core to this design is a beta-binomial Bayesian conjugate model. For hyperparameters α\alpha and β\beta, let the probability of toxicity at dose ii be pip_i, with prior distribution

piBeta(α,β).p_i \sim Beta(\alpha, \beta).

If nin_i patients have been treated at dose ii, yielding xix_i toxicity events, the posterior distribution is

pi|dataBeta(α+xi,β+nixi). p_i | data \sim Beta(\alpha + x_{i}, \beta + n_{i} - x_{i}).

Using this distribution, let the standard deviation of pip_i be denoted by σi\sigma_i. The design seeks a dose with probability of toxicity close to some pre-specified target level, pTp_T. The entire range of possible values for pip_i can be broken up into the following intervals:

  • The underdosing interval (UI), defined as (0,pTK2σi)(0, p_{T} - K_{2} \sigma_{i});
  • The equivalence interval (EI), defined as (pTK2σi,pT+K1σi)(p_{T} - K_{2} \sigma_{i}, p_{T} + K_{1} \sigma_{i});
  • The overdosing interval (OI), defined as (pT+K1σi,1)(p_{T} + K_{1} \sigma_{i}, 1);

for pre-specified model constants, K1,K2K_1, K_2. These intervals are mutally-exclusive and mutually-exhaustive, meaning that every possible probability belongs to precisely one of them. In other words, these intervals form a partition of the probability space, (0,1)(0, 1).

Using the posterior distribution, we can calculate the three probabilities

pUI=Pr(piUI),pEI=Pr(piEI),pOI=Pr(piOI).p_{UI} = Pr(p_i \in \text{UI}), \enspace p_{EI} = Pr(p_i \in \text{EI}), \enspace p_{OI} = Pr(p_i \in \text{OI}).

By definition, pUI+pEI+pOI=1p_{UI} + p_{EI} + p_{OI} = 1. The logical action in the dose-finding trial depends on which of these three probabilities is the greatest. If pUI>pEI,pOIp_{UI} > p_{EI}, p_{OI}, then the current dose is likely an underdose, so our desire should be to escalate dose to i+1i+1. In contrast, if pOI>pUI,pEIp_{OI} > p_{UI}, p_{EI}, then the current dose is likely an overdose and we will want to de-escalate dose to i1i-1 for the next patient. If pEI>pUI,pOIp_{EI} > p_{UI}, p_{OI}, then the current dose is deemed sufficiently close to pTp_T and we will want to stay at dose-level ii.

Further to these rules regarding dose-selection, the following rule is used to avoid recommending dangerous doses. A dose is deemed inadmissible for being excessively toxic if

Pr(pi>pT|data)>ξ, Pr(p_{i} > p_{T} | data) > \xi,

for a certainty threshold, ξ\xi. If a dose is excluded by this rule, it should not be recommended by the model. Irrespective the probabilities pUI,pEI,pOIp_{UI}, p_{EI}, p_{OI}, the design will recommend to stay at dose ii rather than escalate to a dose previously identified as being inadmissible. Furthermore, the design will advocate stopping if the lowest dose is inferred to be inadmissible.

In their paper, the authors demonstrate acceptable operating performance using α=β=0.005\alpha = \beta = 0.005, K1=1K_{1} = 1, K2=1.5K_{2} = 1.5 and ξ=0.95\xi = 0.95. See Ji, Li, and Bekele (2007) and Ji and Yang (2017) for full details.

Implementation in escalation

To demonstrate the method, let us fit the design to a cohort of three patients treated at the first of five doses, one of whom experienced toxicity. For illustration, use the parameters chosen in Ji, Li, and Bekele (2007):

library(escalation)

model <- get_tpi(num_doses = 5, target = 0.3, alpha = 0.005, beta = 0.005, 
                 k1 = 1, k2 = 1.5, exclusion_certainty = 0.95)

fit <- model %>% fit('1NNT') 

The dose recommended for the next cohort is

fit %>% recommended_dose()
#> [1] 1

Unsurprisingly, the design does not advocate escalation. Importantly, the modest toxicity seen so far is not enough to render dose 1 inadmissible:

fit %>% dose_admissible()
#> [1] TRUE TRUE TRUE TRUE TRUE

Let us imagine that we treat another two cohorts at dose 1, and see no toxicity:

fit <- model %>% fit('1NNT 1NNN 1NNN') 

Now, the design is happy to escalate:

fit
#> Patient-level data:
#> # A tibble: 9 × 5
#>   Patient Cohort  Dose   Tox Weight
#>     <int>  <int> <int> <int>  <dbl>
#> 1       1      1     1     0      1
#> 2       2      1     1     0      1
#> 3       3      1     1     1      1
#> 4       4      2     1     0      1
#> 5       5      2     1     0      1
#> 6       6      2     1     0      1
#> 7       7      3     1     0      1
#> 8       8      3     1     0      1
#> 9       9      3     1     0      1
#> 
#> Dose-level data:
#> Warning: `...` must be empty in `format.tbl()`
#> Caused by error in `format_tbl()`:
#> ! `...` must be empty.
#>  Problematic argument:
#>  digits = 3
#> # A tibble: 6 × 8
#>   dose     tox     n empiric_tox_rate mean_prob_tox median_prob_tox admissible
#>   <ord>  <dbl> <dbl>            <dbl>         <dbl>           <dbl> <lgl>     
#> 1 NoDose     0     0            0             0                0    TRUE      
#> 2 1          1     9            0.111         0.112            0.08 TRUE      
#> 3 2          0     0          NaN             0.5             NA    TRUE      
#> 4 3          0     0          NaN             0.5             NA    TRUE      
#> 5 4          0     0          NaN             0.5             NA    TRUE      
#> 6 5          0     0          NaN             0.5             NA    TRUE      
#> # ℹ 1 more variable: recommended <lgl>
#> 
#> The model targets a toxicity level of 0.3.
#> The model advocates continuing at dose 2.

Let us imagine, however, that dose 2 is surprisingly toxic, yielding three toxicities:

fit <- model %>% fit('1NNT 1NNN 1NNN 2TTT') 

Despite the low sample size, the statistical model believes that dose 2 is excessively toxic:

fit %>% prob_tox_exceeds(threshold = 0.25)
#> [1] 0.1007690 0.9999679        NA        NA        NA

and thus inadmissible:

fit %>% dose_admissible()
#> [1]  TRUE FALSE FALSE FALSE FALSE

Note that since dose 2 is believed to be inadmissible, the assumption of monotonically increasing toxicity means that the doses higher than dose 2 are excessively toxic too.

Final dose selection

TPI, like some other designs, selects the final dose differently to how it selects doses mid-trial. To achieve this in escalation, we need an extra selector that will kick-in when the parent selector(s) have selected a non-NA dose but expressed continue == FALSE, i.e. signaled the trial ends now but we are interested in a dose. If used, it will almost surely come last in the selector chain:

model <- get_tpi(num_doses = 5, target = 0.25, k1 = 1, k2 = 1.5, 
                 exclusion_certainty = 0.95) %>%
  stop_at_n(n = 12) %>%
  select_tpi_mtd(exclusion_certainty = 0.95)

outcomes <- '1NNN 2NTN 2NNN 3NTT'
model %>% fit(outcomes) %>% recommended_dose()
#> [1] 2

In the above example, stop_at_n stopped the trial because the threshold sample size was met, and the underlying algorithm identified that at least one dose was worthy of selection. At this juncture, select_tpi_mtd took over and applied the isotonic regression on prob(tox) described by the authors. Whilst the underlying selector(s) were busy conducting the trial (continue == TRUE), select_tpi_mtd kept silent.

Dose paths

In Table 1 of their publication, Ji, Li, and Bekele (2007) list some model recommendations conditional on hypothesised numbers of toxicities in cohorts of varying size. We can use the get_dose_paths function, for instance, to calculate exhaustive model recommendations after a single cohort of three is evaluated at dose 2:

paths <- model %>% get_dose_paths(cohort_sizes = c(3), next_dose = 2)

library(dplyr)
as_tibble(paths) %>% select(outcomes, next_dose) %>% print(n = 100)
#> # A tibble: 5 × 2
#>   outcomes next_dose
#>   <chr>        <dbl>
#> 1 ""               2
#> 2 "NNN"            3
#> 3 "NNT"            2
#> 4 "NTT"            1
#> 5 "TTT"            1

This table confirms the advice following a cohort of three to de-escalate if 2 or 3 toxicities are seen, to escalate if no toxicity is seen, otherwise to remain. Note that the recommendations would actually have been the same if next_dose = 3 or next_dose = 4. In this five-dose setting, they would naturally have been slightly different if next_dose = 1 or next_dose = 5 because we cannot de-escalate below dose 1 or escalate above dose 5.

We can easily visualise those paths with:

For more information on working with dose-paths, refer to the dose-paths vignette.

Simulation

Ji, Li, and Bekele (2007) present simulations in their Table 2, comparing the performance of their TPI method to other designs. We can use the simulate_trials function to reproduce the operating characteristics.

Their example concerns a clinical trial of eight doses that targets 25% toxicity. We must respecify the model object to reflect this. They also elect to limit the trial to a sample size of n=30n=30:

model <- get_tpi(num_doses = 8, target = 0.25, k1 = 1, k2 = 1.5,
                 exclusion_certainty = 0.95) %>%
  stop_at_n(n = 30)

For the sake of speed, we will run just fifty iterations:

num_sims <- 50

In real life, however, we would naturally run many thousands of iterations. Their scenario 1 assumes true probability of toxicity:

sc1 <- c(0.05, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95)

at which the simulated behaviour is:

set.seed(123)
sims <- model %>%
  simulate_trials(num_sims = num_sims, true_prob_tox = sc1, next_dose = 1)

sims
#> Number of iterations: 50 
#> 
#> Number of doses: 8 
#> 
#> True probability of toxicity:
#>    1    2    3    4    5    6    7    8 
#> 0.05 0.25 0.50 0.60 0.70 0.80 0.90 0.95 
#> 
#> Probability of recommendation:
#> NoDose      1      2      3      4      5      6      7      8 
#>   0.00   0.20   0.72   0.06   0.02   0.00   0.00   0.00   0.00 
#> 
#> Probability of administration:
#>     1     2     3     4     5     6     7     8 
#> 0.246 0.600 0.144 0.010 0.000 0.000 0.000 0.000 
#> 
#> Sample size:
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>      30      30      30      30      30      30 
#> 
#> Total toxicities:
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     5.0     6.0     7.0     7.4     8.0    11.0 
#> 
#> Trial duration:
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>   18.55   26.62   29.19   29.81   31.98   45.52

This reproduces their finding that dose 2 is overwhelmingly likely to be recommended, and that the sample size is virtually guaranteed to be 30, i.e. early stopping is unlikely.

For more information on running dose-finding simulations, refer to the simulation vignette.

References

Ji, Yuan, Yisheng Li, and B. Nebiyou Bekele. 2007. Dose-finding in phase I clinical trials based on toxicity probability intervals.” Clinical Trials 4 (3): 235–44. https://doi.org/10.1177/1740774507079442.
Ji, Yuan, and Shengjie Yang. 2017. On the Interval-Based Dose-Finding Designs,” 1–26. https://arxiv.org/abs/1706.03277.