set.seed(42)
N <- 5000
# Storage for NPV, failure stage, and all 13 sampled inputs
npv_results <- numeric(N)
fail_stage <- character(N)
sim_inputs <- as.data.frame(matrix(NA, nrow = N, ncol = 13))
colnames(sim_inputs) <- c(
paste0("Cost_", c("RD","PC","CI","CII")),
paste0("Time_", c("RD","PC","CI","CII")),
paste0("Prob_", c("RD","PC","CI","CII")),
"Profit"
)
for (i in seq_len(N)) {
# ── Sample all uncertain inputs ──────────────────────────────
costs <- sapply(cost_params, function(p) rtriangle(1, p[1], p[2], p[3]))
times <- sapply(time_params, function(p) rtriangle(1, p[1], p[2], p[3]))
probs <- sapply(prob_params, function(p) rtriangle(1, p[1], p[2], p[3]))
profit <- rtriangle(1, profit_params[1], profit_params[2], profit_params[3])
# Store all inputs for sensitivity analysis
sim_inputs[i, 1:4] <- costs
sim_inputs[i, 5:8] <- times
sim_inputs[i, 9:12] <- probs
sim_inputs[i, 13] <- profit
# ── Sequential development process ───────────────────────────
# t_cum[k] = start time of stage k (stage 1 begins at t=0)
t_cum <- cumsum(c(0, times))
pv_cost <- 0
npv <- 0
failed <- FALSE
for (k in 1:4) {
t_start <- t_cum[k]
# Discount stage-k cost back to t=0
pv_cost <- pv_cost + costs[k] / (1 + r)^t_start
# Bernoulli gate: success with sampled probability
success <- runif(1) < probs[k]
if (!success) {
npv <- -pv_cost
fail_stage[i] <- stages[k]
failed <- TRUE
break
}
}
# If all stages succeed, add discounted profit
if (!failed) {
t_market <- t_cum[5] # Time when drug reaches market
npv <- -pv_cost + profit / (1 + r)^t_market
fail_stage[i] <- "Success"
}
npv_results[i] <- npv
}
# ── Summary statistics ────────────────────────────────────────
npv_df <- data.frame(NPV = npv_results, Outcome = fail_stage)
quants <- quantile(npv_results, probs = c(0.05, 0.25, 0.50, 0.75, 0.95))
var_5 <- quants["5%"]
es_5 <- mean(npv_results[npv_results <= var_5])
summary_stats <- data.frame(
Statistic = c("Mean", "Median", "5th Percentile (VaR₉₅)",
"25th Percentile", "50th Percentile",
"75th Percentile", "95th Percentile",
"Std. Deviation", "Prob(NPV > 0)",
"Expected Shortfall (5%)"),
Value = c(
round(mean(npv_results), 2),
round(median(npv_results), 2),
round(quants[1], 2),
round(quants[2], 2),
round(quants[3], 2),
round(quants[4], 2),
round(quants[5], 2),
round(sd(npv_results), 2),
round(mean(npv_results > 0) * 100, 1),
round(es_5, 2)
),
Units = c(rep("$M", 8), "%", "$M")
)
summary_stats %>%
kbl(caption = "NPV Summary Statistics — 5,000 Monte Carlo Simulations") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
column_spec(2, bold = TRUE)