TSD15 Appendix B R-Code
TSD15 Appendix B R-Code
#NOTES
# Text to the right of the "#" sign are comments and are not processed.
# Comments are an essential part of model code and should be used to describe what the code is doing.
###############################################################################
#Start of script###############################################################
###############################################################################
rm(list=ls())
# Set the working directory
# NOTE: Put the working directory in "" symbols (to make it a character vector)
source("DSU_DesFunctions.R")
################################################################################
################################################################################
# Global variables ‐ placed in base environment
###################################################################################
#set options########################################################################
###################################################################################
# Debug mode flag: this will print out the event list and accrued costs and qalys
dbg <‐ T
# Flag for whether individual level patient details should be stored and output
ind <‐ T
if (ind==T){
}
##################################################################################
###################################################################################
# to fix simulation results ‐ use set.seed. Useful for debugging as removes random variation
##################################################################################
###################################################################################
# means
tot.costs.int/npats
tot.qalys.int/npats
tot.costs.noint/npats
tot.qalys.noint/npats
tot.dcosts/npats
tot.dqalys/npats
# events
# Number of deaths not relating to hip fractures in intervention arm
PatData[[37]]$int$evtlist
PatData[[37]]$noint$evtlist
##################################################################################
###################################################################################
# Create object for exporting to Excel
ptnum=1:npats,
itthip=rep(NA, npats),
ittvert1=rep(NA, npats),
ittvert2=rep(NA, npats),
ittdeath=rep(NA, npats),
ihipcount=rep(NA, npats),
ivertcount=rep(NA, npats),
idthhip=rep(NA,npats),
idthall=rep(NA, npats),
icosts=rep(NA, npats),
iqalys=rep(NA, npats),
ntthip=rep(NA, npats),
nttvert1=rep(NA, npats),
nttvert2=rep(NA, npats),
nttdeath=rep(NA, npats),
nhipcount=rep(NA, npats),
nvertcount=rep(NA, npats),
ndthhip=rep(NA,npats),
ndthall=rep(NA, npats),
ncosts=rep(NA, npats),
nqalys=rep(NA, npats)
for (i in 1:npats){
write.csv(ExcelData, file="DSU_PatData.csv")
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Text to the right of the "#" sign are comments and are not processed.
# Comments are an essential part of model code and should be used to describe what the code is doing.
# This promotes transparency and helps avoid and identify errors.
vfracs <‐ rweibull(n=2, shape=2, scale=8) # This code simulates a vector of 2 independent Weibull variates, indicating sojourn times to vertebral fracture
hfrac <‐ rweibull(n=1, shape=4, scale=10) # Similarly, this code simulates a time to hip fracture
death <‐ rnorm(n=1, mean=12, sd=3) # As above, but now time to death using normal distribution
"hfrac", # This allows the user to refer to the corresponding event times by name
"vfrac2",
"death"
),
evttime=c( # This code assigns the event times to a vector, which can be referred to later in the model
hfrac,
vfracs[1], # Note that using square brackets [] selects values from a vector, in this case the "vfracs" vector
vfracs[1] + vfracs[2],
max(0, rnorm(n=1, mean=12, sd=3)) # the normal distribution used above has non‐zero probability of returning a negative value
if (ind==T){
return(output)
}
# InitEventlist.int : input is output from InitEventList.noint
# find the sojourn times for vfrac events and doubles the time to the first one.
vf2 <‐ output$evttime[thisrow] ‐ vf1 # This returns the sojourn time rather than the raw event time
vf2 <‐ vf1 + vf2 # Time to second vfrac FROM time to first vfrac is NOT doubled, it is simply added to the updated time to first vert frac
output$evttime[which(output$evtname=="vfrac1")] <‐ vf1 # Assign these fracture times to output vector
if (ind==T){
this.PatData$int$evtlist <<‐ output # This code stores individual patient output if this option has been chosen (see DesScript code)
} # Useful for debugging and validation, but increases memory requirements and may slow computation
return(output)
# AddOngoing: Calculate additional qalys and costs accrued from previous to current event
# Inputs:
log(1+rate)
return(output)
#######################
# Inputs:
addinstqalys <‐ lclvalq * ((1+lcldrq)^(‐lclcurtime)) # Note use of DISCRETE TIME discounting for instantaneous costs and benefits
return(output)
GetNxtEvt <‐ function(intervention=F){ # This function identifies which event is to be processed next for each patient, depending on intervention
if (intervention==F){ # It loops through events until death has occured, either due to hip fracture or other causes
if (dim(evtlist.noint)[1]> 0){
# Debugging line: If debugging is enabled (see DesScript.R) then this will print out the next event.
# This helps detect errors in model logic (e.g. fracture occurs after death).
if (dbg==T){
print(evtlist.noint)
} else {
} else {
if (dim(evtlist.int)[1]> 0){
if (dbg==T){
print(evtlist.int)
} else {
return(output)
# $evt : event
ReactEvt <‐ function(thisevt, intervention){ # This function processes the next event (as identified in the GetNextEvt function)
if (intervention==F){
# No intervention logic
if (evt=="death"){
if(ind==T){
this.PatData$noint$deathother <<‐ 1 # This indicates that the patient is now dead ‐ no further events
curtime <<‐ Inf # Set current time to Infinity so patient level loop stops
if (ind==T){ # Code chunks like this are processed only if individual patient info is being saved (ind=TRUE)
this.PatData$noint$nvert <<‐ this.PatData$noint$nvert + 1 # This records the number of vertebral fractures experienced AFTER event occurs
if (prvvert==F){
# ===================================
# ===================================
utilmlt <<‐ utilmlt * umult.vfrac # Utility is set to previous utility (utilmlt) multiplied by the multiplier for a vertebral fracture
prvvert <<‐ T
} else {
# ====================================
# ====================================
} else if (evt=="hfrac"){
if (ind==T){
# ====================================
# ====================================
utilmlt <<‐ utilmlt * umult.hfrac # as with vertebral ‐ multiply previous utility by hip fracture modifier
patdies <‐ runif(1) < mortprobhip # This code generates a uniform variate and compares it with the probability of hip fracture related mortality
# If the variate is less than the probability (0.05 in this example), "patdies" is set to TRUE
if (patdies) {
if (ind==T){
this.PatData$noint$deathhip <<‐ this.PatData$noint$deathhip + 1 # This indicates that the patient died of a hip fracture
} else {
stop("Event type not recognised") # Use debugging to identify why this occurred and fix error as appropriate
} else { #
#==============================================================
#==============================================================
if (evt=="death"){
if (ind==T){
curtime <<‐ Inf # Set current time to Infinity so patient level loop stops
if (ind==T){
if (prvvert==F){
# ===================================
# ===================================
prvvert <<‐ T
} else {
# ====================================
# ====================================
} else if (evt=="hfrac"){
if (ind==T){
if (ind==T){
} else {
# set to return nothing (NULL object) so earlier operations are not returned by accident
return(NULL)
}
# Enclose simulation within a function
# initialise variable of total costs and QALYs (note use of superassignment operator (<<‐))
for (i in 1:npats){
if (ind==T){
nvert=0,
nhip=0,
deathhip=0,
deathother=0
),
noint=list(
nvert=0,
nhip=0,
deathhip=0,
deathother=0
# Debugging line
if (dbg==T){
curtime <<‐ 0
thsqalys <<‐ 0
thscosts <<‐ 0
while(curtime < Inf){
if (is.null(Evt)==F){
ReactEvt(Evt, intervention=F)
if(dbg==T){
if (ind==T){
# subtracting costs and qalys as from no intervention arm, and want to know int ‐ noint
# reset curtime
curtime <<‐ 0
thsqalys <<‐ 0
thscosts <<‐ 0
emptylist <<‐ F
if (is.null(Evt)==F){
ReactEvt(Evt, intervention=T)
if(dbg==T){
if (ind==T){
if (ind==T){
# adding costs and qalys as from intervention arm, and no int has already been subtracted