#Required packages:
install.packages("statmod") 
install.packages("metafor")
install.packages("meta")
install.packages("lifecycle") 
install.packages("readr")
install.packages("tidyverse")
install.packages('questionr')
install.packages('dplyr')
install.packages("pacman")    # Cite when using orchard plots. Full reference here: https://doi.org/10.1111/2041-210X.14152
install.packages("devtools")
install.packages("orchaRd")   # for overall pooled effect and meta-regression
install.packages("ggplot2")
install.packages("grid")      # for forest plot titles/subtitles
install.packages("broom")

# orchaRd installation
 rm(list = ls())
 devtools::install_github("daniel1noble/orchaRd", ref = "main", force = TRUE)
 pacman::p_load(devtools, tidyverse, metafor, patchwork, R.rsp, orchaRd, emmeans,
                ape, phytools, flextable)

#Loading packages
library(statmod)
library(meta)
library(metafor)
library(lifecycle)
library(readr)
library(tidyverse)
library(questionr)
library(readxl)
library(dplyr)
library(pacman)
library(orchaRd)
library(ggplot2)
library(grid)
library(broom)

######################### OUTCOME: OPIOID CONSUMPTION ################################
# Read and view data
dataopicons <- read.csv("dataopioidcons.csv")

# Overall analysis
# Calculate Mean Difference (MD = yi) as effect size and its Variance (MDV = vi)

opiconsMD <- escalc(measure = "MD",        #Specify Effect Size Measure
                      m1i = meanexp,       
                      m2i = meanctrl, 
                      sd1i = sdexp, 
                      sd2i = sdctrl, 
                      n1i = nexp, 
                      n2i = nctrl, 
                      data = dataopicons,
                      append = TRUE)
 
dataopicons <- opiconsMD #add MD (yi) and MDV (vi) to the original data set
head(dataopicons) #check if yi and vi are now included

# Perform multilevel (ML) MA of Mean Difference (MD) in opioid consumption
# Calculate variance-covariance (VCV) matrix to account for correlated/non-independent errors
opiconsMDVCV <- vcalc(vi = vi, 
             cluster = studyID, 
             obs = compID,
             data = dataopicons, 
             rho = 0.5)

# Perform multilevel meta-analytic model using the constructed VCV matrix (VCV)
opiconsMD_ML <- rma.mv(yi = yi, 
                     V = opiconsMDVCV,     #VCV matrix
                     random = list(~1 | studyID/compID), #compID is nested within studyID, not a separate source of random variation
                     method = "REML", 
                     data = dataopicons)

summary(opiconsMD_ML)                  #show MA results

# Assess I2 levels and extract between-study I2
opiconsi2 <- i2_ml(opiconsMD_ML)       #breaks down the I2 into the ML levels
opiconsi2                              #show the I2 per level

# Extract Q-statistic and p-value
opicons_overallp <- opiconsMD_ML$pval
opiconsQ_value <- opiconsMD_ML$QE
opiconsQ_pvalue <- opiconsMD_ML$QEp

# Display the heterogeneity statistics
print(paste("I² = ", round(opiconsi2[1],0), "%, Q = ", round(opiconsQ_value, 1), ", p = ", round(opiconsQ_pvalue, 7)))

# Forest plot of multilevel meta-analysis model with VCV
png(filename="FPopiconsMD_ML.png", res=200, width=1600, height=1800) #set up a png file

FPopiconsMD_ML<- forest(opiconsMD_ML, 
             annotate=TRUE, 
             showweights=TRUE,
             slab = dataopicons$compID,
             predstyle = "bar",            #adds prediction interval as bar
             order=dataopicons$compID,     #effect sizes sorted by compid
             main = "MLMA Opioid (equivalent) consumption 24h post-op",    
             header=c("Comparison", "  Weights     MD            95% CI "), 
             digits = c(1,2,1),            #number of decimals for ESCI, SE and weights 
             xlim=c(-80,65),               #horizontal limits of the plot region 
             at=seq(-50,25, by=5),         #position of the x-axis tick marks and corresponding labels
             efac=c(0.1,1),                #length of ticks on error bars
             xlab = "          Favours experimental <-- 0 --> Favours control",     #x-axis label
               )

