Skip to content

Commit 25ff35f

Browse files
committed
finalized code
1 parent a38cba8 commit 25ff35f

File tree

14 files changed

+2491
-1002
lines changed

14 files changed

+2491
-1002
lines changed

R/STM_01.R

Lines changed: 193 additions & 236 deletions
Large diffs are not rendered by default.

R/STM_01_simple.R

Lines changed: 161 additions & 134 deletions
Large diffs are not rendered by default.

R/STM_02.R

Lines changed: 219 additions & 373 deletions
Large diffs are not rendered by default.

R/STM_03.R

Lines changed: 74 additions & 233 deletions
Large diffs are not rendered by default.

R/old/STM_01.R

Lines changed: 370 additions & 0 deletions
Large diffs are not rendered by default.

R/old/STM_01_simple.R

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
###############################################################################
2+
### Implementation of cohort state-transition models in R: ##
3+
### From conceptualization to implementation 2020 ##
4+
###############################################################################
5+
# This code forms the basis for the state-transition model of the article:
6+
# 'Implementation of cohort state-transition models in R'
7+
# Authors:
8+
# - Fernando Alarid-Escudero <[email protected]>
9+
# - Eline Krijkamp
10+
# - Eva A. Enns
11+
# - Alan Yang
12+
# - M.G. Myriam Hunink
13+
# - Petros Pechlivanoglou
14+
# - Hawre Jalal
15+
# Please cite the article when using this code
16+
#
17+
# To program this tutorial we made use of
18+
# R version 4.0.2 (2020-06-22)
19+
# Platform: 64-bit operating system, x64-based processor
20+
# Running under: Windows 10
21+
# RStudio: Version 1.3.1073 2009-2020 RStudio, Inc
22+
23+
###############################################################################
24+
################# Code of Appendix ##########################################
25+
###############################################################################
26+
# Implements a time-independent Sick-Sicker cSTM model #
27+
# Usual care: best available care for the patients with the disease. This scenario reflects the natural history of the disease progressions
28+
# New treatment 1: this new treatment is given to all sick patients, patients in sick and sicker, but does only improves the utility of those being sick.
29+
# New treatment 2: the new treatment reduces disease progression from sick to sicker. However, it is not possible to distinguish those sick from sicker and therefore all individuals in one of the two sick states get the treatment.
30+
# New treatment 1 & new treatment 2”: This strategy combines the new treatment 1 and new treatment 2. The disease progression is reduced and Sick individuals has an improved utility.
31+
# This is the simplified version of the base model (without transition rewards)
32+
33+
##################################### Initial setup ###########################
34+
rm(list = ls()) # remove any variables in R's memory
35+
36+
### Install packages
37+
# install.packages("dplyr") # to manipulate data
38+
# install.packages("reshape2") # to transform data
39+
# install.packages("ggplot2") # to visualize data
40+
# install.packages("scales") # for dollar signs and commas
41+
# install.packages("boot") # to handle log odds and log odds ratios
42+
# install.packages("devtools") # to ensure compatibility among packages
43+
# devtools::install_github("DARTH-git/dampack") # to install dampack form GitHub, for CEA and calculate ICERs
44+
45+
### Load packages
46+
library(dplyr)
47+
library(reshape2)
48+
library(ggplot2)
49+
library(scales)
50+
library(boot)
51+
library(dampack)
52+
53+
### Load functions
54+
source("functions/Functions.R")
55+
56+
##################################### Model input #########################################
57+
## General setup
58+
n_age_init <- 25 # age at baseline
59+
n_age_max <- 110 # maximum age of follow up
60+
n_t <- n_age_max - n_age_init # time horizon, number of cycles
61+
62+
v_n <- c("H", "S1", "S2", "D") # the 4 health states of the model:
63+
# Healthy (H), Sick (S1), Sicker (S2), Dead (D)
64+
n_states <- length(v_n) # number of health states
65+
v_hcc <- rep(1, n_t + 1) # vector of half-cycle correction
66+
v_hcc[1] <- v_hcc[n_t + 1] <- 0.5 # half-cycle correction weight
67+
d_c <- 0.03 # discount rate for costs
68+
d_e <- 0.03 # discount rate for QALYs
69+
v_names_str <- c("Usual care", "New treatment 1", "New treatment 2", "New treatments 1 & 2") # store the strategy names
70+
71+
## Transition probabilities (per cycle) and hazard ratios
72+
p_HD <- 0.002 # constant probability of dying when Healthy (all-cause mortality)
73+
p_HS1 <- 0.15 # probability to become Sick when Healthy
74+
p_S1H <- 0.5 # probability to become Healthy when Sick
75+
p_S1S2 <- 0.105 # probability to become Sicker when Sick
76+
hr_S1 <- 3 # hazard ratio of death in Sick vs Healthy
77+
hr_S2 <- 10 # hazard ratio of death in Sicker vs Healthy
78+
p_S1D <- 1 - exp(log(1 - p_HD) * hr_S1) # probability of dying in Sick
79+
p_S2D <- 1 - exp(log(1 - p_HD) * hr_S2) # probability of dying in Sicker
80+
# For New treatment 2
81+
or_S1S2 <- 0.7 # odds ratio of becoming Sicker when Sick under New treatment 2 compared to Usual care
82+
lor_S1S2 <- log(or_S1S2) # log-odds ratio of becoming Sicker when Sick
83+
logit_S1S2 <- logit(p_S1S2) # log-odds of becoming Sicker when Sick
84+
p_S1S2_trt2 <- inv.logit(logit_S1S2 + lor_S1S2) # probability to become Sicker when Sick under New treatment 2
85+
86+
## State rewards
87+
# Costs
88+
c_H <- 2000 # cost of remaining one cycle Healthy
89+
c_S1 <- 4000 # cost of remaining one cycle Sick
90+
c_S2 <- 15000 # cost of remaining one cycle Sicker
91+
c_D <- 0 # cost of being dead (per cycle)
92+
c_trt1 <- 12000 # cost of New treatment 1 (per cycle)
93+
c_trt2 <- 13000 # cost of New treatment 2 (per cycle)
94+
# Utilities
95+
u_H <- 1 # utility when Healthy
96+
u_S1 <- 0.75 # utility when Sick
97+
u_S2 <- 0.5 # utility when Sicker
98+
u_D <- 0 # utility when Healthy
99+
u_trt1 <- 0.95 # utility when being treated
100+
101+
# Discount weight (equal discounting is assumed for costs and effects)
102+
v_dwc <- 1 / ((1 + d_e) ^ (0:(n_t)))
103+
v_dwe <- 1 / ((1 + d_c) ^ (0:(n_t)))
104+
105+
############################# Construct state-transition models ################
106+
## Initial state vector
107+
# All starting healthy
108+
v_s_init <- c(H = 1, S1 = 0, S2 = 0, D = 0) # initial state vector
109+
v_s_init
110+
111+
## Initialize cohort trace
112+
m_M <- matrix(0,
113+
nrow = (n_t + 1), ncol = n_states,
114+
dimnames = list(0:n_t, v_n))
115+
# Store the initial state vector in the first row of the cohort trace
116+
m_M[1, ] <- v_s_init
117+
# For treatment 2
118+
m_M_trt2 <- m_M
119+
120+
## Initialize transition probability matrix
121+
m_P <- matrix(0,
122+
nrow = n_states, ncol = n_states,
123+
dimnames = list(v_n, v_n)) # define row and column names
124+
## Fill in matrix
125+
# From H
126+
m_P["H", "H"] <- 1 - (p_HS1 + p_HD)
127+
m_P["H", "S1"] <- p_HS1
128+
m_P["H", "D"] <- p_HD
129+
# From S1
130+
m_P["S1", "H"] <- p_S1H
131+
m_P["S1", "S1"] <- 1 - (p_S1H + p_S1S2 + p_S1D)
132+
m_P["S1", "S2"] <- p_S1S2
133+
m_P["S1", "D"] <- p_S1D
134+
# From S2
135+
m_P["S2", "S2"] <- 1 - p_S2D
136+
m_P["S2", "D"] <- p_S2D
137+
# From D
138+
m_P["D", "D"] <- 1
139+
140+
# For New treatment 2
141+
# Only need to update the probabilities involving p_S1S2
142+
m_P_trt2 <- m_P
143+
m_P_trt2["S1", "S1"] <- 1 - (p_S1H + p_S1S2_trt2 + p_S1D)
144+
m_P_trt2["S1", "S2"] <- p_S1S2_trt2
145+
146+
### Check if transition matrix is valid (i.e., each row should add up to 1)
147+
valid <- rowSums(m_P) # sum the rows
148+
if (!isTRUE(all.equal(as.numeric((valid)), as.numeric(rep(1, n_states))))) { #check if the rows are all equal to one
149+
stop("This is not a valid transition Matrix")
150+
}
151+
valid2 <- rowSums(m_P_trt2) # sum the rows
152+
if (!isTRUE(all.equal(as.numeric((valid2)), as.numeric(rep(1, n_states))))) { #check if the rows are all equal to one
153+
stop("This is not a valid transition Matrix")
154+
}
155+
156+
## Initialize 3D transition dynamics array
157+
a_A <- array(0,
158+
dim = c(n_states, n_states, n_t + 1),
159+
dimnames = list(v_n, v_n, 0:n_t))
160+
# Set first slice of A with the initial state vector in its diagonal
161+
diag(a_A[, , 1]) <- v_s_init
162+
# For New treatment 2, the array structure and initial state is identical to Usual care and New treatment 1
163+
a_A_trt2 <- a_A
164+
165+
#### Run Markov model ####
166+
# Iterative solution of time-independent cSTM
167+
for(t in 1:n_t){
168+
# Fill in cohort trace
169+
m_M[t + 1, ] <- m_M[t, ] %*% m_P
170+
m_M_trt2[t + 1, ] <- m_M_trt2[t, ] %*% m_P_trt2
171+
# Fill in transition dynamics array
172+
a_A[, , t + 1] <- m_M[t, ] * m_P
173+
a_A_trt2[, , t + 1] <- m_M_trt2[t, ] * m_P_trt2
174+
}
175+
176+
#### Plot Outputs ####
177+
## Plot the cohort trace for scenarios Usual care and New treatment 1
178+
plot_trace(m_M)
179+
## Plot the cohort trace for scenarios New treatment 2 and New treatments 1 & 2
180+
plot_trace(m_M_trt2)
181+
182+
#### State Rewards ####
183+
## Vector of state utilities under Usual Care
184+
v_u_UC <- c(H = u_H, S1 = u_S1, S2 = u_S2, D = u_D)
185+
## Vector of state costs per cycle under Usual Care
186+
v_c_UC <- c(H = c_H, S1 = c_S1, S2 = c_S2, D = c_D)
187+
188+
## Vector of state utilities under New treatment 1
189+
v_u_trt1 <- c(H = u_H, S1 = u_trt1, S2 = u_S2, D = u_D)
190+
## Vector of state costs per cycle under New treatment 1
191+
v_c_trt1 <- c(H = c_H, S1 = c_S1 + c_trt1, S2 = c_S2 + c_trt1, D = c_D)
192+
193+
## Vector of state utilities under New treatment 2
194+
v_u_trt2 <- c(H = u_H, S1 = u_S1, S2 = u_S2, D = u_D)
195+
## Vector of state costs per cycle under New treatment 2
196+
v_c_trt2 <- c(H = c_H, S1 = c_S1 + c_trt2, S2 = c_S2 + c_trt2, D = c_D)
197+
198+
## Vector of state utilities under New treatment 1 & 2
199+
v_u_trt1_2 <- c(H = u_H, S1 = u_trt1, S2 = u_S2, D = u_D)
200+
## Vector of state costs per cycle under New treatment 1 & 2
201+
v_c_trt1_2 <- c(H = c_H, S1 = c_S1 + (c_trt1 + c_trt2), S2 = c_S2 + (c_trt1 + c_trt2), D = c_D)
202+
203+
#### Expected QALYs and Costs per cycle ####
204+
## Vector of qalys under Usual Care
205+
v_qaly_UC <- m_M %*% v_u_UC
206+
## Vector of costs under Usual Care
207+
v_cost_UC <- m_M %*% v_c_UC
208+
209+
## Vector of qalys under New Treatment 1
210+
v_qaly_trt1 <- m_M %*% v_u_trt1
211+
## Vector of costs under New Treatment 1
212+
v_cost_trt1 <- m_M %*% v_c_trt1
213+
214+
## Vector of qalys under New Treatment 2
215+
v_qaly_trt2 <- m_M_trt2 %*% v_u_trt2
216+
## Vector of costs under New Treatment 2
217+
v_cost_trt2 <- m_M_trt2 %*% v_c_trt2
218+
219+
## Vector of qalys under New Treatment 1 & 2
220+
v_qaly_trt1_2 <- m_M_trt2 %*% v_u_trt1_2
221+
## Vector of costs under New Treatment 1 & 2
222+
v_cost_trt1_2 <- m_M_trt2 %*% v_c_trt1_2
223+
224+
#### Discounted total expected QALYs and Costs per strategy and apply half-cycle correction if applicable ####
225+
### For Usual Care
226+
## QALYs
227+
n_totqaly_UC <- t(v_qaly_UC) %*% (v_dwe * v_hcc)
228+
## Costs
229+
n_totcost_UC <- t(v_cost_UC) %*% (v_dwc * v_hcc)
230+
231+
### For New treatment 1
232+
## QALYs
233+
n_totqaly_trt1 <- t(v_qaly_trt1) %*% (v_dwe * v_hcc)
234+
## Costs
235+
n_totcost_trt1 <- t(v_cost_trt1) %*% (v_dwc * v_hcc)
236+
237+
### For New treatment 2
238+
## QALYs
239+
n_totqaly_trt2 <- t(v_qaly_trt2) %*% (v_dwe * v_hcc)
240+
## Costs
241+
n_totcost_trt2 <- t(v_cost_trt2) %*% (v_dwc * v_hcc)
242+
243+
### For New treatment 1 & 2
244+
## QALYs
245+
n_totqaly_trt1_2 <- t(v_qaly_trt1_2) %*% (v_dwe * v_hcc)
246+
## Costs
247+
n_totcost_trt1_2 <- t(v_cost_trt1_2) %*% (v_dwc * v_hcc)
248+
249+
########################### Cost-effectiveness analysis #######################
250+
### Vector of total costs for all strategies
251+
v_ted_cost <- c(n_totcost_UC, n_totcost_trt1, n_totcost_trt2, n_totcost_trt1_2)
252+
### Vector of effectiveness for all strategies
253+
v_ted_qaly <- c(n_totqaly_UC, n_totqaly_trt1, n_totqaly_trt2, n_totqaly_trt1_2)
254+
255+
### Calculate incremental cost-effectiveness ratios (ICERs)
256+
df_cea <- calculate_icers(cost = v_ted_cost,
257+
effect = v_ted_qaly,
258+
strategies = v_names_str)
259+
df_cea
260+
### Create CEA table with proper formatting
261+
table_cea <- format_table_cea(df_cea)
262+
table_cea
263+
264+
### CEA frontier
265+
plot(df_cea, label = "all") +
266+
expand_limits(x = max(table_cea$QALYs + 0.5))
267+
File renamed without changes.

0 commit comments

Comments
 (0)