Guiding Oncology Dose-Escalation Trials

Andrew Bean, Sebastian Weber, Lukas Widmer

2022-08-25

Introduction

The OncoBayes2 package provides flexible functions for Bayesian meta-analytic modeling of the incidence of Dose Limiting Toxicities (DLTs) by dose level, under treatment regimes involving any number of combination partners. Such models may be used to ensure patient safety during trial conduct by supporting dose-escalation decisions. In addition, the model can support estimation of the Maximum Tolerated Dose (MTD) in adaptive Bayesian dose-escalation designs.

Whereas traditional dose escalation designs, such as the 3+3 design, base the dosing decisions on predefined rules about the number of DLTs in the last one or two cohorts at the current dose, model-based designs such as those using Bayesian Logistic Regression Models (BLRMs) endeavor to model the dose-toxicity relationship as a continuous curve, and allow the model to guide dosing decisions. In this way, all available data contributes to the dosing decisions. Furthermore, extensions to the BLRM approach can support inclusion of available additional data on the compound(s) involved. The additional data can be either historical data collected prior trial conduct or concurrent data, which is collected during trial conduct in the context of another trial/context.

The package supports incorporation of additional data through a Meta-Analytic-Combined (MAC) framework [1]. Within the MAC model the heterogeneous sources of data are assigned to groups and information is shared across groups through a hierarchical model structure. For any given group this leads to borrowing strength from all other groups while discounting the information from other groups. The amount of discounting (or down-weighting) is determined by the heterogeneity. A group is commonly defined to be a trial, but that must not necessarily hold.

The key assumption of the hierarchical model is the exchangability assumption between the groups. There are two independent mechanisms in the package which aim at relaxing the exchangability assumption:

Both techniques are rather advanced and are not discussed further in this introduction.

In the following we illustrate first a very common use case of historical information only and then consider concurrent data in addition. In particular, we will discuss a trial evaluating a combination of two drugs whenever historical information is available on each drug individually from separate trials. This example will be expanded by using in addition concurrent data on one of the drugs and on their combination.

Note on terminology: While in the literature (see [1], [2], and [4]) the term stratum refers to a trial commonly, OncoBayes2 deviates here and uses the term group instead. This is more in line with hierarchical modeling terminology. The term stratum is used to define a higher level grouping structure. That is, every group is assigned to a single stratum within OncoBayes2. This higher level grouping (groups of groups) is necessary whenever differential discounting is used. By convention OncoBayes2 assigns any group to the stratum “all” whenever no stratum is assigned for a group.

## Load involved packages
library(dplyr)   ## for mutate
library(tidyr)   ## defines expand_grid
library(tibble)  ## for tibbles
library(ggplot2) ## for plotting

Example use-case: Dual combination trial with historical information

Consider the application described in Section 3.2 of [1], in which the risk of DLT is to be studied as a function of dose for two drugs, drug A and drug B. Historical information on the toxicity profiles of these two drugs is available from single agent trials trial_A and trial_B. The historical data for this example is available in an internal data set.

kable(hist_combo2)
group_id drug_A drug_B num_patients num_toxicities cohort_time
trial_A 3.0 0.0 3 0 0
trial_A 4.5 0.0 3 0 0
trial_A 6.0 0.0 6 0 0
trial_A 8.0 0.0 3 2 0
trial_B 0.0 33.3 3 0 0
trial_B 0.0 50.0 3 0 0
trial_B 0.0 100.0 4 0 0
trial_B 0.0 200.0 9 0 0
trial_B 0.0 400.0 15 0 0
trial_B 0.0 800.0 20 2 0
trial_B 0.0 1120.0 17 4 0

The objective is to aid dosing and dose-escalation decisions in a future trial, trial_AB, in which the drugs will be combined. Additionally, another investigator-initiated trial IIT will study the same combination concurrently. Note that these as-yet-unobserved sources of data are included in the input data as unobserved factor levels. This mechanism allows us to specify a joint meta-analytic prior for all four sources of historical and concurrent data.

levels(hist_combo2$group_id)
## [1] "trial_A"  "trial_B"  "IIT"      "trial_AB"

