Implementing the EffTox Dose-Finding Design in the Matchpoint Trials

This tutorial complements the manuscript Implementing the EffTox Dose-Finding Design in the Matchpoint Trial (Brock et al.,in submission). Please consult the paper for the clinical background, the methodology details, and full explanation of the terminology.

Dose Transition Pathways

In this notebook, we illustrate the calculation of dose transition pathways (DTPs) in the seamless phase I/II dose-finding clinical trial, Matchpoint.


In [1]:
import numpy as np
from scipy.stats import norm

from clintrials.dosefinding.efftox import EffTox, LpNormCurve, efftox_dtp_detail
from clintrials.dosefinding.efficacytoxicity import dose_transition_pathways, print_dtps

In [2]:
real_doses = [7.5, 15, 30, 45]
trial_size = 30
cohort_size = 3
first_dose = 3
prior_tox_probs = (0.025, 0.05, 0.1, 0.25)
prior_eff_probs = (0.2, 0.3, 0.5, 0.6)
tox_cutoff = 0.40
eff_cutoff = 0.45
tox_certainty = 0.05
eff_certainty = 0.03

In [3]:
mu_t_mean, mu_t_sd = -5.4317, 2.7643
beta_t_mean, beta_t_sd = 3.1761, 2.7703
mu_e_mean, mu_e_sd = -0.8442, 1.9786
beta_e_1_mean, beta_e_1_sd = 1.9857, 1.9820
beta_e_2_mean, beta_e_2_sd = 0, 0.2
psi_mean, psi_sd = 0, 1
efftox_priors = [
    norm(loc=mu_t_mean, scale=mu_t_sd),
    norm(loc=beta_t_mean, scale=beta_t_sd),
    norm(loc=mu_e_mean, scale=mu_e_sd),
    norm(loc=beta_e_1_mean, scale=beta_e_1_sd),
    norm(loc=beta_e_2_mean, scale=beta_e_2_sd),
    norm(loc=psi_mean, scale=psi_sd),
    ]

The above parameters are explained in the manuscript.


In [4]:
hinge_points = [(0.4, 0), (1, 0.7), (0.5, 0.4)]
metric = LpNormCurve(hinge_points[0][0], hinge_points[1][1], hinge_points[2][0], hinge_points[2][1])

In [5]:
et = EffTox(real_doses, efftox_priors, tox_cutoff, eff_cutoff, tox_certainty, eff_certainty, metric, trial_size,
            first_dose)

The EffTox class is an object-oriented implementation of the trial design by Thall & Cook (Thall, P. F., & Cook, J. D. (2004). Dose-Finding Based on Efficacy-Toxicity Trade-Offs. Biometrics, 60(3), 684–693.)

Create patient outcomes 3TTT. Outcomes for a patient are represented by a three item tuple, where:

  • first item is 1-based dose-index give (i.e. 3 is dose-level 3);
  • second item is 1 if toxicity happened, else 0;
  • third item is 1 if efficacy happened, else 0.

Outcomes for several patients are represented as lists:


In [6]:
outcomes = [(3, 1, 0), (3, 1, 0), (3, 1, 0)]

In [7]:
np.random.seed(123)
et.update(outcomes)


Out[7]:
2

The next dose recommended is 2.

Calculate DTPs for the next cohort of three patients. n=100,000 will give a fairly quick and dirty estimate of the DTPs.


In [8]:
np.random.seed(123)
dtps = dose_transition_pathways(et, next_dose=2, cohort_sizes=[3], cohort_number=2, 
                                cases_already_observed=outcomes, custom_output_func=efftox_dtp_detail,
                                n=10**5)

In [9]:
print_dtps(dtps)


2NNN -> Dose 3, Superiority=0.11 * tentative *
2NNE -> Dose 1, Superiority=0.57 * tentative *
2NNT -> Dose -1, Superiority=nan
2NNB -> Dose 1, Superiority=0.47 * tentative *
2NEE -> Dose 1, Superiority=0.64
2NET -> Dose 1, Superiority=0.58 * tentative *
2NEB -> Dose 1, Superiority=0.69
2NTT -> Dose -1, Superiority=nan
2NTB -> Dose 1, Superiority=0.6
2NBB -> Dose 1, Superiority=0.86
2EEE -> Dose 1, Superiority=0.86
2EET -> Dose 1, Superiority=0.66
2EEB -> Dose 1, Superiority=0.9
2ETT -> Dose 1, Superiority=0.7
2ETB -> Dose 1, Superiority=0.81
2EBB -> Dose 1, Superiority=0.9
2TTT -> Dose -1, Superiority=nan
2TTB -> Dose 1, Superiority=0.65
2TBB -> Dose 1, Superiority=0.8
2BBB -> Dose 1, Superiority=0.87

These match Table 3 in the publication.

Superiority is the least of the three pairwise probabilities that the utility of the recommended dose is greater than each of the other doses. Ideally, we want Superiority to be high to be confident that the dose recommended genuinely has the highest utility. Scenarios where Superiority is less than 0.6 are marked as tentative. Dose ambivalence may be a problem here. Sometimes, the design may be restricted from recommending the best dose because of no-skipping rules and dose-inadmissibility.

Using n=1,000,000 will be appreciably slower for 20 paths, but will be more accurate:


In [10]:
dtps2 = dose_transition_pathways(et, next_dose=2, cohort_sizes=[3], cohort_number=2, 
                                 cases_already_observed=outcomes, custom_output_func=efftox_dtp_detail,
                                 n=10**6)

In [11]:
print_dtps(dtps2)


2NNN -> Dose 3, Superiority=0.16 * tentative *
2NNE -> Dose 1, Superiority=0.44 * tentative *
2NNT -> Dose -1, Superiority=nan
2NNB -> Dose 1, Superiority=0.52 * tentative *
2NEE -> Dose 1, Superiority=0.62
2NET -> Dose 1, Superiority=0.6
2NEB -> Dose 1, Superiority=0.68
2NTT -> Dose -1, Superiority=nan
2NTB -> Dose 1, Superiority=0.76
2NBB -> Dose 1, Superiority=0.81
2EEE -> Dose 1, Superiority=0.78
2EET -> Dose 1, Superiority=0.7
2EEB -> Dose 1, Superiority=0.84
2ETT -> Dose 1, Superiority=0.74
2ETB -> Dose 1, Superiority=0.82
2EBB -> Dose 1, Superiority=0.88
2TTT -> Dose -1, Superiority=nan
2TTB -> Dose 1, Superiority=0.74
2TBB -> Dose 1, Superiority=0.8
2BBB -> Dose 1, Superiority=0.89

Again, these match Table 3.

We use DTPs continuously in the running of the Matchpoint trial. They aid in planning, they help to overcome outcome ambiguity, they have highlighted hidden undesirable behaviour implicit in out parameter choices, etc.


In [ ]: