AMR/R/mdro.R

387 lines
14 KiB
R
Raw Normal View History

# ==================================================================== #
# TITLE #
# Antimicrobial Resistance (AMR) Analysis #
# #
2019-01-02 23:24:07 +01:00
# SOURCE #
# https://gitlab.com/msberends/AMR #
# #
# LICENCE #
2019-01-02 23:24:07 +01:00
# (c) 2019 Berends MS (m.s.berends@umcg.nl), Luz CF (c.f.luz@umcg.nl) #
# #
2019-01-02 23:24:07 +01:00
# This R package is free software; you can freely use and distribute #
# it for both personal and commercial purposes under the terms of the #
# GNU General Public License version 2.0 (GNU GPL-2), as published by #
# the Free Software Foundation. #
# #
# This R package was created for academic research and was publicly #
# released in the hope that it will be useful, but it comes WITHOUT #
# ANY WARRANTY OR LIABILITY. #
2019-04-05 18:47:39 +02:00
# Visit our website for more info: https://msberends.gitlab.io/AMR. #
# ==================================================================== #
#' Determine multidrug-resistant organisms (MDRO)
#'
#' Determine which isolates are multidrug-resistant organisms (MDRO) according to country-specific guidelines.
2019-05-10 16:44:59 +02:00
#' @param x table with antibiotic columns, like e.g. \code{AMX} and \code{AMC}
2018-04-25 15:33:58 +02:00
#' @param country country code to determine guidelines. EUCAST rules will be used when left empty, see Details. Should be or a code from the \href{https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements}{list of ISO 3166-1 alpha-2 country codes}. Case-insensitive. Currently supported are \code{de} (Germany) and \code{nl} (the Netherlands).
#' @param info print progress
2018-11-16 20:50:50 +01:00
#' @inheritParams eucast_rules
2019-04-09 14:59:17 +02:00
#' @param verbose print additional info: missing antibiotic columns per parameter
2018-11-16 20:50:50 +01:00
#' @inheritSection eucast_rules Antibiotics
2019-04-09 14:59:17 +02:00
#' @details When \code{country} will be left blank, guidelines will be taken from EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" (\href{http://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf}{link}).
2018-11-16 20:50:50 +01:00
#' @return Ordered factor with levels \code{Negative < Positive, unconfirmed < Positive}.
#' @rdname mdro
2018-10-23 11:15:05 +02:00
#' @importFrom dplyr %>%
2018-12-22 22:39:34 +01:00
#' @importFrom crayon red blue bold
#' @export
2019-01-02 23:24:07 +01:00
#' @inheritSection AMR Read more on our website!
2018-04-25 15:33:58 +02:00
#' @examples
#' library(dplyr)
#'
#' septic_patients %>%
2018-11-16 20:50:50 +01:00
#' mutate(EUCAST = mdro(.),
#' BRMO = brmo(.))
2019-05-10 16:44:59 +02:00
mdro <- function(x,
2018-04-25 15:33:58 +02:00
country = NULL,
2018-10-23 11:15:05 +02:00
col_mo = NULL,
info = TRUE,
2019-05-10 16:44:59 +02:00
verbose = FALSE,
...) {
tbl_ <- x
if (!is.data.frame(tbl_)) {
stop("`x` must be a data frame.", call. = FALSE)
2018-10-23 11:15:05 +02:00
}
# try to find columns based on type
# -- mo
2019-01-15 12:45:24 +01:00
if (is.null(col_mo)) {
2019-05-10 16:44:59 +02:00
col_mo <- search_type_in_df(tbl = tbl_, type = "mo")
2018-12-22 22:39:34 +01:00
}
if (is.null(col_mo)) {
stop("`col_mo` must be set.", call. = FALSE)
2018-04-25 15:33:58 +02:00
}
if (length(country) > 1) {
2019-05-20 19:12:41 +02:00
stop("`country` must be a length one character string.", call. = FALSE)
}
2018-04-25 15:33:58 +02:00
if (is.null(country)) {
2019-05-20 19:12:41 +02:00
country <- "EUCAST"
2018-04-25 15:33:58 +02:00
}
country <- trimws(country)
2019-05-20 19:12:41 +02:00
if (tolower(country) != "eucast" & !country %like% "^[a-z]{2}$") {
stop("This is not a valid ISO 3166-1 alpha-2 country code: '", country, "'. Please see ?mdro.", call. = FALSE)
}
# create list and make country code case-independent
guideline <- list(country = list(code = tolower(country)))
2019-05-20 19:12:41 +02:00
if (guideline$country$code == "eucast") {
guideline$country$name <- "(European guidelines)"
guideline$name <- "EUCAST Expert Rules, \"Intrinsic Resistance and Exceptional Phenotypes Tables\""
guideline$version <- "Version 3.1"
guideline$source <- "http://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf"
2018-04-25 15:33:58 +02:00
# support per country:
2019-05-20 19:12:41 +02:00
} else if (guideline$country$code == "de") {
guideline$country$name <- "Germany"
guideline$name <- ""
guideline$version <- ""
guideline$source <- ""
} else if (guideline$country$code == "nl") {
guideline$country$name <- "The Netherlands"
guideline$name <- "WIP-Richtlijn BRMO"
guideline$version <- "Revision as of December 2017"
guideline$source <- "https://www.rivm.nl/Documenten_en_publicaties/Professioneel_Praktisch/Richtlijnen/Infectieziekten/WIP_Richtlijnen/WIP_Richtlijnen/Ziekenhuizen/WIP_richtlijn_BRMO_Bijzonder_Resistente_Micro_Organismen_ZKH"
2019-04-09 14:59:17 +02:00
# add here more countries like this:
2019-05-20 19:12:41 +02:00
# } else if (country$code == "xx") {
# country$name <- "country name"
} else {
2019-05-20 19:12:41 +02:00
stop("This country code is currently unsupported: ", guideline$country$code, call. = FALSE)
}
if (info == TRUE) {
2018-04-25 15:33:58 +02:00
cat("Determining multidrug-resistant organisms (MDRO), according to:\n",
2018-10-23 11:15:05 +02:00
"Guideline: ", red(paste0(guideline$name, ", ", guideline$version, "\n")),
"Country : ", red(paste0(guideline$country$name, "\n")),
"Source : ", blue(paste0(guideline$source, "\n")),
"\n", sep = "")
}
2019-05-10 16:44:59 +02:00
2019-05-20 19:12:41 +02:00
cols_ab <- get_column_abx(x = x, verbose = verbose, ...)
AMC <- cols_ab["AMC"]
AMK <- cols_ab["AMK"]
AMP <- cols_ab["AMP"]
AMX <- cols_ab["AMX"]
ATM <- cols_ab["ATM"]
AZL <- cols_ab["AZL"]
AZM <- cols_ab["AZM"]
CAZ <- cols_ab["CAZ"]
CED <- cols_ab["CED"]
CHL <- cols_ab["CHL"]
CIP <- cols_ab["CIP"]
CLI <- cols_ab["CLI"]
CLR <- cols_ab["CLR"]
COL <- cols_ab["COL"]
CRO <- cols_ab["CRO"]
CTX <- cols_ab["CTX"]
CXM <- cols_ab["CXM"]
CZO <- cols_ab["CZO"]
DAP <- cols_ab["DAP"]
DOX <- cols_ab["DOX"]
ERY <- cols_ab["ERY"]
ETP <- cols_ab["ETP"]
FEP <- cols_ab["FEP"]
FLC <- cols_ab["FLC"]
FOS <- cols_ab["FOS"]
FOX <- cols_ab["FOX"]
FUS <- cols_ab["FUS"]
GEN <- cols_ab["GEN"]
IPM <- cols_ab["IPM"]
KAN <- cols_ab["KAN"]
LIN <- cols_ab["LIN"]
LNZ <- cols_ab["LNZ"]
LVX <- cols_ab["LVX"]
MEM <- cols_ab["MEM"]
MEZ <- cols_ab["MEZ"]
MTR <- cols_ab["MTR"]
MFX <- cols_ab["MFX"]
MNO <- cols_ab["MNO"]
NAL <- cols_ab["NAL"]
NEO <- cols_ab["NEO"]
NET <- cols_ab["NET"]
NIT <- cols_ab["NIT"]
NOR <- cols_ab["NOR"]
NOV <- cols_ab["NOV"]
OFX <- cols_ab["OFX"]
PEN <- cols_ab["PEN"]
PIP <- cols_ab["PIP"]
PLB <- cols_ab["PLB"]
PRI <- cols_ab["PRI"]
QDA <- cols_ab["QDA"]
RID <- cols_ab["RID"]
RIF <- cols_ab["RIF"]
RXT <- cols_ab["RXT"]
SIS <- cols_ab["SIS"]
SXT <- cols_ab["SXT"]
TCY <- cols_ab["TCY"]
TEC <- cols_ab["TEC"]
TGC <- cols_ab["TGC"]
TIC <- cols_ab["TIC"]
TMP <- cols_ab["TMP"]
TOB <- cols_ab["TOB"]
TZP <- cols_ab["TZP"]
VAN <- cols_ab["VAN"]
2019-05-10 16:44:59 +02:00
2018-04-25 15:33:58 +02:00
2019-04-09 14:59:17 +02:00
ab_missing <- function(ab) {
isTRUE(ab %in% c(NULL, NA)) | length(ab) == 0
}
2018-04-25 15:33:58 +02:00
# antibiotic classes
2019-05-10 16:44:59 +02:00
aminoglycosides <- c(TOB, GEN)
cephalosporins <- c(FEP, CTX, FOX, CED, CAZ, CRO, CXM, CZO)
cephalosporins_3rd <- c(CTX, CRO, CAZ)
carbapenems <- c(ETP, IPM, MEM)
fluoroquinolones <- c(OFX, CIP, LVX, MFX)
2018-04-25 15:33:58 +02:00
# helper function for editing the table
2018-11-16 20:50:50 +01:00
trans_tbl <- function(to, rows, cols, any_all) {
2019-04-09 14:59:17 +02:00
cols <- cols[!ab_missing(cols)]
2019-05-10 16:44:59 +02:00
cols <- cols[!is.na(cols)]
2018-04-25 15:33:58 +02:00
if (length(rows) > 0 & length(cols) > 0) {
2018-11-16 20:50:50 +01:00
if (any_all == "any") {
2019-05-20 19:12:41 +02:00
row_filter <- which(tbl_[, cols] == "R")
2018-11-16 20:50:50 +01:00
} else if (any_all == "all") {
2019-05-20 19:12:41 +02:00
row_filter <- tbl_ %>%
2018-11-16 20:50:50 +01:00
mutate(index = 1:nrow(.)) %>%
filter_at(vars(cols), all_vars(. == "R")) %>%
pull((index))
}
2019-05-20 19:12:41 +02:00
rows <- rows[rows %in% row_filter]
tbl_[rows, "MDRO"] <<- to
2018-04-25 15:33:58 +02:00
}
}
2019-05-10 16:44:59 +02:00
tbl_ <- tbl_ %>%
2018-12-22 22:39:34 +01:00
mutate_at(vars(col_mo), as.mo) %>%
2018-10-23 11:15:05 +02:00
# join to microorganisms data set
left_join_microorganisms(by = col_mo) %>%
# add unconfirmed to where genus is available
mutate(MDRO = ifelse(!is.na(genus), 1, NA_integer_))
2018-04-25 15:33:58 +02:00
2019-05-20 19:12:41 +02:00
if (guideline$country$code == "eucast") {
2018-04-25 15:33:58 +02:00
# EUCAST ------------------------------------------------------------------
# Table 5
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$family == "Enterobacteriaceae"
| tbl_$fullname %like% "^Pseudomonas aeruginosa"
| tbl_$genus == "Acinetobacter"),
2019-05-10 16:44:59 +02:00
COL,
2018-11-16 20:50:50 +01:00
"all")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Salmonella Typhi"),
2018-11-16 20:50:50 +01:00
c(carbapenems, fluoroquinolones),
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Haemophilus influenzae"),
2018-11-16 20:50:50 +01:00
c(cephalosporins_3rd, carbapenems, fluoroquinolones),
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Moraxella catarrhalis"),
2018-11-16 20:50:50 +01:00
c(cephalosporins_3rd, fluoroquinolones),
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Neisseria meningitidis"),
2018-11-16 20:50:50 +01:00
c(cephalosporins_3rd, fluoroquinolones),
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Neisseria gonorrhoeae"),
2019-05-10 16:44:59 +02:00
AZM,
2018-11-16 20:50:50 +01:00
"any")
2018-04-25 15:33:58 +02:00
# Table 6
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Staphylococcus (aureus|epidermidis|coagulase negatief|hominis|haemolyticus|intermedius|pseudointermedius)"),
2019-05-10 16:44:59 +02:00
c(VAN, TEC, DAP, LNZ, QDA, TGC),
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$genus == "Corynebacterium"),
2019-05-10 16:44:59 +02:00
c(VAN, TEC, DAP, LNZ, QDA, TGC),
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Streptococcus pneumoniae"),
2019-05-10 16:44:59 +02:00
c(carbapenems, VAN, TEC, DAP, LNZ, QDA, TGC, RIF),
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3, # Sr. groups A/B/C/G
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Streptococcus (pyogenes|agalactiae|equisimilis|equi|zooepidemicus|dysgalactiae|anginosus)"),
2019-05-10 16:44:59 +02:00
c(PEN, cephalosporins, VAN, TEC, DAP, LNZ, QDA, TGC),
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$genus == "Enterococcus"),
2019-05-10 16:44:59 +02:00
c(DAP, LNZ, TGC, TEC),
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Enterococcus faecalis"),
2019-05-10 16:44:59 +02:00
c(AMP, AMX),
2018-11-16 20:50:50 +01:00
"any")
2018-04-25 15:33:58 +02:00
# Table 7
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$genus == "Bacteroides"),
2019-05-10 16:44:59 +02:00
MTR,
2018-11-16 20:50:50 +01:00
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Clostridium difficile"),
c(MTR, VAN),
2018-11-16 20:50:50 +01:00
"any")
2018-04-25 15:33:58 +02:00
}
2019-05-20 19:12:41 +02:00
if (guideline$country$code == "de") {
2018-04-25 15:33:58 +02:00
# Germany -----------------------------------------------------------------
2018-04-19 14:10:57 +02:00
stop("We are still working on German guidelines in this beta version.", call. = FALSE)
}
2019-05-20 19:12:41 +02:00
if (guideline$country$code == "nl") {
2018-04-25 15:33:58 +02:00
# Netherlands -------------------------------------------------------------
2019-05-20 19:12:41 +02:00
aminoglycosides <- aminoglycosides[!is.na(aminoglycosides)]
fluoroquinolones <- fluoroquinolones[!is.na(fluoroquinolones)]
carbapenems <- carbapenems[!is.na(carbapenems)]
amino <- AMX %or% AMP
third <- CAZ %or% CTX
ESBLs <- c(amino, third)
ESBLs <- ESBLs[!is.na(ESBLs)]
if (length(ESBLs) != 2) {
ESBLs <- character(0)
}
# Table 1
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$family == "Enterobacteriaceae"),
2018-11-16 20:50:50 +01:00
c(aminoglycosides, fluoroquinolones),
"all")
trans_tbl(2,
2019-05-20 19:12:41 +02:00
which(tbl_$family == "Enterobacteriaceae"),
carbapenems,
2018-11-16 20:50:50 +01:00
"any")
2019-05-20 19:12:41 +02:00
trans_tbl(2,
which(tbl_$family == "Enterobacteriaceae"),
ESBLs,
"all")
# Table 2
2018-11-16 20:50:50 +01:00
trans_tbl(2,
2019-05-20 19:12:41 +02:00
which(tbl_$genus == "Acinetobacter"),
2018-11-16 20:50:50 +01:00
c(carbapenems),
"any")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$genus == "Acinetobacter"),
2018-11-16 20:50:50 +01:00
c(aminoglycosides, fluoroquinolones),
"all")
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "^Stenotrophomonas maltophilia"),
2019-05-10 16:44:59 +02:00
SXT,
2018-11-16 20:50:50 +01:00
"all")
2019-05-10 16:44:59 +02:00
if (!ab_missing(MEM) & !ab_missing(IPM)
& !ab_missing(GEN) & !ab_missing(TOB)
& !ab_missing(CIP)
& !ab_missing(CAZ)
& !ab_missing(TZP) ) {
2019-05-11 23:30:10 +02:00
tbl_$psae <- 0
tbl_[which(tbl_[, MEM] == "R" | tbl_[, IPM] == "R"), "psae"] <- 1 + tbl_[which(tbl_[, MEM] == "R" | tbl_[, IPM] == "R"), "psae"]
tbl_[which(tbl_[, GEN] == "R" & tbl_[, TOB] == "R"), "psae"] <- 1 + tbl_[which(tbl_[, GEN] == "R" & tbl_[, TOB] == "R"), "psae"]
tbl_[which(tbl_[, CIP] == "R"), "psae"] <- 1 + tbl_[which(tbl_[, CIP] == "R"), "psae"]
tbl_[which(tbl_[, CAZ] == "R"), "psae"] <- 1 + tbl_[which(tbl_[, CAZ] == "R"), "psae"]
tbl_[which(tbl_[, TZP] == "R"), "psae"] <- 1 + tbl_[which(tbl_[, TZP] == "R"), "psae"]
2018-11-16 20:50:50 +01:00
} else {
2019-05-10 16:44:59 +02:00
tbl_$psae <- 0
2018-11-16 20:50:50 +01:00
}
2019-05-10 16:44:59 +02:00
tbl_[which(
2019-05-20 19:12:41 +02:00
tbl_$fullname %like% "Pseudomonas aeruginosa"
2019-05-10 16:44:59 +02:00
& tbl_$psae >= 3
2019-05-20 19:12:41 +02:00
), "MDRO"] <- 3
# Table 3
2018-11-16 20:50:50 +01:00
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "Streptococcus pneumoniae"),
2019-05-10 16:44:59 +02:00
PEN,
2018-11-16 20:50:50 +01:00
"all")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "Streptococcus pneumoniae"),
2019-05-10 16:44:59 +02:00
VAN,
2018-11-16 20:50:50 +01:00
"all")
trans_tbl(3,
2019-05-20 19:12:41 +02:00
which(tbl_$fullname %like% "Enterococcus faecium"),
2019-05-10 16:44:59 +02:00
c(PEN, VAN),
2018-11-16 20:50:50 +01:00
"all")
}
2019-05-10 16:44:59 +02:00
factor(x = tbl_$MDRO,
2018-11-16 20:50:50 +01:00
levels = 1:3,
2019-05-20 19:12:41 +02:00
labels = c("Negative", "Positive, unconfirmed", "Positive"),
ordered = TRUE)
}
2018-11-16 20:50:50 +01:00
#' @rdname mdro
#' @export
2018-11-16 20:50:50 +01:00
brmo <- function(..., country = "nl") {
mdro(..., country = "nl")
}
2018-11-16 20:50:50 +01:00
#' @rdname mdro
#' @export
2019-05-10 16:44:59 +02:00
mrgn <- function(x, country = "de", ...) {
mdro(x = x, country = "de", ...)
2018-04-25 15:33:58 +02:00
}
2018-11-16 20:50:50 +01:00
#' @rdname mdro
2018-04-25 15:33:58 +02:00
#' @export
2019-05-10 16:44:59 +02:00
eucast_exceptional_phenotypes <- function(x, country = "EUCAST", ...) {
mdro(x = x, country = "EUCAST", ...)
}