However, we will first consider only the dual combination trial AB and it’s historical data and add concurrent data at a later stage.

Setting up the trial design

The function blrm_trial provides an object-oriented framework for operationalizing the dose-escalation trial design. This framework is intended as a convenient wrapper for the main model-fitting engine of the package, the blrm_exnex() function. The latter allows additional flexibility for specifying the functional form of the model, but blrm_trial covers the most common use-cases. This introductory vignette highlights blrm_trial in lieu of blrm_exnex; the reader is referred to the help-page of the function?blrm_exnex for more details.

One begins with blrm_trial by specifying three key design elements:

Information about the study drugs is encoded through a tibble as below. This includes the names of the study-drugs, the reference doses (see [3] or ?blrm_exnex to understand the role this choice plays in the model specification), the dosing units, and (optionally) the a priori expected DLT rate for each study drug given individually at the respective reference doses.

All design information for the study described in [1] is also included as built-in datasets, which are part of the OncoBayes2 package.

Drug info

drug_name dose_ref dose_unit reference_p_dlt
drug_A 6 mg 0.2
drug_B 1500 mg 0.2

Dose info

The provisional dose levels are specified as below. For conciseness, we limit the dose level of in these provisional doses.

group_id drug_A drug_B dose_id
trial_AB 3 0 27
trial_AB 3 400 28
trial_AB 3 800 30
trial_AB 6 0 35
trial_AB 6 400 36
trial_AB 6 800 38

Initializing a blrm_trial

Together with the data described in the previous section, these objects can be used to initialize a blrm_trial object.

Specifying the prior and fitting the model

At this point, the trial design has been initialized. However, in the absence of simplified_prior = TRUE, we have not yet specified the prior distribution for the dose-toxicity model.

OncoBayes2 provides two methods for completing the model specification:

  1. Use simplified_prior = TRUE, which employs a package-default prior distribution, subject to a small number of optional arguments controlling the details.

  2. Provide a full prior specification to be passed to the blrm_exnex function.

For simplicity and conciseness purposes, here we use method #1, which is not recommended for actual trials as the prior should be chosen deliberately and there is no guarantee that the simplified prior will remain stable across releases of the package. See ?'example-combo2_trial' for an example of #2. The below choice of prior broadly follows the case study in [4], although we slightly deviate from the model in [4] by a different reference dose and mean reference DLT rate.

To employ the simplified prior, and fit the model with MCMC:

Now, the object combo2_trial_start contains the posterior model fit at the start of the trial, in addition to the trial design details. Next we highlight the main methods for extracting relevant information from it.

Summary of prior specification

The function prior_summary provides a facility for printing, in a readable format, a summary of the prior specification.

Summary of posterior

The main target of inference is generally the probability of DLT at a selection of provisional dose levels. To obtain these summaries for the provisional doses specified previously, we simply write:

group_id drug_A drug_B dose_id stratum_id mean sd 2.5% 50% 97.5% prob_underdose prob_target prob_overdose ewoc_ok
trial_AB 3 0 27 all 0.07 0.07 0.00 0.04 0.26 0.91 0.08 0.01 TRUE
trial_AB 3 400 28 all 0.11 0.09 0.01 0.08 0.36 0.80 0.17 0.03 TRUE
trial_AB 3 800 30 all 0.19 0.13 0.03 0.15 0.54 0.54 0.33 0.14 TRUE
trial_AB 6 0 35 all 0.16 0.11 0.02 0.14 0.46 0.59 0.33 0.08 TRUE
trial_AB 6 400 36 all 0.21 0.15 0.03 0.17 0.60 0.47 0.33 0.20 TRUE
trial_AB 6 800 38 all 0.29 0.22 0.02 0.23 0.80 0.35 0.29 0.36 FALSE

Such summaries may be used to assess which combination doses have unacceptable high risk of toxicity. For example, according to the escalation with overdose control (EWOC) design criteria [3], one would compute the posterior probability that each dose is excessively toxic (column prob_overdose; note that the definition of “excessively toxic” is encoded in the blrm_trial object through the interval_prob argument), and take as eligible doses only those where this probability does not exceed 25% (column ewoc_ok).