# Add heterogeneity statistics to the plot
mtext(side = 1, line = 4, 
      text = paste("I² = ", round(opiconsi2[1],0), "%, Q = ", round(opiconsQ_value, 2), ", p = ", round(opiconsQ_pvalue, 3)), 
      cex = 0.8)

dev.off()

#------- Opioid consumption: exploring heterogeneity (post-hoc)-----------------------# 
#-------- Moderator: added multimodal component----------------------------#

freq(dataopicons$mmc_added)
#          n    % val%
#clonidine  1  4.2  4.2
#dexa       3 12.5 12.5
#dexmed     3 12.5 12.5
#ketamine   4 16.7 16.7
#lidocaine  3 12.5 12.5
#magnesium  7 29.2 29.2
#pregabalin 3 12.5 12.5

# N.B. limited power due to many small strata

# Include moderator into the model, regardless of categorical or continuous
opicons_mmcomp  <- rma.mv(yi = yi, 
                  V = opiconsMDVCV, 
                  random = list(~1 | studyID/compID),     #compID is nested within studyID, not a separate source of random variation
                  mods = ~ mmc_added,                     #column of the tested moderator
                  test = "t", 
                  method = "REML", 
                  data = dataopicons)

summary(opicons_mmcomp)

opiconsi2_mmcomp <- i2_ml(opicons_mmcomp)       #breaks down the I2 into the ML levels
opiconsi2_mmcomp                                #show the I2 per level

# Orchard plot visualizing the regression analysis
OPopicons_mmcomp<- orchard_plot(opicons_mmcomp,
                               mod = "mmc_added",
                               group = "studyID",
                               angle = 0,
                               xlab = "Mean Difference in opioid (equivalent) consumption 24h post-op",       # X-axis label
                               alpha = 0.5,                    # CI fill transparency
                               col = "darkblue"                # point and CI color
                                ) +
    annotate(geom="text", x= 1, y= -40, label= paste0("italic(I)^{2} == ", round(opiconsi2_mmcomp[1],0), "*\"%\""), 
           color="black", parse = TRUE, size = 3.5)

ggsave("OPopicons_mmcomp.png", width = 7, height = 5, dpi = 300)

#######Sensitivity analysis: remove non-parametric data (means+SDs recalculated from medians+IQR)######

sens_dataopicons <- dataopicons %>%
  filter(orgdataformat != "medianiqr_recalc")

sens_opiconsMDVCV <- vcalc(vi = vi, 
                      cluster = studyID, 
                      obs = compID,
                      data = sens_dataopicons, 
                      rho = 0.5)

sens_opiconsMD_ML <- rma.mv(yi = yi, 
                       V = sens_opiconsMDVCV,                   #VCV matrix
                       random = list(~1 | studyID/compID),      #compID is nested within studyID, not a separate source of random variation
                       method = "REML", 
                       data = sens_dataopicons)

summary(sens_opiconsMD_ML)                       #show MA results
sens_opiconsi2 <- i2_ml(sens_opiconsMD_ML)       #breaks down the I2 into the ML levels
sens_opiconsi2                                   #show the I2 per level

######################## sensitivity analysis - applied cluster-robust variance estimation at the study level#############
opiconsMD_ML_robust <- robust(opiconsMD_ML, cluster = studyID)
summary(opiconsMD_ML_robust)


############################## OUTCOME: PAIN SCALES  ##############################
#read and view data
datapain <- read.csv("datapain.csv")

#----------------------------------------------------------------------------- 
# Overall analysis
# Calculate Mean Difference (MD = yi) as effect size and its Variance (MDV = vi)

painMD <- escalc(measure = "MD", 
                     m1i = meanexp, 
                     m2i = meanctrl, 
                     sd1i = sdexp, 
                     sd2i = sdctrl, 
                     n1i = nexp, 
                     n2i = nctrl, 
                     data = datapain,
                     append = TRUE)

