knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
rm(list = ls())
##################################
##' Surgey Cancellation ##
##' Last edited: Dec 31, 2022. ##
##################################
## [1] Set file paths
genpath <- "~jonathansmith/Desktop/Surgery Project/" # general file path (for later use)
datapath <- paste0(genpath,"Data/December_2022_Master_Log_Case_Cancellation_Study.xlsx") # data file path (for importing)
## [2] Import data from excel (using readxl) and create datasets
# xl_list <- lapply(readxl::excel_sheets(datapath), readxl::read_excel, path = datapath)
# names(xl_list) <- gsub(" ", "", readxl::excel_sheets(datapath))
# list2env(xl_list, .GlobalEnv)
# rm(xl_list, datapath)
#' NOTE: importing from Excel gave wonky results that did not align with
#' the Excel sheets, likely due to formatting in the Excel document. Converted
#' all to CSVs and directly imported since only three sheets:
caselog <- read.csv(paste0(genpath, "/Data/caselog.csv"), na.strings = c(""))
afterhours <- read.csv(paste0(genpath, "/Data/afterhourslog.csv"), na.strings = c(""))
daily <- read.csv(paste0(genpath, "/Data/dailyreport.csv"), na.strings = c(""))
########################################################################################################################
## [3] Recode some variables
########################################################################################################################
names(caselog) <- tolower(names(caselog)) # convert all names to lower case for convenience
names(caselog)[c(2, 15)] <- c("id", "surgerydone") #rename key variables for convenience
# reformat imported numerical representation of a date to a proper date format
caselog$date <- as.Date(caselog$date, origin = "1899-12-30")
caselog$gender[caselog$gender %in% "UNKNOWN"] <- NA
#' Removing rows with unusable or questionable data
#' [1] during camps - c("HIP REPLACEMENT CAMP", "DERMATOLOGY CAMP", "UROLOGY CAMP")
#' [2] 14 SIDs with peculiar data
# this is parsimoniously done by simply removing those without the age variables
caselog <- caselog[!is.na(caselog$rounded.age) & !is.na(caselog$age), ]
#' coalesce reasons cor cancellation into one column
#' Will actually create two for posterity:
#' ["reason_original"] - retain original input value (typically "YES", but occasionally have other contextual information)
#' ["reason"] - replace value with column name from which it was derived (relevant analytically)
caselog[, c("reason_original", "reason")] <- cbind(caselog[16:35],
t(apply(caselog[16:35], 1, function(i){
c(i[which(!is.na(i))[1]],
colnames(caselog[16:35])[which(!is.na(i))[1]])
})))[21:22]
# Format the "reason" variable to be more palatable
#' User-defined function to choose case (i.e., sentence or title case)
#' @param x any string
#' @param type defaults to sentence case
fixcase <- function(x, type = c("sentence", "title", "allcaps")) {
x <- tolower(x)
x <- trimws(x, which = "both")
if("sentence" %in% tolower(type)){
substr(x, 1, 1) <- toupper(substr(x, 1, 1))
x}
else{
if("title" %in% tolower(type)){
tools::toTitleCase(x)
} else {
if("allcaps" %in% tolower(type)){
toupper(x)
}
}
}
}
# Format the responses
caselog[,"reason"] <- fixcase(gsub("\\.", " ", # remove dots in between words
gsub("\\..reason", "", # remove extraneous "..reason"
caselog[,"reason"])
))
# same for original reason column, though no formatting
caselog[,"reason_original"] <- fixcase(caselog[,"reason_original"])
# identify reasons that were *never* the cause of a cancellation
none_reason <- fixcase(gsub("\\.", " ", # remove dots in between words
gsub("\\..reason", "", # remove extraneous "..reason"
names(caselog[, colSums(is.na(caselog)) == nrow(caselog)]))
))
caselog$ward <- fixcase(caselog$ward, type = "allcaps")
# Three entries have FLOOR with additional details - removing those
caselog$ward[grepl("\\(", caselog$ward)] <- "FLOOR"
##' @TODO fix to ensure it properly separate (sometimes separates on the second hyphen)
caselog[, c("procname","diagname")] <- strcapture("(.*)-(.*)",
caselog$procedure.name...diagnosis.name,
data.frame(procname = "", diagname = ""))
caselog$procname <- ifelse(is.na(caselog$procname) & !is.na(caselog$procedure.name...diagnosis.name),
caselog$procedure.name...diagnosis.name,
caselog$procname)
caselog[c("procname","diagname")] <- lapply(caselog[c("procname", "diagname")], trimws, which = "both")
#caselog[grep("-", caselog$procname), c("procedure.name...diagnosis.name", "procname", "diagname")]
# remove columns no longer needed
caselog <- caselog[, -c(4, 13, 16:35, 40)]
# Rename for convenience
names(caselog) <- c("entryno", "id", "date", "gender", "ward", "district",
"occu", "firstcase", "starttime_firstcase",
"delay_reason", "surgtype", "specialty", "surgerydone", "vis_repeatno",
"daystillcomp", "completion_status", "notes",
"age", "reason_original", "reason",
"procname", "diagname")
# Create age group variables
caselog$agegrp <- findInterval(caselog$age, c(15, 30, 45, 65), left.open = TRUE)
#with(caselog, table(age, agegrp))
caselog[-c(3, 18)] <- lapply(caselog[-c(3, 18)], as.factor)
levels(caselog$surgerydone) <- c("Surgery Not Done", "Surgery Completed")
levels(caselog$agegrp) <- c("0-15 Years", "16-30 Years", "31-45 Years", "46-60 Years", "60+ Years")
levels(caselog$gender) <- fixcase(levels(caselog$gender))
totab <- table(caselog$surgerydone)
totmat <- matrix(c(totab,
paste0(sprintf("%.1f", prop.table(totab)*100),"%")), ncol = 2)
totvec <- paste0(totmat[,1], " (", totmat[,2], ")")
totdf <- data.frame(names(totab), totvec)
names(totdf) <- c("Surgery Status", "n (%)")
knitr::kable(totdf)
| Surgery Status | n (%) |
|---|---|
| Surgery Not Done | 832 (27.3%) |
| Surgery Completed | 2220 (72.7%) |
tab1Vars <- c("gender", "agegrp", "ward", "district", "firstcase", "surgtype", "specialty", "age")
table1::label(caselog$gender) <- "Sex"
table1::label(caselog$agegrp) <- "Age Group"
table1::label(caselog$firstcase) <- "First Case"
table1::label(caselog$surgtype) <- "Type of Surgery"
table1::label(caselog$specialty) <- "Surgical Specialty"
table1::label(caselog$ward) <- "Ward"
table1::label(caselog$district) <- "District"
table1::label(caselog$age) <- "Age"
tab1Form <- as.formula(paste0("~ ", paste(tab1Vars, collapse = " + "), " | surgerydone"))
table1::table1(tab1Form, data = caselog, overall = "Total")
| Surgery Not Done (N=832) |
Surgery Completed (N=2220) |
Total (N=3052) |
|
|---|---|---|---|
| Sex | |||
| Female | 205 (24.6%) | 586 (26.4%) | 791 (25.9%) |
| Male | 626 (75.2%) | 1634 (73.6%) | 2260 (74.0%) |
| Missing | 1 (0.1%) | 0 (0%) | 1 (0.0%) |
| Age Group | |||
| 0-15 Years | 224 (26.9%) | 734 (33.1%) | 958 (31.4%) |
| 16-30 Years | 177 (21.3%) | 420 (18.9%) | 597 (19.6%) |
| 31-45 Years | 218 (26.2%) | 536 (24.1%) | 754 (24.7%) |
| 46-60 Years | 156 (18.8%) | 368 (16.6%) | 524 (17.2%) |
| 60+ Years | 57 (6.9%) | 162 (7.3%) | 219 (7.2%) |
| Ward | |||
| FLOOR | 826 (99.3%) | 2199 (99.1%) | 3025 (99.1%) |
| HDU | 4 (0.5%) | 17 (0.8%) | 21 (0.7%) |
| ICU | 2 (0.2%) | 1 (0.0%) | 3 (0.1%) |
| ONCOLOGY | 0 (0%) | 1 (0.0%) | 1 (0.0%) |
| SHDU | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| District | |||
| NTCHEU | 0 (0%) | 1 (0.0%) | 1 (0.0%) |
| BALAKA | 4 (0.5%) | 12 (0.5%) | 16 (0.5%) |
| BLANTYRE | 0 (0%) | 3 (0.1%) | 3 (0.1%) |
| CHITIPA | 0 (0%) | 6 (0.3%) | 6 (0.2%) |
| DEDZA | 65 (7.8%) | 146 (6.6%) | 211 (6.9%) |
| DOWA | 55 (6.6%) | 111 (5.0%) | 166 (5.4%) |
| KARONGA | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| KASUNGU | 27 (3.2%) | 108 (4.9%) | 135 (4.4%) |
| KASUNGU | 1 (0.1%) | 2 (0.1%) | 3 (0.1%) |
| LILONGWE | 265 (31.9%) | 929 (41.8%) | 1194 (39.1%) |
| MACHINGA | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| MANGOCHI | 1 (0.1%) | 0 (0%) | 1 (0.0%) |
| MCHINJI | 32 (3.8%) | 84 (3.8%) | 116 (3.8%) |
| MTCHINJI | 1 (0.1%) | 0 (0%) | 1 (0.0%) |
| MULANJE | 1 (0.1%) | 2 (0.1%) | 3 (0.1%) |
| MWANZA | 0 (0%) | 1 (0.0%) | 1 (0.0%) |
| MZIMBA | 3 (0.4%) | 7 (0.3%) | 10 (0.3%) |
| MZUZU | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| NKHATABAY | 0 (0%) | 4 (0.2%) | 4 (0.1%) |
| NKHOTAKOTA | 15 (1.8%) | 64 (2.9%) | 79 (2.6%) |
| NTCHEU | 40 (4.8%) | 96 (4.3%) | 136 (4.5%) |
| NTCHEU | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| NTCHISI | 32 (3.8%) | 62 (2.8%) | 94 (3.1%) |
| PHALOMBE | 0 (0%) | 2 (0.1%) | 2 (0.1%) |
| RUMPHI | 4 (0.5%) | 1 (0.0%) | 5 (0.2%) |
| SALIMA | 36 (4.3%) | 133 (6.0%) | 169 (5.5%) |
| THYOLO | 1 (0.1%) | 1 (0.0%) | 2 (0.1%) |
| UNKNOWN | 124 (14.9%) | 42 (1.9%) | 166 (5.4%) |
| ZOMBA | 0 (0%) | 4 (0.2%) | 4 (0.1%) |
| Missing | 125 (15.0%) | 391 (17.6%) | 516 (16.9%) |
| First Case | |||
| NO | 825 (99.2%) | 1571 (70.8%) | 2396 (78.5%) |
| YES | 7 (0.8%) | 649 (29.2%) | 656 (21.5%) |
| Type of Surgery | |||
| ELECTIVE | 747 (89.8%) | 1814 (81.7%) | 2561 (83.9%) |
| EMERGENCY | 85 (10.2%) | 406 (18.3%) | 491 (16.1%) |
| Surgical Specialty | |||
| ENT | 54 (6.5%) | 170 (7.7%) | 224 (7.3%) |
| GS | 202 (24.3%) | 525 (23.6%) | 727 (23.8%) |
| NS | 2 (0.2%) | 5 (0.2%) | 7 (0.2%) |
| ORTHO | 302 (36.3%) | 639 (28.8%) | 941 (30.8%) |
| PEDS | 136 (16.3%) | 437 (19.7%) | 573 (18.8%) |
| PLASTICS | 57 (6.9%) | 158 (7.1%) | 215 (7.0%) |
| URO | 79 (9.5%) | 286 (12.9%) | 365 (12.0%) |
| Age | |||
| Mean (SD) | 31.8 (21.5) | 29.6 (22.2) | 30.2 (22.0) |
| Median [Min, Max] | 32.0 [0, 89.0] | 30.0 [0, 90.0] | 30.0 [0, 90.0] |
Statistical comparison by group (Chi-squared used for categorical; t-test for numerical (Age)). May not be meaningful for factors with a substantial number of permutatinos with low cell counts (ie, Ward and District)
pvalue <- function(x, ...) {
y <- unlist(x)
g <- factor(rep(1:length(x), times = sapply(x, length)))
if (is.numeric(y)) {
p <- t.test(y ~ g)$p.value
} else {
p <- chisq.test(table(y, g))$p.value
}
c("", sub("<", "<", format.pval(p, digits = 3, eps = 0.001)))
}
table1::table1(tab1Form, data = caselog, overall = FALSE, extra.col = list(`P-value` = pvalue))
| Surgery Not Done (N=832) |
Surgery Completed (N=2220) |
P-value | |
|---|---|---|---|
| Sex | |||
| Female | 205 (24.6%) | 586 (26.4%) | 0.356 |
| Male | 626 (75.2%) | 1634 (73.6%) | |
| Missing | 1 (0.1%) | 0 (0%) | |
| Age Group | |||
| 0-15 Years | 224 (26.9%) | 734 (33.1%) | 0.0185 |
| 16-30 Years | 177 (21.3%) | 420 (18.9%) | |
| 31-45 Years | 218 (26.2%) | 536 (24.1%) | |
| 46-60 Years | 156 (18.8%) | 368 (16.6%) | |
| 60+ Years | 57 (6.9%) | 162 (7.3%) | |
| Ward | |||
| FLOOR | 826 (99.3%) | 2199 (99.1%) | 0.381 |
| HDU | 4 (0.5%) | 17 (0.8%) | |
| ICU | 2 (0.2%) | 1 (0.0%) | |
| ONCOLOGY | 0 (0%) | 1 (0.0%) | |
| SHDU | 0 (0%) | 2 (0.1%) | |
| District | |||
| NTCHEU | 0 (0%) | 1 (0.0%) | <0.001 |
| BALAKA | 4 (0.5%) | 12 (0.5%) | |
| BLANTYRE | 0 (0%) | 3 (0.1%) | |
| CHITIPA | 0 (0%) | 6 (0.3%) | |
| DEDZA | 65 (7.8%) | 146 (6.6%) | |
| DOWA | 55 (6.6%) | 111 (5.0%) | |
| KARONGA | 0 (0%) | 2 (0.1%) | |
| KASUNGU | 27 (3.2%) | 108 (4.9%) | |
| KASUNGU | 1 (0.1%) | 2 (0.1%) | |
| LILONGWE | 265 (31.9%) | 929 (41.8%) | |
| MACHINGA | 0 (0%) | 2 (0.1%) | |
| MANGOCHI | 1 (0.1%) | 0 (0%) | |
| MCHINJI | 32 (3.8%) | 84 (3.8%) | |
| MTCHINJI | 1 (0.1%) | 0 (0%) | |
| MULANJE | 1 (0.1%) | 2 (0.1%) | |
| MWANZA | 0 (0%) | 1 (0.0%) | |
| MZIMBA | 3 (0.4%) | 7 (0.3%) | |
| MZUZU | 0 (0%) | 2 (0.1%) | |
| NKHATABAY | 0 (0%) | 4 (0.2%) | |
| NKHOTAKOTA | 15 (1.8%) | 64 (2.9%) | |
| NTCHEU | 40 (4.8%) | 96 (4.3%) | |
| NTCHEU | 0 (0%) | 2 (0.1%) | |
| NTCHISI | 32 (3.8%) | 62 (2.8%) | |
| PHALOMBE | 0 (0%) | 2 (0.1%) | |
| RUMPHI | 4 (0.5%) | 1 (0.0%) | |
| SALIMA | 36 (4.3%) | 133 (6.0%) | |
| THYOLO | 1 (0.1%) | 1 (0.0%) | |
| UNKNOWN | 124 (14.9%) | 42 (1.9%) | |
| ZOMBA | 0 (0%) | 4 (0.2%) | |
| Missing | 125 (15.0%) | 391 (17.6%) | |
| First Case | |||
| NO | 825 (99.2%) | 1571 (70.8%) | <0.001 |
| YES | 7 (0.8%) | 649 (29.2%) | |
| Type of Surgery | |||
| ELECTIVE | 747 (89.8%) | 1814 (81.7%) | <0.001 |
| EMERGENCY | 85 (10.2%) | 406 (18.3%) | |
| Surgical Specialty | |||
| ENT | 54 (6.5%) | 170 (7.7%) | 0.00133 |
| GS | 202 (24.3%) | 525 (23.6%) | |
| NS | 2 (0.2%) | 5 (0.2%) | |
| ORTHO | 302 (36.3%) | 639 (28.8%) | |
| PEDS | 136 (16.3%) | 437 (19.7%) | |
| PLASTICS | 57 (6.9%) | 158 (7.1%) | |
| URO | 79 (9.5%) | 286 (12.9%) | |
| Age | |||
| Mean (SD) | 31.8 (21.5) | 29.6 (22.2) | 0.0119 |
| Median [Min, Max] | 32.0 [0, 89.0] | 30.0 [0, 90.0] |
Crude (unadjusted) odds ratios estimated by logistic regression implemented through generalized estimating equations to account for potential correlation between subjects
########################################################################################################################
## [5] GEE Analysis
########################################################################################################################
gee_summary <- function(model_object, ci = 0.95){
a <- coef(summary(model_object))
mult <- qnorm((1 + ci) / 2)
restab <- with(as.data.frame(a),
cbind(est = Estimate,
lwr = Estimate - mult*`Robust S.E.`,
upr = Estimate + mult*`Robust S.E.`))
rownames(restab) <- rownames(a)
return(data.frame(restab))
}
# Need to convert to 0/1 for logistic regression
caselog$surgerydone <- ifelse(as.numeric(caselog$surgerydone) == 2, 0,
as.numeric(caselog$surgerydone))
# reference general and emergency surgery
caselog$specialty <- relevel(caselog$specialty, ref = "GS")
caselog$surgtype <- relevel(caselog$surgtype, ref = "EMERGENCY")
# Fucntion to format ORs
prettyOR <- function(x){
nnames <- gsub("get\\(xx\\)", "", rownames(x))
x[] <- lapply(x[], function(x) sprintf("%.2f", x))
y <- paste0(x$est, " (", x$lwr, ", ", x$upr, ")")
names(y) <- nnames
data.frame(OR = y)
}
# Function to supress output
hush <- function(code){
sink("NUL")
tmp <- code
sink()
return(tmp)
}
#initiate empt list
ORlist <- list()
notlist <- c("ward", "district")
# loop through variables
# note ward and dstrict did ont converge
for (xx in tab1Vars[!(tab1Vars %in% notlist)]) {
ORlist[[xx]] <-
prettyOR(
exp(gee_summary(
hush(gee::gee(surgerydone ~ get(xx),
data = caselog[!is.na(caselog[, xx]), ],
id = id, family = binomial,
corstr = "exchangeable"))
)[-1,])
)
}
knitr::kable(do.call(rbind, ORlist), caption = "Unadjusted Odds Ratios, GEE Models", booktabs = TRUE)
| OR | |
|---|---|
| gender | 1.10 (0.91, 1.32) |
| agegrp.16-30 Years | 1.38 (1.10, 1.74) |
| agegrp.31-45 Years | 1.33 (1.07, 1.66) |
| agegrp.46-60 Years | 1.39 (1.09, 1.77) |
| agegrp.60+ Years | 1.15 (0.82, 1.61) |
| firstcase | 0.02 (0.01, 0.04) |
| surgtype | 1.97 (1.53, 2.52) |
| specialty.ENT | 0.83 (0.58, 1.17) |
| specialty.NS | 1.04 (0.20, 5.40) |
| specialty.ORTHO | 1.23 (0.99, 1.52) |
| specialty.PEDS | 0.81 (0.63, 1.04) |
| specialty.PLASTICS | 0.94 (0.66, 1.32) |
| specialty.URO | 0.72 (0.53, 0.97) |
| age | 1.00 (1.00, 1.01) |
Reasons for surgery not being completed
reasTab <- table(caselog$reason)
reasTab <- reasTab[order(reasTab, decreasing = TRUE)]
fmttd <- data.frame(paste0(data.frame(reasTab)[,2], " (",
sprintf("%.1f",
(data.frame(reasTab)[,2]/sum(data.frame(reasTab)[,2])*100)),
"%)"))
fmtfinal <- data.frame(Reason = names(reasTab), fmttd)
fmtfinal[(nrow(fmtfinal)+1):((nrow(fmtfinal)+1) + (length(none_reason) - 1)),] <- cbind(none_reason, "0 (0%)")
names(fmtfinal) <- c("Reason", "n (%)")
knitr::kable(fmtfinal)
| Reason | n (%) |
|---|---|
| Not enough time at end of day | 231 (27.8%) |
| Emergency case overflow | 112 (13.5%) |
| Patient not prepared or unfit | 104 (12.5%) |
| Other | 99 (11.9%) |
| Patient is absent or absconded | 95 (11.4%) |
| Too many elective cases booked by surgeon | 50 (6.0%) |
| No water | 48 (5.8%) |
| Patient medical condition changed | 26 (3.1%) |
| Lack or malfunction of theatre equipment | 17 (2.0%) |
| No surgeon | 15 (1.8%) |
| Lack of sterile drapes | 10 (1.2%) |
| Lack of sterile sets | 10 (1.2%) |
| Blood shortage | 4 (0.5%) |
| Lack of operative single use supplies | 4 (0.5%) |
| Theatre closure | 4 (0.5%) |
| No anesthetist | 3 (0.4%) |
| Lack of ppe | 0 (0%) |
| Lack of anesthetic single use supplies | 0 (0%) |
| Power outage | 0 (0%) |
| No nurse | 0 (0%) |
library(dplyr)
casedate <- data.frame(table(caselog$date, caselog$surgerydone))
names(casedate) <- c("Date", "surgerydone", "Count")
casedate$Date <- as.Date(casedate$Date)
casedate <- casedate %>%
group_by(surgerydone) %>%
mutate(rlmean = zoo::rollmean(Count, k = 7, fill = NA),
rlmed = zoo::rollmedian(Count, k = 7, fill = NA))
propvals <- tidyr::pivot_wider(casedate[,-c(3,5)], names_from = surgerydone, values_from = rlmean)
propvals$prop <- unlist(propvals[,3]/rowSums(propvals[,2:3]))
makeTransparent <- function(Colr, alpha = 100){
newColor <- col2rgb(Colr)
apply(newColor, 2, function(yy){
rgb(red = yy[1], green = yy[2], blue = yy[3],
alpha = alpha, maxColorValue = 255)})
}
cols <- rev(c("#DF536B", "#61D04F"))
pcols <- makeTransparent(c(cols, "black"), alpha = 50)
lcols <- makeTransparent(c(cols, "black"), alpha = 100)
par(mar = c(5, 4, 4, 4) + 0.3)
plot(NA, type = "n", xlim = range(casedate$Date),
ylab = "", xlab = "",
ylim = c(0, max(casedate$Count) + 15), axes = FALSE)
for(xx in 0:1){
points(x = casedate$Date[casedate$surgerydone == xx],
y = casedate$Count[casedate$surgerydone == xx],
pch = 21, bg = pcols[xx + 1], col = pcols[3])
lines(x = casedate$Date[casedate$surgerydone == xx],
y = casedate$rlmean[casedate$surgerydone == xx],
lty = 1, col = lcols[xx + 1], lwd = 2)
}
ax1vals <- c(min(casedate$Date),
lubridate::floor_date(seq(min(casedate$Date),
max(casedate$Date),
by = 'month'), unit = "month") + 14,
max(casedate$Date))
axis(2)
par(new = TRUE)
plot(propvals$Date, propvals$prop,
ylim = c(0,1), axes = FALSE,
type = "l", lty = 3, lwd = 2,
xlab = "", ylab = "")
axis(1, at = ax1vals,
labels = format(ax1vals, "%d-%b"))
axis(4, at = seq(0,1,0.25),
labels = sprintf("%1.0f%%", 100 * seq(0, 1, 0.25)))
mtext("Date", side = 1, padj = 4)
mtext("Number of surgeries", side = 2, padj = -4)
mtext("Percent", side = 4, padj = 4)
legend("topleft", title = "Surgical Status", title.adj = 0.05, title.font = 2,
c("Completed", "Cancelled", "7-Day Averages", "Percent Cancelled (7-Day Average)"),
bty = "n", col = "black", pch = c(21, 21, NA, NA),
lty = c(NA, NA, 1, 3), lwd = c(NA, NA, 2, 2),
pt.bg = c(cols, NA, NA))
text(x = max(ax1vals) - 8, y = 0.95, "p = 0.702", cex = 0.75)
Methods
Study design and setting
This study was conducted between June 2022 and December 2022 at Kamuzu Central Hospital (KCH) in Lilongwe, Malawi. KCH is the largest (?) tertiary care facility in Malawi and serves approximately xx million individuals throughout urban, peri-urban, and rural communities in Malawi’s Central Region. KCH has approximately xx attending surgeons and yy resident surgeons covering approximately zz surgeries per year. KCH provides the following specialties: ear nose and throat (ENT), general surgery (GS), NS(?), orthopedic (ORTHO), pediatric (PEDS), plastic (PLASTICS), and urology (URO).
We maintained an administrative database of all adult and pediatric surgical patients scheduled for elective or emergency surgery between June 23, 2022, and December 20, 2022. Participants were followed from their surgical scheduling until a surgical status was determined (completed or cancelled). Patients were excluded if […].
[…]
Model outcome and covariates
Our primary outcome was surgical status (completed or cancelled). We defined a surgical status as completed if the surgery was performed, including surgeries that were delayed to a later date. We defined a surgical status as cancelled if the surgery was not performed due to a patient or hospital complication. Surgeries that were scheduled but later not performed due to change in clinical recommendations were not considered cancelled and excluded. We considered a patient to have absconded if they were unable to be contacted after xx attempts over yy weeks.
Our primary covariates assessed included sex, age, ward, first case of the day (per specialty), type of surgery (elective or emergency), and surgical specialty. Demographic and surgical covariates were described using frequency and proportions for categorical characteristics and median and interquartile range (IQR) for continuous covariates.
We examined the associations between surgical status and key covariates using both univariate and multivariate logistic regression implemented through generalized estimating equations (GEE) with an exchangeable correlation matrix to account for potential correlation that may arise between individuals with repeat or multiple surgeries. Model selection for the final multivariate model was based on both quantitative and qualitative reasoning (i.e., bivariate associations, a priori knowledge of plausible associations, and causal diagrams) to identify minimally sufficient adjustment sets and reduce potentially spurious associations. In both univariate and multivariate models, parameters and standard errors were transformed to compute crude and adjusted odds ratios (cOR and aOR, respectively) and 95% confidence intervals (CIs) to determine if an association was statistically significant; CIs that did not contain the null value (1.0) were considered significant.
We used an Auto-Regressive Integrated Moving Average (ARIMA) model to test if the proportion of cancellations changed over time, which accounts for autocorrelation that may arise due to persistent complications over time (i.e., theatre closure, blood shortage.). We tested for linear, monotonic, and non-monotonic/non-linear trends using sieve-bootstrap versions of the t-test, Mann-Kendall test, and WAVK test, respectively.
Ethical considerations This study was approved [or deemed IRB exempt?] by the Institutional Board of Review (IRB) at the Yale School of Medicine, the [ethics committee] at KCH, and [Malawi Department of Health?]
Results A total of 3052 surgeries were scheduled during the study timeframe. Characteristics of cases stratified by surgical status are presented in Table 1. The median age of all cases was 30.0 years, and 74% of cases were male. The vast majority of cases came from the floor wards (99%), with only 27 (1%) coming from the intensive care unit, medical/surgical high dependency units, and oncology combined; 491 (16%) of all cases were emergency cases. The majority of patients (n = 1858, 61%) lived outside the Lilongwe district.
A total of 832 (27%) were cancelled due to hospital or patient complications (Table 2). The primary reason for cancellation was lack of time (n = 231, 28%), followed by emergency case overflow (n = 112, 14%), patient not prepared (n = 104, 13%) and patient absconded (n = 95, 11%). Despite the first case of the day for each specialty making up over 20 percent of all cases (n = 656), only 7 (1%) of first cases were cancelled. There was not a statistically significant trend in the number of cancellations over time.
The odds of a cancellation were higher among patients aged 16- to 60-year-old when compared to patients under 15 years old, with cORs of 1.4 (CI: 1.1-1.7), 1.3 (CI: 1.1-1.7), and 1.4 (1.1-1.8) for 16-30, 31-45, and 46-60 years old, respectively (Table 2). The odds of elective surgeries being cancelled were twice as high compared to emergency surgeries (cOR 2.0, CI: 1.5-2.5). The odds of a surgery being cancelled were 98% lower when it was the surgical specialty’s first case of the day (cOR 0.02, CI: 0.01-0.04).