Step 0: Loading Libraries and Defining Parameters

library(data.table)
d1_study <- ("RITONAVIR") #Drug 1 of DDI
d2_study <- ("ATORVASTATIN") #Drug 2 of DDI
ae_study <- ("MYOPATHY/RHABDOMYOLYSIS") #AE of DDI

file_path <- ("C://Users//William//Desktop//DDI Codes//data//processed//1.csv")

Step 1: Function to Generate Contingency Table

get_ic_table <- function(d1, d2, ae, merged){
  
  merged$d1i <- ifelse(merged$active_ingredient == d1, 1, 0)
  merged$d2i <- ifelse(merged$active_ingredient == d2, 1, 0)
  merged$aei <- ifelse(merged$pt == ae, 1, 0)
  
  merged <- merged[, .(id, d1i, d2i, aei)]
  
  flat <-  merged[, .(d1i = max(d1i), d2i = max(d2i), aei = max(aei)), by = id]
  
  #individual cell counts
  n111 <- nrow(flat[flat$d1i == 1 & flat$d2i == 1 & flat$aei == 1])
  n110 <- nrow(flat[flat$d1i == 1 & flat$d2i == 1 & flat$aei == 0])
  
  n101 <- nrow(flat[flat$d1i == 1 & flat$d2i == 0 & flat$aei == 1])
  n100 <- nrow(flat[flat$d1i == 1 & flat$d2i == 0 & flat$aei == 0])
  
  n011 <- nrow(flat[flat$d1i == 0 & flat$d2i == 1 & flat$aei == 1])
  n010 <- nrow(flat[flat$d1i == 0 & flat$d2i == 1 & flat$aei == 0])
  
  n001 <- nrow(flat[flat$d1i == 0 & flat$d2i == 0 & flat$aei == 1])
  n000 <- nrow(flat[flat$d1i == 0 & flat$d2i == 0 & flat$aei == 0])
  
  #total cell count
  n... <- nrow(flat)
  
  #we need to recode this as the BCPNN paper codes the pairs differently, drug-ae-drug instead of drug-drug-ae, basically switching the 2nd index with third
  
  # Recode the counts based on BCPNN paper convention
  n111r <- n111  # This remains the same
  n110r <- n101  # Drug-AE-No Drug
  n101r <- n110  # Drug-No AE-Drug
  n011r <- n011  # No Drug-AE-Drug (same)
  n100r <- n100  # Drug-No AE-No Drug (same)
  n000r <- n000  # No Drug-No AE-No Drug (same)
  n010r <- n001  # No Drug-AE-No Drug
  n001r <- n010  # No Drug-No AE-Drug

  # Marginal totals using recoded values
  n1..r <- n111r + n110r + n101r + n100r  # Drug 1 = Yes (regardless of Drug 2 and AE)
  n0..r <- n010r + n011r + n000r + n001r  # Drug 1 = No
  n.1.r <- n111r + n011r + n101r + n001r  # Adverse Event = Yes (regardless of drugs)
  n.0.r <- n110r + n010r + n100r + n000r  # Adverse Event = No
  n..1r <- n111r + n110r + n011r + n001r  # Drug 2 = Yes (regardless of Drug 1 and AE)
  n..0r <- n101r + n100r + n010r + n000r  # Drug 2 = No

  # Pairwise marginals using recoded values
  n11.r <- n111r + n110r  # Drug 1 = Yes, Drug 2 = Yes
  n10.r <- n101r + n100r  # Drug 1 = Yes, Drug 2 = No
  n01.r <- n011r + n001r  # Drug 1 = No, Drug 2 = Yes
  n00.r <- n010r + n000r  # Drug 1 = No, Drug 2 = No

  n1.1r <- n111r + n101r  # Drug 1 = Yes, Adverse Event = Yes
  n1.0r <- n110r + n100r  # Drug 1 = Yes, Adverse Event = No
  n0.1r <- n011r + n001r  # Drug 1 = No, Adverse Event = Yes
  n0.0r <- n010r + n000r  # Drug 1 = No, Adverse Event = No

  n.11r <- n111r + n011r  # Drug 2 = Yes, Adverse Event = Yes
  n.10r <- n110r + n010r  # Drug 2 = Yes, Adverse Event = No
  n.01r <- n101r + n001r  # Drug 2 = No, Adverse Event = Yes
  n.00r <- n100r + n000r  # Drug 2 = No, Adverse Event = No

  # Storing all counts in a list with recoded values
  counts <- list(
  # Individual cell counts
  n111 = n111r, n110 = n110r, n101 = n101r, n100 = n100r,
  n011 = n011r, n010 = n010r, n001 = n001r, n000 = n000r,

  # Marginal totals
  n1.. = n1..r, n0.. = n0..r,
  n.1. = n.1.r, n.0. = n.0.r,
  n..1 = n..1r, n..0 = n..0r,

  # Pairwise marginals
  n11. = n11.r, n10. = n10.r, n01. = n01.r, n00. = n00.r,
  n1.1 = n1.1r, n1.0 = n1.0r, n0.1 = n0.1r, n0.0 = n0.0r,
  n.11 = n.11r, n.10 = n.10r, n.01 = n.01r, n.00 = n.00r,

  # Total count
  n... = n111r + n110r + n101r + n011r + n100r + n010r + n001r + n000r
)

return(counts)
}

