--- title: "Backfill cohorts" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Backfill cohorts} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: vignettes.bib --- ## Introduction Backfilling cohorts are increasingly used in dose escalation studies, see @Barnett2023. The idea is that once a dose level has been tested and found to be safe, then while the dose escalation continues at higher dose levels, additional patients can be enrolled at the lower dose levels to gather more data on safety and/or efficacy. This is particularly useful in trials where patient recruitment is slow or when there is a need to gather more information on lower dose levels for regulatory or clinical reasons. Sometimes health authorities are asking to include the backfilling cohorts in the simulations to check the operating characteristics (especially PMDA). In addition, it is useful to get more precise operating characteristics for the overall trial by actually simulating the backfilling cohorts, when they are part of the actual clinical trial design. Hence, with `crmPack` it is now possible to include backfilling cohorts in the simulations. This vignette illustrates how to do this. ## Framework Conceptually the backfilling details are part of the design of a trial. Therefore the details are included via a dedicated slot `backfill` in the `Design` class. It contains an object of class `Backfill`, which captures these details via the following slots: - `cohort_size`: How large are the backfilling cohorts? Here an object of class `CohortSize` is used. - `max_size`: What is the overall maximum number of patients across all backfill cohorts? - `opening`: When can a backfill cohort be opened or recruited into? Here an object of a new `Opening` rules class is used. These rules can be based on e.g. dose level, current highest/maximum safe dose, efficacy responses, etc. - `recruitment`: How fast can patients be recruited into backfill cohorts? Here an object of a new `Recruitment` class is used. You can choose between immediate recruitment vs. a ratio compared to active dose escalation cohort (e.g. when active cohort has 3 patients recruited then here only 1/3 so 1 patient per cycle to backfill cohorts). - `priority`: When there are multiple open backfill cohorts, which one should be recruited into first? Here a simple string is used to specify the priority rule (first "lowest" dose, first "highest" dose, or "random"). Additional parts of the backfill cohort framework comprise: - The `Data` objects have a slot `backfilled` identifying whether each patient was backfilled or not, and the slot `response` identifies whether each patient had a response (1) or not (0). When these are not actively set by the user, then they default to no patients backfilled (all `FALSE`) and no response data available (all `NA`). - Some `Stopping` rules can optionally exclude backfill patients for assessing whether a trial can be stopped or not. This is currently the case for the `StoppingPatientsNearDose` rule. ## Algorithm For reference, we describe the algorithm used in the `simulate` method for `Design` objects to include backfilling cohorts: 1. Backfilling is only possible if the `opening` is *not* an `OpeningNone` object. Otherwise no backfilling is done in any of the trial simulations. 2. A queue of open backfill cohorts is maintained, and it is initialized as empty at the start of the trial. Likewise, the total number of backfill patients recruited so far is initialized to zero. 3. Throughout the trial simulations, for each patient next to the toxicity outcome, in addition also the binary response is simulated according to the assumed dose-response probability function (note that placebo patients can also have a response in principle, according to the placebo dose used in the dose grid). 4. After each dose escalation cohort is completed (i.e. all patients in the cohort have their toxicity and response outcomes observed), the backfill queue is updated: a. First, for all of the previous cohorts in the trial we check whether a backfill cohort can be opened, or can stay open (in case it was already open). This is done by applying the `opening` rules to each cohort in turn, in combination with comparing the cohort's dose with the current dose: It is a strict requirement that only cohorts with doses below the current dose can be open for backfilling (as the name suggests already). b. If a cohort is already in the backfill queue, we first check whether it is full already (maximum number of backfill patients in this particular queue has been reached). If we can open it and it is not full, then it is opened or remains open. If it is full, it remains closed. If we cannot open it, then it is closed (if it was open before), but still remains in the queue for record keeping. c. If a cohort is not yet in the backfill queue and we can open it, then it is added to the backfill queue. The maximum size of each backfill cohort is determined by the `size` rule. 5. After updating the backfill queue, the number of patients which can be enrolled in this dose cycle into backfill cohorts is determined according to the `recruitment` rule and the `max_size` limit for the overall number of backfill patients. If there are no backfill patients which can be recruited, then we continue with the next dose escalation cohort. 6. Assuming that we can recruit backfill patients, the cohorts from the backfill queue are sorted according to the `priority` option. Then patients are recruited into the open backfill cohorts in this order (filling up as many as possible patients for each cohort), until either all possible backfill patients for this cycle have been recruited, or all open backfill cohorts are full. 7. Backfill cohorts which have reached their maximum size are closed (i.e. they cannot be recruited into anymore), but they remain in the backfill queue for record keeping. 8. After recruiting any backfilling patients, the trial simulation continues with determining the maximum increment, fitting the regression model, selecting the next dose, checking stopping rules, as usual. Note that toxicity outcomes from backfill patients are deliberately included in the model fitting and dose selection, to reflect the actual data situation in the trial. ## Examples ### Standard components We start with the standard components of a CRM design, which are not changed by the backfilling framework - except that for the `StoppingPatientsNearDose` rule we can now choose whether to include backfill patients or not. ``` r library(crmPack) # Define the dose-grid. emptydata <- Data( doseGrid = c(0.1, 0.2, 0.5, 1, 3, 5, 10, 15, 20, 25, 40, 50, 60, 70, 80, 100) ) # Define the dose-toxicity model. model <- LogisticLogNormal( mean = c(-0.85, 1), cov = matrix(c(5, -0.5, -0.5, 5), nrow = 2), ref_dose = 56 ) # Choose the rule for selecting the next dose. myNextBest <- NextBestNCRM( target = c(0.2, 0.35), overdose = c(0.35, 1), max_overdose_prob = 0.25 ) # Choose the rule for stopping. myStopping1 <- StoppingMinCohorts(nCohorts = 3) myStopping2 <- StoppingTargetProb( target = c(0.2, 0.35), prob = 0.5 ) myStopping3 <- StoppingMinPatients(nPatients = 40) myStopping4 <- StoppingPatientsNearDose(nPatients = 10L, percentage = 30, include_backfill = FALSE) myStopping <- (myStopping1 & myStopping2 & myStopping4) | myStopping3 | StoppingMissingDose() # Choose the rule for dose increments. myIncrements <- IncrementsRelative( intervals = c(0, 20, 50), increments = c(1, 0.67, 0.33) ) ``` ### No backfill cohorts First we can define a design without backfill cohorts, which is the default behaviour when no backfill details are specified: ``` r design_no_backfill <- Design( model = model, nextBest = myNextBest, stopping = myStopping, increments = myIncrements, cohort_size = CohortSizeConst(3), data = emptydata, startingDose = 3 ) design_no_backfill@backfill ``` No backfill cohorts at all will be opened. > **Note:** This concise statement is produced by dedicated `knit_print` methods for the `Backfill` class, which help with the consistent reporting of `crmPack` design details in vignettes and reports. ### Simple backfill cohorts Let's continue with the simplest case of backfill cohorts. Here we define backfill cohorts of size 3 patients each, with a maximum of 12 backfill patients in total. Backfill cohorts can be opened at any time (i.e. immediately), and recruitment into backfill cohorts is immediate (i.e. as fast as dose escalation cohorts). When multiple backfill cohorts are open, then the lowest dose level is recruited into first. ``` r backfill_simple <- Backfill( cohort_size = CohortSizeConst(3), max_size = 12, opening = OpeningMinCohorts(min_cohorts = 1), recruitment = RecruitmentUnlimited(), priority = "lowest" ) backfill_simple ``` **Cohort size**: A constant size of 3 participants. **Opening rule**: If 1 or more cohorts have been treated in total. **Recruitment**: Unlimited recruitment of backfill patients is allowed. **Total number of backfill patients**: 12 backfill patients. **Priority of higher vs. lower dose backfill cohorts**: lowest dose. We can now add this backfill specification to the design: ``` r design_simple_backfill <- design_no_backfill design_simple_backfill@backfill <- backfill_simple ``` ### More complex backfill cohorts Now let's make things a bit more complex. We define a random number of patients for each backfill cohort, with a minimum of 1 patient and a maximum of 6 patients. Backfill cohorts can only be opened once at least 3 dose escalation cohorts have been completed. Note that this will lead to a delayed opening of cohorts 1, 2, and 3 for simultaneous backfilling. In addition, at least one response must have been observed at the cohort's dose level or a lower dose level before it could be opened for backfill. For example, if there is no response observed in cohort 1 at the lowest dose level, then it will not be opened for backfilling at all. We will be able to specify the assumed dose-response probability function in the `simulate` method call later. Recruitment into backfill cohorts is slower than dose escalation cohorts, with a ratio of 1 backfill patient for every 2 dose escalation patients. When multiple backfill cohorts are open, then the highest dose level is recruited into first. The total maximum number of backfill patients is set to 20. ``` r backfill_complex <- Backfill( cohort_size = CohortSizeRandom(min_size = 1, max_size = 6), opening = OpeningMinCohorts(min_cohorts = 3) & OpeningMinResponses( min_responses = 1, include_lower_doses = TRUE ), recruitment = RecruitmentRatio(ratio = 1 / 2), priority = "highest", max_size = 20 ) backfill_complex ``` **Cohort size**: A random cohort size drawn uniformly between 1 and 6 participants. **Opening rule**: If both of the following rules are satisfied: - If 3 or more cohorts have been treated in total. - If 1 or more responses have been observed at this dose or lower. **Recruitment**: Backfill patients are recruited at a ratio of 0.5 per patient in the main trial cohort. **Total number of backfill patients**: 20 backfill patients. **Priority of higher vs. lower dose backfill cohorts**: highest dose. Note how we can combine multiple opening rules using the `&` operator. This is analogous to how stopping rules can be combined. Indeed, it is also possible to use the `|` operator to combine opening rules with an "or" logic. Again we add this backfill specification to the design: ``` r design_complex_backfill <- design_no_backfill design_complex_backfill@backfill <- backfill_complex ``` ### Simulations with backfill cohorts Now we can run trial simulations including backfill cohorts. Here we illustrate this with both the simple and the complex backfill cohort designs defined above. For the complex backfill design we also need to specify the assumed dose-response probability function. Similarly as for the dose-toxicity function, it might be worth to consider a few different scenarios in practice. ``` r # Assumed dose-response probability function. mytruthResponse <- function(dose) { plogis(- 4 + 0.2 * dose) / 4 } curve(mytruthResponse(x), from = 0, to = max(emptydata@doseGrid), xlab = "Dose", ylab = "Probability of Response / Toxicity", main = "Assumed Functions", ylim = c(0, 1)) myTruth <- probFunction(design_simple_backfill@model, alpha0 = 3, alpha1 = 3) curve(myTruth(x), from = 0, to = max(emptydata@doseGrid), add = TRUE, col = "red") ```
plot of chunk unnamed-chunk-8
plot of chunk unnamed-chunk-10
plot of chunk unnamed-chunk-11