# add MD and MDV to the original data set
datapain <- painMD
head(datapain) #check if yi and vi are now included

# Subset for 4h timepoint
datapain4h <- subset(datapain, timepoint == 4)

# Perform multilevel (ML) MA of Mean Difference (MD) in pain
# Calculate variance-covariance (VCV) matrix to account for correlated/non-independent errors
pain4hMDVCV <- vcalc(vi = vi, 
                    cluster = studyID, 
                    obs = compID,
                    data = datapain4h,
                    rho = 0.5)

# multilevel meta-analytic model using the constructed VCV matrix (VCV)
pain4hMD_ML <- rma.mv(yi = yi, 
                            V = pain4hMDVCV,                    # VCV matrix
                            random = list(~1 | studyID/compID), #compID is nested within studyID, not a separate source of random variation
                            method = "REML", 
                            data = datapain4h)

summary(pain4hMD_ML)

# Investigating I2 and extract between-study I2
pain4hi2 <- i2_ml(pain4hMD_ML)       #breaks down the I2 into the ML levels
pain4hi2                              #show the I2 per level

# Extract Q-statistic and p-value
pain4h_overallp <- pain4hMD_ML$pval
pain4hQ_value <- pain4hMD_ML$QE
pain4hQ_pvalue <- pain4hMD_ML$QEp

# Display the heterogeneity statistics
print(paste("I² = ",round(pain4hi2[1],0), "%, Q = ", round(pain4hQ_value, 2), ", p = ", round(pain4hQ_pvalue, 3)))

# Forest plot of multilevel meta-analysis model with VCV
png(filename="FPpain4hMD_ML.png", res=200, width=1600, height=1800) #set up a png file

FPpain4hMD_ML<- forest(pain4hMD_ML, 
                        annotate=TRUE, 
                        showweights=TRUE,
                        slab = datapain4h$compID, 
                        predstyle = "bar",            #adds prediction interval as bar
                        #order=datapain$compID,     #effect sizes sorted by compid
                        main = "MLMA pain scale score 4h post-op",    
                        header=c("Comparison", "  Weights     MD            95% CI "), 
                        digits = c(1,2,1),            #number of decimals for ESCI, SE and weights 
                        xlim=c(-7,7),               #horizontal limits of the plot region 
                        at=seq(-3,3, by=0.5),         #position of the x-axis tick marks and corresponding labels
                        efac=c(0.1,1),                #length of ticks on error bars
                        xlab = "          Favours experimental <-- 0 --> Favours control",     #x-axis label
)

# Add heterogeneity statistics to the plot
mtext(side = 1, line = 4, 
      text = paste("I² = ", round(pain4hi2[1],0), "%, Q = ", round(pain4hQ_value, 2), ", p = ", round(pain4hQ_pvalue, 3)), 
      cex = 0.8)

dev.off()

#------- Pain at 4 hours post-op: exploring heterogeneity (post-hoc)-----------------------# 
#-------- Moderator: added multimodal component----------------------------#

freq(datapain4h$mmc_added)
#          n    % val%
#dexa       7 24.1 24.1
#dexmed     4 13.8 13.8
#ketamine   6 20.7 20.7
#lidocaine  4 13.8 13.8
#magnesium  3 10.3 10.3
#pregabalin 5 17.2 17.2

# N.B. limited power due to many small strata

# Code to include moderator into the model, regardless of categorical or continuous
pain4h_mmcomp  <- rma.mv(yi = yi, 
                         V = pain4hMDVCV, 
                         random = list(~1 | studyID/compID),     #compID is nested within studyID, not a separate source of random variation
                         mods = ~ mmc_added,                     #column of the tested moderator
                         test = "t", 
                         method = "REML", 
                         data = datapain4h)

summary(pain4h_mmcomp)

# Investigating I2 and extract between-study I2
pain4hi2_mmcomp <- i2_ml(pain4h_mmcomp)       #breaks down the I2 into the ML levels
pain4hi2_mmcomp                              #show the I2 per level