Posterior accuracy of EWOC estimation

Since the posterior is represented with a large sample of the target density, any estimate derived from it is subject to finite sampling error. The sampling error is determined by the posterior sample size and the quality of the used Markov chain Monte Carlo (MCMC). Hence, it is required to ensure that the MCMC chains have converged and that the number of samples representing the posterior is large enough to estimate desired quantities of interest with sufficient accuracy. The OncoBayes2 package automatically warns in case of non-convergence as indicated by the Rhat diagnostic [5]. All model parameters must have an Rhat of less than \(1.1\) (values much larger than \(1.0\) indicate non-convergence).

As the primary objective for a BLRM is to determine a safe set of doses via estimation of EWOC, the key quantities defining EWOC are monitored for convergence and sufficient accuracy for each pre-defined dose as well. These diagnostics can be obtained for the pre-defined set of doses via the ewoc_check summary routine as:

group_id drug_A drug_B dose_id stratum_id prob_overdose_est prob_overdose_stat prob_overdose_mcse prob_overdose_ess prob_overdose_rhat
trial_AB 3 0 27 all 0.090 -100.199 0.002 2164.255 1.000
trial_AB 3 400 28 all 0.142 -58.042 0.003 1990.485 1.000
trial_AB 3 800 30 all 0.252 -15.260 0.005 2190.385 1.002
trial_AB 6 0 35 all 0.217 -25.267 0.004 2301.301 1.000
trial_AB 6 400 36 all 0.288 -7.149 0.006 2167.891 1.002
trial_AB 6 800 38 all 0.423 7.402 0.013 2115.179 1.002

For the standard EWOC criterion, the prob_overdose_est column contains the 75% quantile of the posterior DLT probability, which must be smaller than 33%. The prob_overdose_stat column is centered by 33% and standardized with the Monte-Carlo standard error (mcse). Therefore, negative values correspond to safe doses and since the quantity is approximately distributed as a standard normal random variate, the statistic can be compared with quantiles of the standard normal distribution. OncoBayes2 will warn for an imprecise EWOC estimate whenever the statistic is within the range of the central 95% interval of \((-1.96,1.96)\). Whenever this occurs it can be useful to increase the number of iterations in order to decrease the mcse, which scales with the inverse of the square root of the MC ess. The MC ess is the number of independent samples the posterior corresponds to (recall that MCMC results in correlated samples). For more information please refer to the help of the summary.blrm_trial function (see help("blrm_trial", help_type="summary")).

We can see that for the pre-defined doses of the trial the EWOC decision can be determined with more than enough accuracy given that the statistic closest to \(0\) is \(-7.15\).

Posterior predictive summaries

The BLRM allows a principled approach to predicting the number of DLTs that may be observed in a future cohort. This may be a key estimand for understanding and limiting the toxicity risk to patients. For example, suppose a candidate starting dose for the new trial trial_AB is 3 mg of drug A + 400 mg of drug B. We may wish to check that at this dose, the predictive probability of 2 or more DLTs out of an initial cohort of 3 to 6 patients is sufficiently low.

num_patients (-1,0] (0,1] (1,6]
3 0.728 0.223 0.049
4 0.665 0.252 0.083
5 0.611 0.271 0.118
6 0.564 0.283 0.153

This tells us that for the initial cohort, according to the model, the chance of two or more patients developing DLTs ranges from 4.9% to 15.3%, depending on the number of patients enrolled.

Updating the model with new data

Dose-escalation designs are adaptive in nature, as dosing decisions are made after each sequential cohort. The model must be updated with the accrued data for each dose escalation decision point. If a new cohort of patients is observed, say:

One can update the model to incorporate this new information using update() with add_data equal to the new cohort:

This yields a new blrm_trial object with updated data and posterior summaries. Obtaining the summaries for the pre-planned provisional doses is then again straightforward:

group_id drug_A drug_B dose_id stratum_id mean sd 2.5% 50% 97.5% prob_underdose prob_target prob_overdose ewoc_ok
trial_AB 3 0 27 all 0.08 0.07 0.00 0.06 0.26 0.89 0.10 0.01 TRUE
trial_AB 3 400 28 all 0.13 0.08 0.03 0.11 0.33 0.71 0.26 0.03 TRUE
trial_AB 3 800 30 all 0.22 0.13 0.04 0.19 0.53 0.39 0.43 0.18 TRUE
trial_AB 6 0 35 all 0.18 0.11 0.03 0.16 0.46 0.52 0.38 0.10 TRUE
trial_AB 6 400 36 all 0.24 0.15 0.04 0.21 0.61 0.35 0.39 0.26 FALSE
trial_AB 6 800 38 all 0.34 0.22 0.04 0.30 0.81 0.26 0.29 0.45 FALSE

In case posterior summaries are needed for doses other than the pre-planned ones, then this is possible using the newdata_prediction functionality, which allows to specify a different set of doses via the newdata argument:

group_id drug_A drug_B stratum_id dose_id mean sd 2.5% 50% 97.5% prob_underdose prob_target prob_overdose ewoc_ok
trial_AB 4.5 400 all NA 0.18 0.11 0.04 0.16 0.46 0.50 0.39 0.11 TRUE
trial_AB 4.5 600 all NA 0.23 0.14 0.04 0.20 0.58 0.39 0.39 0.22 TRUE
trial_AB 4.5 800 all NA 0.27 0.17 0.04 0.24 0.69 0.31 0.35 0.33 FALSE

Data scenarios

It may be of interest to test prospectively how this model responds in various scenarios for upcoming cohorts.

This can be done easily by again using update() with the add_data argument. In the code below, we explore 3 possible outcomes for a subsequent cohort enrolled at 3 mg drug A + 800 mg drug B, and review the model’s inference at adjacent doses.

Model inference for trial AB when varying hypothetical DLT scenarios for a cohort of size 3
Scenario drug_A drug_B mean sd 2.5% 50% 97.5% prob_underdose prob_target prob_overdose ewoc_ok
0/3 DLTs 3.0 600 0.13 0.08 0.03 0.11 0.31 0.72 0.26 0.02 TRUE
0/3 DLTs 3.0 800 0.16 0.10 0.04 0.14 0.39 0.57 0.37 0.06 TRUE
0/3 DLTs 4.5 600 0.17 0.11 0.03 0.15 0.44 0.55 0.36 0.09 TRUE
0/3 DLTs 4.5 800 0.21 0.13 0.03 0.18 0.53 0.45 0.38 0.17 TRUE
1/3 DLTs 3.0 600 0.19 0.09 0.05 0.17 0.42 0.44 0.48 0.08 TRUE
1/3 DLTs 3.0 800 0.24 0.12 0.06 0.22 0.52 0.27 0.52 0.21 TRUE
1/3 DLTs 4.5 600 0.25 0.13 0.06 0.23 0.56 0.27 0.50 0.23 TRUE
1/3 DLTs 4.5 800 0.31 0.16 0.06 0.28 0.67 0.18 0.43 0.39 FALSE
2/3 DLTs 3.0 600 0.25 0.11 0.08 0.24 0.49 0.22 0.56 0.22 TRUE
2/3 DLTs 3.0 800 0.33 0.13 0.11 0.31 0.60 0.09 0.46 0.45 FALSE
2/3 DLTs 4.5 600 0.34 0.15 0.10 0.32 0.66 0.11 0.42 0.48 FALSE
2/3 DLTs 4.5 800 0.42 0.17 0.12 0.41 0.76 0.06 0.28 0.66 FALSE

Continuation of example: Using concurrent data

In the example of [1], at the time of completion of the first stage of trial_AB, the following additional data was observed.

trial_AB_data <- filter(codata_combo2, group_id == "trial_AB", cohort_time==1)
kable(trial_AB_data)
group_id drug_A drug_B num_patients num_toxicities cohort_time
trial_AB 3 400 3 0 1
trial_AB 3 800 3 1 1
trial_AB 6 400 3 1 1

