This is a core class in this package. It encapsulates that an object (e.g. a CRM model, a 3+3 model) is able to recommend doses, keep track of how many patients have been treated at what doses, what toxicity outcomes have been seen, and whether a trial should continue. It offers a consistent interface to many dose-finding methods, including CRM, TPI, mTPI, BOIN, EffTox, 3+3, and more.

Once you have a standardised interface, modularisation offers a powerful way to adorn dose-finding methods with extra desirable behaviour. selector objects can be daisy-chained togther using magrittr's pipe operator. For instance, the CRM fitting method in dfcrm is fantastic because it runs quickly and is simple to call. However, it does not recommend that a trial stops if a dose is too toxic or if n patients have already been treated at the recommended dose. Each of these behaviours can be bolted on via additional selectors. Furthermore, those behaviours and more can be bolted on to any dose selector because of the modular approach implemented in escalation. See Examples.

selector objects are obtained by calling the fit function on a selector_factory object. A selector_factory object is obtained by initially calling a function like get_dfcrm, get_three_plus_three or get_boin. Users may then add desired extra behaviour with subsequent calls to functions like stop_when_n_at_dose or stop_when_too_toxic.

The selector class also supports that an object will be able to perform inferential calculations on the rates of toxicity via functions like mean_prob_tox, median_prob_tox, and prob_tox_exceeds. However, naturally the sophistication of those calculations will vary by model implementation. For example, a full MCMC method will be able to quantify any probability you like by working with posterior samples. In contrast, a method like the crm function in dfcrm that uses the plug-in method to estimate posterior dose-toxicity curves cannot natively estimate the median probability of tox.

selector()

See also

Examples


# Start with a simple CRM model
skeleton <- c(0.05, 0.1, 0.25, 0.4, 0.6)
target <- 0.25
model1 <- get_dfcrm(skeleton = skeleton, target = target)

# Add a rule to stop when 9 patients are treated at the recommended dose
model2 <- get_dfcrm(skeleton = skeleton, target = target) %>%
  stop_when_n_at_dose(n = 9, dose = 'recommended')

# Add a rule to stop if toxicity rate at lowest dose likely exceeds target
model3 <- get_dfcrm(skeleton = skeleton, target = target) %>%
  stop_when_n_at_dose(n = 9, dose = 'recommended') %>%
  stop_when_too_toxic(dose = 1, tox_threshold = target, confidence = 0.5)

# We now have three CRM models that differ in their stopping behaviour.
# Let's fit each to some outcomes to see those differences:

outcomes <- '1NNN 2NTT 1NNT'
fit1 <- model1 %>% fit(outcomes)
fit2 <- model2 %>% fit(outcomes)
fit3 <- model3 %>% fit(outcomes)

fit1 %>% recommended_dose()
#> [1] 1
fit1 %>% continue()
#> [1] TRUE

fit2 %>% recommended_dose()
#> [1] 1
fit2 %>% continue()
#> [1] TRUE

fit3 %>% recommended_dose()
#> [1] NA
fit3 %>% continue()
#> [1] FALSE
# Already model3 wants to stop because of excessive toxicity.

# Let's carry on with models 1 and 2 by adding another cohort:

outcomes <- '1NNN 2NTT 1NNT 1NNN'
fit1 <- model1 %>% fit(outcomes)
fit2 <- model2 %>% fit(outcomes)

fit1 %>% recommended_dose()
#> [1] 1
fit1 %>% continue()
#> [1] TRUE

fit2 %>% recommended_dose()
#> [1] 1
fit2 %>% continue()
#> [1] FALSE

# Model1 wants to continue - in fact it will never stop.
# In contrast, model2 has seen 9 at dose 1 so, rather than suggest dose 1
# again, it suggests the trial should stop.

# For contrast, let us consider a BOIN model on the same outcomes
boin_fitter <- get_boin(num_doses = length(skeleton), target = target)
fit4 <- boin_fitter %>% fit(outcomes)
fit4 %>% recommended_dose()
#> [1] 2
fit4 %>% continue()
#> [1] TRUE

# Full selector interface:
fit <- fit2
fit %>% tox_target()
#> [1] 0.25
fit %>% num_patients()
#> [1] 12
fit %>% cohort()
#>  [1] 1 1 1 2 2 2 3 3 3 4 4 4
fit %>% doses_given()
#>  [1] 1 1 1 2 2 2 1 1 1 1 1 1
fit %>% tox()
#>  [1] 0 0 0 0 1 1 0 0 1 0 0 0
fit %>% weight()
#>  [1] 1 1 1 1 1 1 1 1 1 1 1 1
fit %>% num_tox()
#> [1] 3
fit %>% model_frame()
#> # A tibble: 12 × 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     0      1
#>  4       4      2     2     0      1
#>  5       5      2     2     1      1
#>  6       6      2     2     1      1
#>  7       7      3     1     0      1
#>  8       8      3     1     0      1
#>  9       9      3     1     1      1
#> 10      10      4     1     0      1
#> 11      11      4     1     0      1
#> 12      12      4     1     0      1
fit %>% num_doses()
#> [1] 5
fit %>% dose_indices()
#> [1] 1 2 3 4 5
fit %>% dose_strings()
#> [1] "1" "2" "3" "4" "5"
fit %>% recommended_dose()
#> [1] 1
fit %>% continue()
#> [1] FALSE
fit %>% n_at_dose()
#> [1] 9 3 0 0 0
fit %>% n_at_recommended_dose()
#> [1] 9
fit %>% is_randomising()
#> [1] FALSE
fit %>% prob_administer()
#>    1    2    3    4    5 
#> 0.75 0.25 0.00 0.00 0.00 
fit %>% tox_at_dose()
#> [1] 1 2 0 0 0
fit %>% empiric_tox_rate()
#> [1] 0.1111111 0.6666667       NaN       NaN       NaN
fit %>% mean_prob_tox()
#> [1] 0.2069561 0.2979677 0.4824122 0.6176620 0.7644430
fit %>% median_prob_tox()
#> [1] 0.2069561 0.2979677 0.4824122 0.6176620 0.7644430
fit %>% dose_admissible()
#> [1] TRUE TRUE TRUE TRUE TRUE
fit %>% prob_tox_quantile(0.9)
#> [1] 0.3649670 0.4608277 0.6272346 0.7346974 0.8420860
fit %>% prob_tox_exceeds(0.5)
#> [1] 0.009230096 0.054695783 0.442521884 0.851725376 0.996745367
fit %>% supports_sampling()
#> [1] TRUE
fit %>% prob_tox_samples()
#> # A tibble: 4,000 × 6
#>    .draw    `1`   `2`   `3`   `4`   `5`
#>    <chr>  <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1 1     0.224  0.317 0.501 0.633 0.775
#>  2 2     0.256  0.351 0.532 0.659 0.793
#>  3 3     0.181  0.269 0.454 0.593 0.747
#>  4 4     0.292  0.389 0.566 0.687 0.811
#>  5 5     0.220  0.312 0.496 0.629 0.772
#>  6 6     0.212  0.304 0.488 0.622 0.768
#>  7 7     0.0916 0.159 0.331 0.481 0.665
#>  8 8     0.329  0.426 0.598 0.712 0.827
#>  9 9     0.224  0.316 0.500 0.632 0.775
#> 10 10    0.303  0.399 0.575 0.694 0.816
#> # ℹ 3,990 more rows