####### create orchardplot of added multimodal components #############
OPpain4h_mmcomp<- orchard_plot(
                  mod = "mmc_added",          # match moderator name exactly
                  object = pain4h_mmcomp,
                  xlab = "Mean Difference in pain scale score",
                  transfm = "none",           # no transformation needed for MD
                  angle = 0,                 # rotate x-axis labels if needed
                  alpha = 0.5,                # transparency for raw data dots
                  col = "darkblue",                # point and CI color
                  group = "studyID"
) +
  annotate(geom="text", x= 1, y= -2, label= paste0("italic(I)^{2} == ", round(pain4hi2_mmcomp[1],0), "*\"%\""), 
           color="black", parse = TRUE, size = 3.5)

ggsave("OPpain4h_mmcomp.png", width = 7, height = 5, dpi = 300)


#---------Sensitivity analysis: remove non-parametric data (means+SDs recalculated from medians+IQR)---------#
###########
sens_datapain4h <- datapain4h %>%
  filter(orgdataformat != "medianiqr_recalc")

sens_pain4hMDVCV <- vcalc(vi = vi, 
                           cluster = studyID, 
                           obs = compID,
                           data = sens_datapain4h, 
                           rho = 0.5)

sens_pain4hMD_ML <- rma.mv(yi = yi, 
                            V = sens_pain4hMDVCV,     #VCV matrix
                            random = list(~1 | studyID/compID), 
                            method = "REML", 
                            data = sens_datapain4h)

summary(sens_pain4hMD_ML)                       #show MA results
sens_pain4hi2 <- i2_ml(sens_pain4hMD_ML)       #breaks down the I2 into the ML levels
sens_pain4hi2                                   #show the I2 per level

######################## sensitivity analysis - applied cluster-robust variance estimation at the study level#############
pain4hMD_ML_robust <- robust(pain4hMD_ML, cluster = studyID)
summary(pain4hMD_ML_robust)

#---------Sensitivity analysis: change MD to SMD ---------#
###########
#read and view data
datapainSMD <- read.csv("datapain.csv")
painSMD <- escalc(measure = "SMD", 
                 m1i = meanexp, 
                 m2i = meanctrl, 
                 sd1i = sdexp, 
                 sd2i = sdctrl, 
                 n1i = nexp, 
                 n2i = nctrl, 
                 data = datapain,
                 append = TRUE)

# add SMD and SMDV to the original data set
datapainSMD <- painSMD
head(datapainSMD) #check if yi and vi are now included

# Subset for 4h timepoint
datapain4hSMD <- subset(datapainSMD, timepoint == 4)

# Perform multilevel (ML) MA of Standardized Mean Difference (SMD) pain
# Calculate variance-covariance (VCV) matrix to account for correlated/non-independent errors
pain4hSMDVCV <- vcalc(vi = vi, 
                     cluster = studyID, 
                     obs = compID,
                     data = datapain4hSMD,
                     rho = 0.5)

# multilevel meta-analytic model using the constructed VCV matrix (VCV)
pain4hSMD_ML <- rma.mv(yi = yi, 
                      V = pain4hSMDVCV,                    # VCV matrix
                      random = list(~1 | studyID/compID), #compID is nested within studyID, not a separate source of random variation
                      method = "REML", 
                      data = datapain4hSMD)

summary(pain4hSMD_ML)

# Investigating I2 and extract between-study I2
pain4hSMDi2 <- i2_ml(pain4hSMD_ML)       #breaks down the I2 into the ML levels
pain4hSMDi2                              #show the I2 per level

# Extract Q-statistic and p-value
pain4hSMD_overallp <- pain4hSMD_ML$pval
pain4hSMDQ_value <- pain4hSMD_ML$QE
pain4hSMDQ_pvalue <- pain4hSMD_ML$QEp

# Display the heterogeneity statistics
print(paste("I² = ", round(pain4hSMDi2[1],0) , "%, Q = ", round(pain4hSMDQ_value, 2), ", p = ", round(pain4hSMDQ_pvalue, 3)))