The cell counts correspond to the following table

Contingency Table Format for Extended BCPNN
Contingency Table Format for Extended BCPNN

Step 2: Function to Compute Extended IC

get_ic_result <- function(n111, n110, n101, n011, n100, n010, n001, n000, n...) {
  
  #Prior calculation:
  
  #Smoothed marginal Probabilities
  q1.. <- (n111 + n110 + n101 + n100 + 0.5) / (n... + 1) #Expected value for drug 1
  q0.. <- (n011 + n010 + n001 + n000 + 0.5) / (n... + 1) #Expected value for no drug 1
  q.1. <- (n111 + n011 + n101 + n001 + 0.5) / (n... + 1) #Expected value for ae
  q.0. <- (n110 + n010 + n100 + n000 + 0.5) / (n... + 1) #Expected value for no ae
  q..1 <- (n111 + n110 + n011 + n001 + 0.5) / (n... + 1) #Expected value for drug 2
  q..0 <- (n101 + n100 + n010 + n000 + 0.5) / (n... + 1) #Expected value for no drug 2

  #Smoothed pairwise Probabilities
  q11. <- (n111 + n110 + 0.25) / (n... + 1) #Expected drug 1 and ae
  q10. <- (n101 + n100 + 0.25) / (n... + 1) #Expected drug 1 and no ae
  q01. <- (n011 + n001 + 0.25) / (n... + 1) #Expected no drug 1 and ae
  q00. <- (n010 + n000 + 0.25) / (n... + 1) #Expected no drug 1 and no ae
  q1.1 <- (n111 + n101 + 0.25) / (n... + 1) #Expected drug 1 and drug 2
  q1.0 <- (n110 + n100 + 0.25) / (n... + 1) #Expected drug 1 and no drug 2
  q0.1 <- (n011 + n001 + 0.25) / (n... + 1) #Expected no drug 1 and drug 2
  q0.0 <- (n010 + n000 + 0.25) / (n... + 1) #Expected no drug 1 and no drug 2
  q.11 <- (n111 + n011 + 0.25) / (n... + 1) #Expected drug 2 and ae
  q.10 <- (n110 + n010 + 0.25) / (n... + 1) #Expected drug 2 and no ae
  q.01 <- (n101 + n001 + 0.25) / (n... + 1) #Expected no drug 2 and ae
  q.00 <- (n100 + n000 + 0.25) / (n... + 1) #Expected no drug 2 and no ae

  # Step 2: Compute hyperparameters for moderating prior for third order IC
  a... <- 0.5 * ((q1.. * q.1. * q..1) / (q11. * q1.1 * q.11))

  a111 <- ((q11. * q1.1 * q.11) / (q1.. * q.1. * q..1)) * a...
  a110 <- ((q11. * q1.0 * q.10) / (q1.. * q.1. * q..0)) * a...
  a101 <- ((q10. * q1.1 * q.01) / (q1.. * q.0. * q..1)) * a...
  a100 <- ((q10. * q1.0 * q.00) / (q1.. * q.0. * q..0)) * a...
  a011 <- ((q01. * q0.1 * q.11) / (q0.. * q.1. * q..0)) * a...
  a010 <- ((q01. * q0.0 * q.10) / (q0.. * q.1. * q..0)) * a...
  a001 <- ((q00. * q0.1 * q.01) / (q0.. * q.0. * q..1)) * a...
  a000 <- ((q00. * q0.0 * q.00) / (q0.. * q.0. * q..0)) * a...

  # Step 3: Compute posterior probabilities (updating the prior)
  gam111 <- a111 + n111
  gam110 <- a110 + n110
  gam101 <- a101 + n101
  gam011 <- a011 + n011
  gam100 <- a100 + n100
  gam010 <- a010 + n010
  gam001 <- a001 + n001
  gam000 <- a000 + n000

  # Sum of all gamma values
  gamma_sum <- sum(gam111, gam110, gam101, gam011, gam100, gam010, gam001, gam000)

  # Step 4: Compute joint probabilities
  Ep111 <- gam111 / gamma_sum
  Ep110 <- gam110 / gamma_sum
  Ep101 <- gam101 / gamma_sum
  Ep011 <- gam011 / gamma_sum
  Ep100 <- gam100 / gamma_sum
  Ep010 <- gam010 / gamma_sum
  Ep001 <- gam001 / gamma_sum
  Ep000 <- gam000 / gamma_sum

  # Step 5: Compute marginal probabilities
  Ep11. <- (gam111 + gam110) / gamma_sum
  Ep1.1 <- (gam111 + gam101) / gamma_sum
  Ep.11 <- (gam111 + gam011) / gamma_sum
  Ep1.. <- (gam111 + gam110 + gam101 + gam100) / gamma_sum
  Ep.1. <- (gam111 + gam110 + gam011 + gam010) / gamma_sum
  Ep..1 <- (gam111 + gam101 + gam011 + gam001) / gamma_sum

  # Step 6: Compute third-order IC MAP estimate
  ic_map <- log2((Ep111 * Ep1.. * Ep.1. * Ep..1) / (Ep11. * Ep1.1 * Ep.11))

  # Step 7: Compute ratios for third-order combinations, uncertainty adjustment
  r111 <- round(gam111 / pmin(gam111 + gam110 + gam101, gam111 + gam011 + gam101, gam111 + gam110 + gam011), 1)

  # Step 8: Look up Ar and Br based on r111
  id <- c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1)
  Alist <- c(3.09, 2.93, 2.78, 2.62, 2.45, 2.25, 2.03, 1.79, 1.61, 1.13, 0.073)
  Blist <- c(2.22, 2.27, 2.26, 2.25, 2.15, 2.12, 2.05, 1.93, 1.89, 1.15, -0.081)

  Ar <- Alist[which(id == r111)]
  Br <- Blist[which(id == r111)]

  # Step 9: Compute Delta
  delta <- Ar / sqrt(gam111) + Br * gam111^(-1.5)

  # Step 10: Compute Confidence Intervals
  IC_LB <- ic_map - delta
  IC_UB <- ic_map + delta

  # Step 11: Return results as a list
  return(list(
    IC = ic_map,
    IC_LB = IC_LB,
    IC_UB = IC_UB
  ))
}

Step 2: Calling the Function to Generate Table and Result

Generating the Table

data <- fread(file_path)
data$pt <- toupper(data$pt)
setnames(data, old = names(data)[1], new = "id")

ic_counts <- as.data.table(get_ic_table(d1 = d1_study, d2 = d2_study, ae = ae_study, merged = data))

ic_counts[, `:=` (d1 = d1_study, d2 = d2_study, ae = ae_study)]
setcolorder(ic_counts, c("d1", "d2", "ae"))


list2env(ic_counts, envir = .GlobalEnv)
## <environment: R_GlobalEnv>

Computing the IC

ic_result <- as.data.table(get_ic_result(n111 = n111, 
                           n110 = n110,
                           n101 = n101,
                           n011 = n011,
                           n100 = n100, 
                           n010 = n010,
                           n001 = n001,
                           n000 = n000,
                           n... = n...))


ic_result[, `:=` (d1 = d1_study, d2 = d2_study, ae = ae_study)]
setcolorder(ic_result, c("d1", "d2", "ae"))
DT::datatable(ic_counts, options = list(pageLength = 10, scrollX = TRUE))
DT::datatable(ic_result, options = list(pageLength = 10, scrollX = TRUE))