These data are easily incorporated into the model using another call to update, as below.

combo2_trial_histdata <- update(combo2_trial_start, add_data = trial_AB_data)

However, during the first stage of trial_AB, the trial_A studying drug A did continue and collected more data on the drug A dose-toxicity relationship:

trial_A_codata <- filter(codata_combo2, group_id == "trial_A", cohort_time==1)
kable(trial_A_codata)
group_id drug_A drug_B num_patients num_toxicities cohort_time
trial_A 3.0 0 3 0 1
trial_A 4.5 0 6 0 1
trial_A 6.0 0 11 0 1
trial_A 8.0 0 3 2 1

Wthin the MAC framework we may simply add the concurrent data to our overall model which yields refined predictions for future cohorts.

combo2_trial_codata <- update(combo2_trial_histdata, add_data = trial_A_codata)

To compare the effect of co-data in this case it is simplest to visualize the interval probabilities as predicted by the model for the different data constellations. Here we use the function plot_toxicity_intervals_stacked to explore the dose-toxicity relationship in a continuous manner in terms of the dose.

plot_toxicity_intervals_stacked(combo2_trial_histdata,
                                newdata=mutate(dose_info, dose_id=NULL, stratum_id="all"),
                                x = vars(drug_B),
                                group = vars(drug_A),
                                facet_args = list(ncol = 1)
) + ggtitle("Trial AB with historical data only")

plot_toxicity_intervals_stacked(combo2_trial_codata,
                                newdata=mutate(dose_info, dose_id=NULL, stratum_id="all"),
                                x = vars(drug_B),
                                group = vars(drug_A),
                                facet_args = list(ncol = 1)
) + ggtitle("Trial AB with historical and concurrent data on drug A")

As we can observe, the additional data on drug A moves the maximal admissible dose allowed by EWOC towards higher doses for drug B whenever drug A is 6 mg. This reflects that drug A has been observed to be relatively safe, since no DLT was observed for a number of doses.

In the example of [1], during the conduct of the second stage of the trial_AB an additional external data source from a new trial became available. This time it is stemming from another trial which is an investigator-initiated trial IIT of the same combination. Numerous toxicities were observed in this concurrent study as stage 2 of trial_AB.

trial_AB_stage_2_codata <- filter(codata_combo2, cohort_time==2)
kable(trial_AB_stage_2_codata)
group_id drug_A drug_B num_patients num_toxicities cohort_time
IIT 3.0 400 3 0 2
IIT 3.0 800 7 5 2
IIT 4.5 400 3 0 2
IIT 6.0 400 6 0 2
IIT 6.0 600 3 2 2
trial_AB 3.0 400 3 0 2
trial_AB 3.0 800 6 2 2
trial_AB 4.5 600 10 2 2
trial_AB 6.0 400 10 3 2

As before, through the MAC framework, these data can influence the model summaries for trial_AB. We leave it to the reader to explore the differences in the co-data (combined historical and concurrent data) vs the historical data only approach.

To conclude we present a graphical summary of the dose-toxicity relationship for the dual combination trial for the final data constellation. Note that we use the data option of update here to ensure that we use an entirely new dataset which includes all data collected; so this includes historical, trial and concurrent data:

combo2_trial_final <- update(combo2_trial_start, data = codata_combo2)

As final summary we consider the 75% quantile of the probability for a DLT at all dose combinations. Whenever the 75% quantile exceeds 33%, then the EWOC criterion is violated and the dose is too toxic.

grid_length <- 25

dose_info_plot_grid <- expand_grid(stratum_id = "all",
                                   group_id = "trial_AB",
                                   drug_A=seq(min(dose_info_combo2$drug_A), max(dose_info_combo2$drug_A), length.out=grid_length),
                                   drug_B=seq(min(dose_info_combo2$drug_B), max(dose_info_combo2$drug_B), length.out=grid_length))


dose_info_plot_grid_sum <- summary(combo2_trial_final,
                                   newdata=dose_info_plot_grid,
                                   prob=0.5)