# Forest plot of multilevel meta-analysis model with VCV
png(filename="FPpain4hSMD_ML.png", res=200, width=1600, height=1800) #set up a png file

FPpain4hSMD_ML<- forest(pain4hSMD_ML, 
                       annotate=TRUE, 
                       showweights=TRUE,
                       slab = datapain4hSMD$compID, 
                       predstyle = "bar",            #adds prediction interval as bar
                       #order=datapainSMD$compID,     #effect sizes sorted by compid
                       main = "MLMA pain scale score 4h post-op",    
                       header=c("Comparison", "  Weights     SMD            95% CI "), 
                       digits = c(1,2,1),            #number of decimals for ESCI, SE and weights 
                       xlim=c(-7,7),               #horizontal limits of the plot region 
                       at=seq(-3,3, by=0.5),         #position of the x-axis tick marks and corresponding labels
                       efac=c(0.1,1),                #length of ticks on error bars
                       xlab = "          Favours experimental <-- 0 --> Favours control",     #x-axis label
)

# Add heterogeneity statistics to the plot
mtext(side = 1, line = 4, 
      text = paste("I² = ", round(pain4hSMDi2[1],0), "%, Q = ", round(pain4hSMDQ_value, 2), ", p = ", round(pain4hSMDQ_pvalue, 3)), 
      cex = 0.8)

dev.off()


######################### PAIN over timepoints ################################
# the escalc aleady existis in painMD for all timepoints

painallMDVCV <- vcalc(vi = vi, 
                     cluster = studyID, 
                     obs = compID,
                     data = datapain,
                     subgroup = timepoint, #only comparisons within the same study and same timepoint are assumed correlated
                     rho = 0.8)            #assumed strong correlation between timepoints of the same study (standard is 0.5)

# multilevel meta-analytic model using the constructed VCV matrix (VCV)
painallMD_ML <- rma.mv(yi = yi, 
                      V = painallMDVCV, # VCV matrix
                      random = list(~1 | studyID/compID), #compID is nested within studyID, not a separate source of random variation
                      method = "REML", 
                      data = datapain)

summary(painallMD_ML)

# test timepoint as moderator
datapain$timepoint <- factor(datapain$timepoint)  # do this before model fitting

painallMD_ML_timepoint <- rma.mv(yi = yi, 
                       V = painallMDVCV, # VCV matrix
                       random = list(~1 | studyID/compID), #compID is nested within studyID, not a separate source of random variation
                       method = "REML", 
                       mods = ~ factor(timepoint), #enable to test timepoint as modifier
                       data = datapain)

summary(painallMD_ML_timepoint)

####### create orchardplot of timepoints #############
orchard_plot(
  object = painallMD_ML_timepoint,
  mod = "timepoint",          # match moderator name exactly
  xlab = "Mean Difference (MD)",
  transfm = "none",           # no transformation needed for MD
  angle = 45,                 # rotate x-axis labels if needed
  alpha = 0.5,                # transparency for raw data dots
  group = TRUE,               # show study-level points
  g = "studyID"               # grouping variable from your random effects
) 
ggsave("OPpainall_timepoints.png", width = 7, height = 5, dpi = 400)

#-----------------------------------------------------------------------------
#Publication bias - funnel plots

# Funnel plot for opioid consumption
png(filename="FUNopiconsMD_ML.png", res=200, width=1600, height=1600) #set up a png file
funnel(opiconsMD_ML, main = "Funnel plot - Opioid Consumption")
dev.off()

# Funnel plot for pain at 4h
png(filename="FUNpain4hMD_ML.png", res=200, width=1600, height=1600) #set up a png file
funnel(pain4hMD_ML, main = "Funnel plot - Pain Scores at 4h")
dev.off()

#run regtests on unilevel MA (not available for multilevel)
regtest(rma(yi, vi, data = dataopicons), model = "rma", predictor = "sei")
regtest(rma(yi, vi, data = datapain4h), model = "rma", predictor = "sei")


#END OF ANALYSIS#
