Example With IPD Trial Data - Descem
Example With IPD Trial Data - Descem
Introduction
This document runs a discrete event simulation model using simulated individual patient data (IPD) to show how
the functions can be used to generate a model when IPD from a trial is available.
options(scipen = 999)
options(tibble.print_max = 50)
Model concept
The model is represented below. All patients start in the progression-free state and may move to the progressed
state. At any point in time they can die, depending on the risk of each disease stage. Patients may also
experience a disease event which accelerates progression.
Progression-accelerating event Progressed
Death
Progression-free
#Overall survival
km.est.OS <- survfit(Surv(AVAL_OS/365.25, Event_OS) ~ ARMCD, data = IPD) #KM curve
TTP.scale = as.numeric(TTP.fit$coef[2]),
TTP.shape = as.numeric(TTP.fit$coef[1]),
TTP.coef.int = as.numeric(TTP.fit$coef[3]), #Intervention effect
#Utilities
util.PFS = 0.6, #Utility while in progression-free state
util.PPS = 0.4, #Utility while in progressed state
disutil.PAE = -0.02, #One-off disutility of progression-accelerating event
#Costs
cost.drug.int = 85000, #Annual intervention cost
cost.drug.ref = 29000, #Annual cost of reference treatment
cost.admin.SC = 150, #Unit cost for each SC administration
cost.admin.oral = 300, #One-off cost for oral administration
cost.dm.PFS = 3000, #Annual disease-management cost in progression-free state
cost.dm.PPS = 5000, #Annual disease-management cost in progressed state
cost.ae.int = 2200, #Annual adverse event costs for intervention
cost.ae.ref = 1400, #Annual adverse event costs for reference treatment
)
#Define variables that do not change as we loop through interventions for a patient
common_pt_inputs <- add_item(
#Patient baseline characteristics
Sex = as.numeric(IPD[i,"SEX"]), #Record sex of individual patient. 0 = Female; 1 =Male
BLAge = as.numeric(IPD[i,"AGE"]), #Record patient age at baseline
#Define variables that change as we loop through treatments for each patient.
unique_pt_inputs <- add_item(
fl.int = 0, #Flag to determine if patient is on intervention. Initialized as 0, but w
ill be changed to current arm in the Start event.
fl.prog = 0, #Flag to determine if patient has progressed. All patients start progress
ion-free
fl.ontx = 1, #Flag to determine if patient is on treatment. All patients start on trea
tment
fl.PAE = 0, #Flag to determine if progression-accelerating event occurred
pfs.time = NA #Recording of time at progression
)
Events
Add Initial Events
We define now the possible events that can occur for the intervention and reference arm respectively using the
add_tte() function. Only patients in the intervention arm can have a treatment discontinuation, while patients in
both arms can have a progression, progression-accelerating and death event. The seed argument is being used in
the draw_tte() function which uses the i item to ensure that event times specific to each patient can be
replicated and updated at later time points.
init_event_list <-
#Events applicable to intervention
add_tte(trt="int",
evts = c("Start",
"TxDisc",
"Progression",
"PAE",
"Death"),
input={
Start <- 0
Progression <- draw_tte(1,'weibull',coef1=TTP.shape, coef2= TTP.scale + TTP.coef.in
t,seed = as.numeric(paste0(1,i,simulation)))
TxDisc <- Inf #Treatment discontinuation will occur at progression
Death <- min(draw_tte(1,'weibull',coef1=OS.shape, coef2= OS.scale + OS.coef.int, see
d = as.numeric(paste0(42,i,simulation))), nat.death) #Death occurs at earliest of diseas
e-related death or non-disease-related death
PAE <- draw_tte(1,'exp',coef1=-log(1-0.05)) #Occurrence of the progression-accelerat
ing event has a 5% probability for the intervention arm
}) %>%
Utilities
Utilities relating to progression-free and progressed states are common across arms.
util_ongoing <- add_util(evt = c("Start","TxDisc","Progression","Death","PAE"),
trt = c("int", "ref"), #common utility across arms
util = ifelse(fl.prog == 0, util.PFS, util.PPS)
)
Costs
Costs are defined specific to each arm due to differences in drug, administration and AE costs. A one-off cost for
oral administration of the reference arm is also applied.
cost_ongoing <-
#Drug costs are specific to each arm
add_cost(
evt = c("Start","TxDisc","Progression","Death","PAE"),
trt = "int",
cost = (cost.drug.int +
cost.admin.SC * 12 + #Intervention is administered once a month
cost.ae.int) * fl.ontx +
ifelse(fl.prog == 0,cost.dm.PFS,cost.dm.PPS)
) %>%
add_cost(
evt = c("Start","TxDisc","Progression","Death","PAE"),
trt = "ref",
cost = cost.drug.ref + cost.ae.ref + #No ongoing administration cost as reference tr
eatment is oral
ifelse(fl.prog == 0,cost.dm.PFS,cost.dm.PPS)
)
#One-off cost only applies to oral administration of reference treatment, applied at sta
rt of treatment
cost_instant <- add_cost(
evt = "Start",
trt = "ref",
cost = cost.admin.oral
)
Model
Model Execution
The model is executed with the event reactions and inputs previously defined for each patient in the simulated
data set.
#Logic is: per patient, per intervention, per event, react to that event.
results <- RunSim(
npats=as.numeric(nrow(IPD)), # Simulating the number of patients for whic
h we have IPD
n_sim=1, # We run all patients once (per treatment)
psa_bool = FALSE, # No PSA for this example
trt_list = c("int", "ref"),
common_all_inputs = common_all_inputs,
common_pt_inputs = common_pt_inputs,
unique_pt_inputs = unique_pt_inputs,
init_event_list = init_event_list,
evt_react_list = evt_react_list,
util_ongoing_list = util_ongoing,
cost_ongoing_list = cost_ongoing,
cost_instant_list = cost_instant,
ncores = 2,
drc = 0.035, # discount rate for costs
drq = 0.035, # discount rate for QALYs
input_out = c("BLAge","Sex","nat.death","pfs.time")
)
#> [1] "Simulation number: 1"
#> [1] "Time to run iteration 1: 2.36s"
#> [1] "Total time to run: 2.36s"
psa_ipd[1:10,] %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
evtname evttime cost qaly ly pat_id trt Sex BLAge nat.death pfs.tim
We can also check what has been the absolute number of events per strategy.
trt evtname n
Plots
We now use the simulation output to plot the Kaplan-Meier curves of the simulated OS and PFS against the
observed Kaplan-Meier curves. The simulated progression-free survival curve is lower than the observed due to
the addition of the progression-accelerating event into the model.
#Overall survival
KM.death <- psa_ipd %>% filter(evtname=="Death") %>% mutate(Event = 1)
ggsurvplot(km.comb,combine = TRUE,
title="Progression-free Survival",
palette=c("coral","turquoise","turquoise3","coral3"),
legend.labs=c("Observed - Ref","Observed - Int","Predicted - Int","Predicted
- Ref"),
linetype = c(2,2,1,1),
xlim=c(0,5), break.time.by = 1, censor = FALSE)
Developed by Valerie Aponte Ribero, Javier Sanchez Site built with pkgdown (https://pkgdown.r-lib.org/)
Alvarez. 2.0.6.