Skip to content

Commit

Permalink
Updated model code for JNO paper
Browse files Browse the repository at this point in the history
  • Loading branch information
xiao-zang-git committed Nov 14, 2022
1 parent 2b35bdf commit 489a344
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 74 deletions.
Binary file modified Inputs/MasterTable.xlsx
Binary file not shown.
Binary file modified Inputs/ProgramData.xlsx
Binary file not shown.
42 changes: 22 additions & 20 deletions analyze_calibration.R
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ tar.data <- Target$pe


## Plot ##
par(mfrow = c(1, 3))
par(oma = c(4,1,1,1), mfrow = c(1, 3), mar = c(4, 4, 1, 1))
# REVIEWED md = median, change to mean
mean_oddeath <- apply(calibration_results_subset[, c("od.death16", "od.death17", "od.death18", "od.death19")], 2, mean)
ymax <- max(calibration_results_subset[, c("od.death16", "od.death17", "od.death18", "od.death19")])
plot(
x = 2016:2019, tar.data[1:4], col = "black", pch = 18, xlab = "Year", ylab = "Number of opioid overdose deaths", cex = 1.2, cex.axis = 1.2, cex.lab = 1.3,
xaxt = "n", ylim = c(0, 500), frame.plot = FALSE
)
# title("A", adj = 0, line =-0.5)
title("A", adj = 0.05, line =-2, cex.main = 1.5)
for (i in 1:cal_sample) {
lines(x = 2016:2019, y = calibration_results_subset[i, c("od.death16", "od.death17", "od.death18", "od.death19")], col = adjustcolor("indianred1", alpha = 0.2), lwd = 3)
}
Expand All @@ -168,21 +168,7 @@ points(x = 2016:2019, tar.data[1:4], col = "black", pch = 16, cex = 1.2, cex.axi
axis(1, at = 2016:2019, pos = 0, lwd.ticks = 0, cex.axis = 1.2)
# axis(side=1, pos=0, lwd.ticks=0)
abline(h = 0)
# legend("top",
# legend = c("Target", "Model: mean", "Model: simulation"),
# col = c("black", "red", adjustcolor("indianred1", alpha = 0.2)),
# pch = c(16, NA, NA),
# lty = c(NA, 1, 1),
# lwd = 3,
# bty = "n",
# pt.cex = 1.2,
# cex = 1.1,
# text.col = "black",
# horiz = T
# )
# legend(x="bottomright",
# col=c("dodgerblue","firebrick2", "lightgrey"),
# lwd=c(1.2, 1, 0.5), lty = c(1, NA, NA), pch=c(16, 16,16), pt.cex = c(1,1.1,1), cex = 0.8, bty = "n")



mean_fxdeath <- apply(calibration_results_subset[, c("fx.death16", "fx.death17", "fx.death18", "fx.death19")], 2, median)
Expand All @@ -191,7 +177,7 @@ plot(
x = 2016:2019, tar.data[5:8], col = "black", pch = 18, xlab = "Year", ylab = "Percentage of opioid overdose deaths involving fentanyl", cex = 1.2, cex.axis = 1.2, cex.lab = 1.3,
xaxt = "n", yaxt = "n", ylim = c(0, 1), frame.plot = FALSE
)
# title("A", adj = 0, line =-0.5)
title("B", adj = 0.05, line =-2, cex.main = 1.5)
for (i in 1:cal_sample) {
lines(x = 2016:2019, y = calibration_results_subset[i, c("fx.death16", "fx.death17", "fx.death18", "fx.death19")], col = adjustcolor("indianred1", alpha = 0.2), lwd = 2)
}
Expand All @@ -208,7 +194,7 @@ plot(
x = 2016:2019, tar.data[9:12], col = "black", pch = 18, xlab = "Year", ylab = "Number of ED visists for opioid overdose", cex = 1.2, cex.axis = 1.2, cex.lab = 1.3,
xaxt = "n", ylim = c(0, 2500), frame.plot = FALSE
)

title("C", adj = 0.05, line =-2, cex.main = 1.5)
for (i in 1:cal_sample) {
lines(x = 2016:2019, y = calibration_results_subset[i, c("ed.visit16", "ed.visit17", "ed.visit18", "ed.visit19")], col = adjustcolor("indianred1", alpha = 0.2), lwd = 2)
}
Expand All @@ -217,6 +203,22 @@ points(x = 2016:2019, tar.data[9:12], col = "black", pch = 16, cex = 1.2, cex.ax
axis(1, at = 2016:2019, pos = 0, lwd.ticks = 0, cex.axis = 1.2)
abline(h = 0)

par(fig = c(0, 1, 0, 1), oma = c(1, 0, 0, 0), mar = c(0, 0, 0, 0), new = TRUE)
plot(0, 0, type = 'l', bty = 'n', xaxt = 'n', yaxt = 'n')
legend("bottom",
legend = c("Target", "Model: mean", "Model: simulation"),
col = c("black", "red", adjustcolor("indianred1", alpha = 0.2)),
pch = c(16, NA, NA),
lty = c(NA, 1, 1),
lwd = 3,
bty = "n",
pt.cex = 2,
cex = 1.4,
text.col = "black",
horiz = T)



par(mfrow = c(1, 1))
legend("top",
legend = c("Target", "Model: mean", "Model: simulation"),
Expand Down Expand Up @@ -263,4 +265,4 @@ p <- ggplot(ggplot.data, aes(x = case, y = pe, color = case)) +
labs(y = "Value", x = "") +
theme_bw()

## City-level validation, please go to CityLevelValidation.R ##
## City-level validation, please go to validate_city.R ##
Binary file modified calibration/Calibrated_results.xlsx
Binary file not shown.
4 changes: 2 additions & 2 deletions data_input.R
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ params$out.prebopioid <- out.prebopioid
## Parameters for microsimulation ##
# life table: for mortality
mor.bg.y <- read.xlsx(WB, sheet = "LifeTable")$pe
params$mor.bg <- 1 - (1 - mor.bg.y / 1000000)^(1 / 12)
params$mor.bg <- 1 - (1 - mor.bg.y / 100000)^(1 / 12)
mor.drug.y <- read.xlsx(WB, sheet = "LifeTable")$drug
params$mor.drug <- 1 - (1 - mor.drug.y / 1000000)^(1 / 12)
params$mor.drug <- 1 - (1 - mor.drug.y / 100000)^(1 / 12)
# REVIEWED gp = general population
mor.gp <- read.xlsx(WB, sheet = "LifeTable")$age
rm(list = c("mor.bg.y", "mor.drug.y"))
Expand Down
11 changes: 6 additions & 5 deletions main.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ source("io_setup.R")
###########################################################################################################
# Define different program scenarios for distributing additional 10,000 kits and initialize matrices and arrrays for results
sim.seed <- sim.seed[1:500]
scenario.name <- c("Status Quo", "SSP_10K", "MailEvent_10K", "Healthcare_10K")
scenario.name <- c("Status Quo", "SSP_10K", "Outreach_10K", "MailEvent_10K", "Healthcare_10K")
mx.od.death.last <- matrix(0, nrow = length(sim.seed), ncol = length(scenario.name))
mx.costs.totl <- matrix(0, nrow = length(sim.seed), ncol = length(scenario.name))
colnames(mx.od.death.last) <- colnames(mx.costs.totl) <-scenario.name
Expand Down Expand Up @@ -107,10 +107,11 @@ for (ss in 1:length(sim.seed)){
# toc()
}

rgn.results <- data.frame(location = rep(v.region, 4), scenario = rep(scenario.name, each=num_regions),
N_Nx_mean = numeric(num_regions * 4), N_Nx_lower = numeric(num_regions * 4), N_Nx_upper = numeric(num_regions * 4),
N_ODDeath_mean = numeric(num_regions * 4), N_ODDeath_lower = numeric(num_regions * 4), N_ODDeath_upper = numeric(num_regions * 4),
population = rep(colSums(Demographic[,-c(1:3)]), 4))
n.scenario <- length(scenario.name)
rgn.results <- data.frame(location = rep(v.region, n.scenario), scenario = rep(scenario.name, each=num_regions),
N_Nx_mean = numeric(num_regions * n.scenario), N_Nx_lower = numeric(num_regions * n.scenario), N_Nx_upper = numeric(num_regions * n.scenario),
N_ODDeath_mean = numeric(num_regions * n.scenario), N_ODDeath_lower = numeric(num_regions * n.scenario), N_ODDeath_upper = numeric(num_regions * n.scenario),
population = rep(colSums(Demographic[,-c(1:3)]), n.scenario))

for (ii in 1:length(scenario.name)){
rgn.results$N_Nx_mean[rgn.results$scenario== scenario.name[ii]] <- apply(array.oend.nlx.rgn, c(1,2), mean)[,scenario.name[ii]]
Expand Down
138 changes: 138 additions & 0 deletions main_ProgProp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env Rscript

###############################################################################################
###################### PROFOUND Naloxone Distribution model #### 2022 #########################
###############################################################################################
# Main module for the microsimulation of the Profound Naloxone distribution model:
#
# Author: Xiao Zang, PhD; Shayla Nolen, MPH; Sam Bessy, MSc
# Marshall Lab, Department of Epidemiology, Brown University
# Created: May 06, 2020
# ATTN: currently not used but will incorporate other main analysis modules here later
###############################################################################################

###############################################################################################
#### Individual-based microsimulation ####
#### 7 health states: prescribed (preb), illicit-injection (il.hr) - high-risk ####
#### illicit-noninjection (il.lr) - low-risk ####
#### inactive (inact), non-opioid drug use (NODU) - stimulant, ####
#### relapsed (relap), death (dead) ####
#### 1 health event: Overdose ####
#### Attributes: age, sex, residence, race, ####
#### current state (curr.state), opioid use state (OU.state), ####
#### initial state (init.state), initial age (inits.age), ####
#### fenatneyl exposure (fx), ever overdosed (ever.od) ####
#### Built to inform Naloxone distribution strategies to prevent overdose death ####
###############################################################################################

#############################################################################
# 1. SET directory and workspace
#############################################################################
rm(list = ls())
library(argparser)
library(dplyr)
library(tictoc)
library(openxlsx)
library(abind)
library(tictoc)
source("transition_probability.R")
source("microsim.R")
source("decision_tree.R")
source("data_input.R")
source("naloxone_availability_program.R")
source("cost_effectiveness.R")

yr_start <- 2016 # starting year of simulation
yr_end <- 2022 # end year of simulation (also the year for evaluation)
discount.rate <- 0.03 # discounting of costs by 3%

args <- arg_parser("arguments")
args <- add_argument(args, "--seed", help = "seed for random numbers", default = 2021)
args <- add_argument(args, "--regional", help = "flag to run regional model", flag = TRUE)
args <- add_argument(args, "--outfile", help = "file to store outputs", default = "OverdoseDeath_RIV1_0.csv")
args <- add_argument(args, "--ppl", help = "file with initial ppl info", default = "Inputs/init_pop.rds")
argv <- parse_args(args)
seed <- as.integer(argv$seed)
init_ppl.file <- argv$ppl
source("io_setup.R")

##################################### Run simulation ######################################################
############## RI modeling analysis: distributing 10,000 kits through different programs ##################
###########################################################################################################
# Define different program scenarios for distributing additional 10,000 kits and initialize matrices and arrrays for results
sim.seed <- sim.seed[1:500]
scenario.name <- c("Status Quo", "SSP_10K", "Outreach_10K", "MailEvent_10K", "Healthcare_10K")
mx.od.death.last <- matrix(0, nrow = length(sim.seed), ncol = length(scenario.name))
mx.costs.totl <- matrix(0, nrow = length(sim.seed), ncol = length(scenario.name))
colnames(mx.od.death.last) <- colnames(mx.costs.totl) <-scenario.name
mx.od.death.1st <- mx.od.death.2nd <- mx.od.death.totl <- mx.od.death.wtns.1st <- mx.od.death.wtns.2nd <- mx.od.death.wtns.last <- mx.od.death.wtns.totl <- mx.od.death.last

array.od.death.rgn <- array(0, dim = c(num_regions, length(scenario.name), length(sim.seed)))
array.oend.nlx.rgn <- array(0, dim = c(num_regions, length(scenario.name), length(sim.seed)))
dimnames(array.od.death.rgn)[[1]] <- dimnames(array.oend.nlx.rgn)[[1]] <- v.region
dimnames(array.od.death.rgn)[[2]] <- dimnames(array.oend.nlx.rgn)[[2]] <- scenario.name
# Simulation based on multiple parameter sets and seeds (calibrated)
for (ss in 1:length(sim.seed)){
# tic()
print(paste0("Parameter set: ", ss))
vparameters.temp <- sim.data.ls[[ss]]
sim_sq <- MicroSim(init_ppl, params = vparameters.temp, timesteps, agent_states, discount.rate, PT.out = FALSE, strategy = "SQ", seed = sim.seed[ss]) # run for status quo
mx.od.death.1st[ss, "Status Quo"] <- sum(sim_sq$m.oddeath[(timesteps-35):(timesteps-24), ])
mx.od.death.2nd[ss, "Status Quo"] <- sum(sim_sq$m.oddeath[(timesteps-23):(timesteps-12), ])
mx.od.death.last[ss, "Status Quo"] <- sum(sim_sq$m.oddeath[(timesteps-11):timesteps, ])
mx.od.death.totl[ss, "Status Quo"] <- sum(sim_sq$m.oddeath[(timesteps-35):timesteps, ])
mx.od.death.wtns.1st[ss, "Status Quo"] <- sum(sim_sq$v.oddeath.w[(timesteps-35):(timesteps-24)])
mx.od.death.wtns.2nd[ss, "Status Quo"] <- sum(sim_sq$v.oddeath.w[(timesteps-23):(timesteps-12)])
mx.od.death.wtns.last[ss, "Status Quo"] <- sum(sim_sq$v.oddeath.w[(timesteps-11):timesteps])
mx.od.death.wtns.totl[ss, "Status Quo"] <- sum(sim_sq$v.oddeath.w[(timesteps-35):timesteps])
mx.costs.totl[ss, "Status Quo"] <- sim_sq$total.cost
array.od.death.rgn[ , "Status Quo", ss] <- colSums(sim_sq$m.oddeath[(timesteps-11):timesteps, ])
array.oend.nlx.rgn[ , "Status Quo", ss] <- as.vector(t(sim_sq$n.nlx.OEND.str))

for (jj in 2:length(scenario.name)){
vparameters.temp$expand.kits <- 10000
programprop <- read.xlsx("Inputs/ProgramData.xlsx")
sim_pg <- MicroSim(init_ppl, params = vparameters.temp, timesteps, agent_states, discount.rate, PT.out = FALSE, strategy = scenario.name[jj], seed = sim.seed[ss]) # run for program scenario
mx.od.death.1st[ss, jj] <- sum(sim_pg$m.oddeath[(timesteps-35):(timesteps-24), ])
mx.od.death.2nd[ss, jj] <- sum(sim_pg$m.oddeath[(timesteps-23):(timesteps-12), ])
mx.od.death.last[ss, jj] <- sum(sim_pg$m.oddeath[(timesteps-11):timesteps, ])
mx.od.death.totl[ss, jj] <- sum(sim_pg$m.oddeath[(timesteps-35):timesteps, ])
mx.od.death.wtns.1st[ss, jj] <- sum(sim_pg$v.oddeath.w[(timesteps-35):(timesteps-24)])
mx.od.death.wtns.2nd[ss, jj] <- sum(sim_pg$v.oddeath.w[(timesteps-23):(timesteps-12)])
mx.od.death.wtns.last[ss, jj] <- sum(sim_pg$v.oddeath.w[(timesteps-11):timesteps])
mx.od.death.wtns.totl[ss, jj] <- sum(sim_pg$v.oddeath.w[(timesteps-35):timesteps])
mx.costs.totl[ss, jj] <- sim_pg$total.cost
array.od.death.rgn[ , jj, ss] <- colSums(sim_pg$m.oddeath[(timesteps-11):timesteps, ])
array.oend.nlx.rgn[ , jj, ss] <- as.vector(t(sim_pg$n.nlx.OEND.str))
}
# toc()
}

n.scenario <- length(scenario.name)
rgn.results <- data.frame(location = rep(v.region, n.scenario), scenario = rep(scenario.name, each=num_regions),
N_Nx_mean = numeric(num_regions * n.scenario), N_Nx_lower = numeric(num_regions * n.scenario), N_Nx_upper = numeric(num_regions * n.scenario),
N_ODDeath_mean = numeric(num_regions * n.scenario), N_ODDeath_lower = numeric(num_regions * n.scenario), N_ODDeath_upper = numeric(num_regions * n.scenario),
population = rep(colSums(Demographic[,-c(1:3)]), n.scenario))

for (ii in 1:length(scenario.name)){
rgn.results$N_Nx_mean[rgn.results$scenario== scenario.name[ii]] <- apply(array.oend.nlx.rgn, c(1,2), mean)[,scenario.name[ii]]
rgn.results$N_Nx_lower[rgn.results$scenario== scenario.name[ii]] <- apply(array.oend.nlx.rgn, c(1,2), function(x) quantile(x, probs = c(0.025)))[,scenario.name[ii]]
rgn.results$N_Nx_upper[rgn.results$scenario== scenario.name[ii]] <- apply(array.oend.nlx.rgn, c(1,2), function(x) quantile(x, probs = c(0.975)))[,scenario.name[ii]]
rgn.results$N_ODDeath_mean[rgn.results$scenario== scenario.name[ii]] <- apply(array.od.death.rgn, c(1,2), mean)[,scenario.name[ii]]
rgn.results$N_ODDeath_lower[rgn.results$scenario== scenario.name[ii]] <- apply(array.od.death.rgn, c(1,2), function(x) quantile(x, probs = c(0.025)))[,scenario.name[ii]]
rgn.results$N_ODDeath_upper[rgn.results$scenario== scenario.name[ii]] <- apply(array.od.death.rgn, c(1,2), function(x) quantile(x, probs = c(0.975)))[,scenario.name[ii]]
}

detach("package:openxlsx", unload = TRUE)
library(xlsx)

write.xlsx(mx.od.death.1st, file = ("Outputs/RI10K/ODdeaths_prog.xlsx"), sheetName = "1st year", row.names = F)
write.xlsx(mx.od.death.2nd, file = ("Outputs/RI10K/ODdeaths_prog.xlsx"), sheetName = "2nd year", append = T, row.names = F)
write.xlsx(mx.od.death.last, file = ("Outputs/RI10K/ODdeaths_prog.xlsx"), sheetName= "last year", append = T, row.names = F)
write.xlsx(mx.od.death.totl, file = ("Outputs/RI10K/ODdeaths_prog.xlsx"), sheetName= "Total", append = T, row.names = F)
write.xlsx(mx.od.death.wtns.1st, file = ("Outputs/RI10K/WitnessedDeaths_prog.xlsx"), sheetName = "1st year", row.names = F)
write.xlsx(mx.od.death.wtns.2nd, file = ("Outputs/RI10K/WitnessedDeaths_prog.xlsx"), sheetName = "2nd year", append = T, row.names = F)
write.xlsx(mx.od.death.wtns.last, file = ("Outputs/RI10K/WitnessedDeaths_prog.xlsx"), sheetName= "last year", append = T, row.names = F)
write.xlsx(mx.od.death.wtns.totl, file = ("Outputs/RI10K/WitnessedDeaths_prog.xlsx"), sheetName= "Total", append = T, row.names = F)
write.xlsx(mx.costs.totl, file = ("Outputs/RI10K/Total_costs_prog.xlsx"), row.names = F)
write.csv(rgn.results, file = ("Outputs/RI10K/Results_bytown_prog.csv"), row.names = F)
6 changes: 3 additions & 3 deletions microsim.R
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ MicroSim <- function(init_ppl, params, timesteps, agent_states, discount.rate, P
if (strategy == "SSP_10K"){
SSP_drug_resid <- crosstab_drug_resid[, c("il.hr", "NODU")]*rep(c(1, 0.133), each = nrow(crosstab_drug_resid))
n.nlx.OEND.str <- round(rowSums(n.nlx.OEND.str.add.total * SSP_drug_resid / sum(SSP_drug_resid)), 0) + n.nlx.OEND.str.base
# } else if (strategy == "StreetOutreach_10K"){
# StreetOutreach_drug_resid <- crosstab_drug_resid[, c("il.lr", "il.hr", "NODU")]
# n.nlx.OEND.str <- round(rowSums(n.nlx.OEND.str.add.total * StreetOutreach_drug_resid / sum(StreetOutreach_drug_resid)), 0) + n.nlx.OEND.str.base
} else if (strategy == "Outreach_10K"){
Outreach_drug_resid <- crosstab_drug_resid[, c("il.lr", "il.hr", "NODU")]
n.nlx.OEND.str <- round(rowSums(n.nlx.OEND.str.add.total * Outreach_drug_resid / sum(Outreach_drug_resid)), 0) + n.nlx.OEND.str.base
} else if (strategy == "MailEvent_10K"){
MailEvent_drug_resid <- crosstab_drug_resid
n.nlx.OEND.str <- round(rowSums(n.nlx.OEND.str.add.total * MailEvent_drug_resid / sum(MailEvent_drug_resid)), 0) + n.nlx.OEND.str.base
Expand Down
4 changes: 4 additions & 0 deletions naloxone_availability.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ nlx.avail.algm <- function(n.nlx, crosstab_drug_resid, OD_loc_pub, nlx.adj, cap,
Healthcare_drug_resid <- crosstab_drug_resid[, c("preb")]
n.nlx.drug.resid <- n.nlx.drug.resid.base
n.nlx.drug.resid[, c("preb")] <- tenk * Healthcare_drug_resid / sum(Healthcare_drug_resid) + n.nlx.drug.resid.base[, c("preb")]
} else if (strategy == "Outreach_10K"){
Outreach_drug_resid <- crosstab_drug_resid[, c("il.lr", "il.hr", "NODU")]
n.nlx.drug.resid <- n.nlx.drug.resid.base
n.nlx.drug.resid[, c("il.lr", "il.hr", "NODU")] <- tenk * Outreach_drug_resid / sum(Outreach_drug_resid) + n.nlx.drug.resid.base[, c("il.lr", "il.hr", "NODU")]
}
p.nlx.avail <- cap * (1 - exp(-(nlx.adj / cap) * n.nlx.drug.resid / crosstab_drug_resid))
return(p.nlx.avail)
Expand Down
Loading

0 comments on commit 489a344

Please sign in to comment.