ggplot(dose_info_plot_grid_sum, aes(drug_A, drug_B, z = !!as.name("75%"))) +
  geom_contour_filled(breaks=c(0, 0.1, 0.16, 0.33, 1)) +
  scale_fill_brewer("Quantile Range", type="div", palette = "RdBu", direction=-1) +
  ggtitle("DLT Probability 75% Quantile")

References

[1] Neuenschwander, B., Roychoudhury, S., & Schmidli, H. (2016). On the use of co-data in clinical trials. Statistics in Biopharmaceutical Research, 8(3), 345-354.

[2] Neuenschwander, B., Wandel, S., Roychoudhury, S., & Bailey, S. (2016). Robust exchangeability designs for early phase clinical trials with multiple strata. Pharmaceutical statistics, 15(2), 123-134.

[3] Neuenschwander, B., Branson, M., & Gsponer, T. (2008). Critical aspects of the Bayesian approach to phase I cancer trials. Statistics in medicine, 27(13), 2420-2439.

[4] Neuenschwander, B., Matano, A., Tang, Z., Roychoudhury, S., Wandel, S. Bailey, Stuart. (2014). A Bayesian Industry Approach to Phase I Combination Trials in Oncology. In Statistical methods in drug combination studies (Vol. 69). CRC Press.

[5] Vehtari, A., Gelman, A., Simpson, D., Carpenter, B., Bürkner, P. C. (2021). Rank-Normalization, Folding, and Localization: An Improved (\(\hat{R}\)) for Assessing Convergence of MCMC, Bayesian Analysis, 16 (2), 667–718. https://doi.org/10.1214/20-BA1221

Session Info

sessionInfo()
## R version 4.1.0 (2021-05-18)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.4 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_3.3.5    tibble_3.1.3     tidyr_1.1.3      dplyr_1.0.8     
## [5] knitr_1.33       OncoBayes2_0.8-7
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.7           prettyunits_1.1.1    ps_1.6.0            
##  [4] assertthat_0.2.1     digest_0.6.29        utf8_1.2.2          
##  [7] V8_3.4.2             R6_2.5.1             plyr_1.8.6          
## [10] ggridges_0.5.3       backports_1.2.1      stats4_4.1.0        
## [13] evaluate_0.14        highr_0.9            pillar_1.6.2        
## [16] rlang_1.0.1          curl_4.3.2           callr_3.7.0         
## [19] jquerylib_0.1.4      checkmate_2.0.0      rmarkdown_2.11      
## [22] labeling_0.4.2       stringr_1.4.0        loo_2.4.1           
## [25] munsell_0.5.0        compiler_4.1.0       xfun_0.25           
## [28] rstan_2.21.2         pkgconfig_2.0.3      pkgbuild_1.2.0      
## [31] rstantools_2.1.1     htmltools_0.5.2      tidyselect_1.1.1    
## [34] gridExtra_2.3        tensorA_0.36.2       codetools_0.2-18    
## [37] matrixStats_0.60.1   fansi_0.5.0          withr_2.4.3         
## [40] crayon_1.4.2         grid_4.1.0           distributional_0.2.2
## [43] jsonlite_1.7.2       gtable_0.3.0         lifecycle_1.0.1     
## [46] DBI_1.1.2            magrittr_2.0.1       posterior_1.3.0     
## [49] StanHeaders_2.21.0-7 scales_1.1.1         RcppParallel_5.1.4  
## [52] cli_3.1.1            stringi_1.7.3        farver_2.1.0        
## [55] bslib_0.3.1          ellipsis_0.3.2       generics_0.1.0      
## [58] vctrs_0.3.8          Formula_1.2-4        RColorBrewer_1.1-2  
## [61] tools_4.1.0          glue_1.6.1           purrr_0.3.4         
## [64] parallel_4.1.0       processx_3.5.2       abind_1.4-5         
## [67] fastmap_1.1.0        yaml_2.2.1           inline_0.3.19       
## [70] colorspace_2.0-2     isoband_0.2.5        bayesplot_1.8.1     
## [73] sass_0.4.0