1
0
mirror of https://github.com/msberends/AMR.git synced 2025-06-07 19:14:01 +02:00

(v2.1.1.9278) support AMR selectors in custom MDRO guideline

This commit is contained in:
dr. M.S. (Matthijs) Berends 2025-05-16 11:20:19 +02:00
parent b8d7c8af7f
commit 139f79d372
No known key found for this signature in database
15 changed files with 582 additions and 514 deletions

View File

@ -1,6 +1,6 @@
Package: AMR
Version: 2.1.1.9277
Date: 2025-05-15
Version: 2.1.1.9278
Date: 2025-05-16
Title: Antimicrobial Resistance Data Analysis
Description: Functions to simplify and standardise antimicrobial resistance (AMR)
data analysis and to work with microbial and antimicrobial properties by

View File

@ -1,4 +1,4 @@
# AMR 2.1.1.9277
# AMR 2.1.1.9278
*(this beta version will eventually become v3.0. We're happy to reach a new major milestone soon, which will be all about the new One Health support! Install this beta using [the instructions here](https://amr-for-r.org/#get-this-package).)*
@ -148,9 +148,10 @@ This package now supports not only tools for AMR data analysis in clinical setti
* Improved algorithm of `first_isolate()` when using the phenotype-based method, to prioritise records with the highest availability of SIR values
* `scale_y_percent()` can now cope with ranges outside the 0-100% range
* MDRO determination (using `mdro()`)
* The Verbose Mode (`verbose = TRUE`) now includes the guideline name
* Implemented the new Dutch national MDRO guideline (SRI-richtlijn BRMO, Nov 2024)
* Added arguments `esbl`, `carbapenemase`, `mecA`, `mecC`, `vanA`, `vanB` to denote column names or logical values indicating presence of these genes (or production of their proteins)
* The Verbose Mode (`verbose = TRUE`) now includes the guideline name
* Added upport for antimicrobial selectors to use as as a custom rule (`custom_mdro_guideline()`)
* Added console colours support of `sir` class for Positron
### Other

View File

@ -1020,6 +1020,17 @@ get_current_data <- function(arg_name, call) {
}
}
# now check if it was run with eval(), which has arguments `expr`, `envir`, and `enclos`
from_eval_parse <- vapply(FUN.VALUE = logical(1), frms, function(e) all(c("expr", "envir", "enclos") %in% names(e)))
for (env in frms[which(from_eval_parse)]) {
if (valid_df(env$envir)) {
# the element `envir` could contain the data in case of
# e.g. `eval(parse(text = "any(cephalosporins_3rd() == 'R')"), envir = example_isolates)`
# this is also used by run_custom_mdro_guideline() to support antimicrobial selectors in the part before `~`
return(env$envir)
}
}
# no data.frame found, so an error must be returned:
if (is.na(arg_name)) {
if (isTRUE(is.numeric(call))) {
@ -1104,6 +1115,44 @@ get_group_names <- function(x) {
}
}
format_custom_query_rule <- function(query, colours = has_colour()) {
# this is used by custom EUCAST and custom MDRO rules
# font_black() is a bit expensive so do it once:
txt <- font_black("{text}")
query <- gsub(" & ", sub("{text}", font_bold(" and "), txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" | ", sub("{text}", " or ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" + ", sub("{text}", " plus ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" - ", sub("{text}", " minus ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" / ", sub("{text}", " divided by ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" * ", sub("{text}", " times ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" == ", sub("{text}", " is ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" > ", sub("{text}", " is higher than ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" < ", sub("{text}", " is lower than ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" >= ", sub("{text}", " is higher than or equal to ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" <= ", sub("{text}", " is lower than or equal to ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" ^ ", sub("{text}", " to the power of ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" %in% ", sub("{text}", " is one of ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" %like% ", sub("{text}", " resembles ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub("any\\((.*)\\)$", paste0(font_black("any of "), "\\1"), query)
query <- gsub("all\\((.*)\\)$", paste0(font_black("all of "), "\\1"), query)
if (colours == TRUE) {
query <- gsub("[\"']R[\"']", font_rose_bg(" R "), query)
query <- gsub("[\"']SDD[\"']", font_orange_bg(" SDD "), query)
query <- gsub("[\"']S[\"']", font_green_bg(" S "), query)
query <- gsub("[\"']NI[\"']", font_grey_bg(font_black(" NI ")), query)
query <- gsub("[\"']I[\"']", font_orange_bg(" I "), query)
}
# replace the black colour 'stops' with blue colour 'starts'
query <- gsub("\033[39m", "\033[34m", as.character(query), fixed = TRUE)
# start with blue
query <- paste0("\033[34m", query)
if (colours == FALSE) {
query <- font_stripstyle(query)
}
query
}
unique_call_id <- function(entire_session = FALSE, match_fn = NULL) {
if (entire_session == TRUE) {
return(c(envir = "session", call = "session"))

View File

@ -89,6 +89,10 @@
#' #> 2 Klebsiella pneumoniae R R S
#' ```
#'
#' ### Sharing rules among multiple users
#'
#' The rules set (the `y` object in this case) could be exported to a shared file location using [saveRDS()] if you collaborate with multiple users. The custom rules set could then be imported using [readRDS()].
#'
#' ### Usage of multiple antimicrobials and antimicrobial group names
#'
#' You can define antimicrobial groups instead of single antimicrobials for the rule consequence, which is the part *after* the tilde (~). In the examples above, the antimicrobial group `aminopenicillins` includes both ampicillin and amoxicillin.
@ -278,35 +282,3 @@ print.custom_eucast_rules <- function(x, ...) {
cat("\n ", rule_if, "\n", rule_then, "\n", sep = "")
}
}
format_custom_query_rule <- function(query, colours = has_colour()) {
# font_black() is a bit expensive so do it once:
txt <- font_black("{text}")
query <- gsub(" & ", sub("{text}", font_bold(" and "), txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" | ", sub("{text}", " or ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" + ", sub("{text}", " plus ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" - ", sub("{text}", " minus ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" / ", sub("{text}", " divided by ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" * ", sub("{text}", " times ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" == ", sub("{text}", " is ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" > ", sub("{text}", " is higher than ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" < ", sub("{text}", " is lower than ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" >= ", sub("{text}", " is higher than or equal to ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" <= ", sub("{text}", " is lower than or equal to ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" ^ ", sub("{text}", " to the power of ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" %in% ", sub("{text}", " is one of ", txt, fixed = TRUE), query, fixed = TRUE)
query <- gsub(" %like% ", sub("{text}", " resembles ", txt, fixed = TRUE), query, fixed = TRUE)
if (colours == TRUE) {
query <- gsub('"R"', font_rose_bg(" R "), query, fixed = TRUE)
query <- gsub('"S"', font_green_bg(" S "), query, fixed = TRUE)
query <- gsub('"I"', font_orange_bg(" I "), query, fixed = TRUE)
}
# replace the black colour 'stops' with blue colour 'starts'
query <- gsub("\033[39m", "\033[34m", as.character(query), fixed = TRUE)
# start with blue
query <- paste0("\033[34m", query)
if (colours == FALSE) {
query <- font_stripstyle(query)
}
query
}

313
R/custom_mdro_guideline.R Executable file
View File

@ -0,0 +1,313 @@
# ==================================================================== #
# TITLE: #
# AMR: An R Package for Working with Antimicrobial Resistance Data #
# #
# SOURCE CODE: #
# https://github.com/msberends/AMR #
# #
# PLEASE CITE THIS SOFTWARE AS: #
# Berends MS, Luz CF, Friedrich AW, et al. (2022). #
# AMR: An R Package for Working with Antimicrobial Resistance Data. #
# Journal of Statistical Software, 104(3), 1-31. #
# https://doi.org/10.18637/jss.v104.i03 #
# #
# Developed at the University of Groningen and the University Medical #
# Center Groningen in The Netherlands, in collaboration with many #
# colleagues from around the world, see our website. #
# #
# 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. #
# We created this package for both routine data analysis and academic #
# research and it was publicly released in the hope that it will be #
# useful, but it comes WITHOUT ANY WARRANTY OR LIABILITY. #
# #
# Visit our website for the full manual and a complete tutorial about #
# how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== #
#' Define Custom MDRO Guideline
#'
#' Define custom a MDRO guideline for your organisation or specific analysis and use the output of this function in [mdro()].
#' @param ... Guideline rules in [formula][base::tilde] notation, see below for instructions, and in *Examples*.
#' @inheritParams mdro
#' @details
#' Using a custom MDRO guideline is of importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
#' @section How it works:
#'
#' ### Basics
#'
#' If you are familiar with the [`case_when()`][dplyr::case_when()] function of the `dplyr` package, you will recognise the input method to set your own rules. Rules must be set using what \R considers to be the 'formula notation'. The rule itself is written *before* the tilde (`~`) and the consequence of the rule is written *after* the tilde:
#'
#' ```r
#' custom <- custom_mdro_guideline(CIP == "R" & age > 60 ~ "Elderly Type A",
#' ERY == "R" & age > 60 ~ "Elderly Type B")
#' ```
#'
#' If a row/an isolate matches the first rule, the value after the first `~` (in this case *'Elderly Type A'*) will be set as MDRO value. Otherwise, the second rule will be tried and so on. The number of rules is unlimited.
#'
#' You can print the rules set in the console for an overview. Colours will help reading it if your console supports colours.
#'
#' ```r
#' custom
#' #> A set of custom MDRO rules:
#' #> 1. If CIP is R and age is higher than 60 then: Elderly Type A
#' #> 2. If ERY is R and age is higher than 60 then: Elderly Type B
#' #> 3. Otherwise: Negative
#'
#' #> Unmatched rows will return NA.
#' #> Results will be of class 'factor', with ordered levels: Negative < Elderly Type A < Elderly Type B
#' ```
#'
#' The outcome of the function can be used for the `guideline` argument in the [mdro()] function:
#'
#' ```r
#' x <- mdro(example_isolates, guideline = custom)
#' #> Determining MDROs based on custom rules, resulting in factor levels: Negative < Elderly Type A < Elderly Type B.
#' #> - Custom MDRO rule 1: CIP == "R" & age > 60 (198 rows matched)
#' #> - Custom MDRO rule 2: ERY == "R" & age > 60 (732 rows matched)
#' #> => Found 930 custom defined MDROs out of 2000 isolates (46.5%)
#'
#' table(x)
#' #> x
#' #> Negative Elderly Type A Elderly Type B
#' #> 1070 198 732
#' ```
#'
#' Rules can also be combined with other custom rules by using [c()]:
#'
#' ```r
#' x <- mdro(example_isolates,
#' guideline = c(custom,
#' custom_mdro_guideline(ERY == "R" & age > 50 ~ "Elderly Type C")))
#' #> Determining MDROs based on custom rules, resulting in factor levels: Negative < Elderly Type A < Elderly Type B < Elderly Type C.
#' #> - Custom MDRO rule 1: CIP == "R" & age > 60 (198 rows matched)
#' #> - Custom MDRO rule 2: ERY == "R" & age > 60 (732 rows matched)
#' #> - Custom MDRO rule 3: ERY == "R" & age > 50 (109 rows matched)
#' #> => Found 1039 custom defined MDROs out of 2000 isolates (52.0%)
#'
#' table(x)
#' #> x
#' #> Negative Elderly Type A Elderly Type B Elderly Type C
#' #> 961 198 732 109
#' ```
#'
#' ### Sharing rules among multiple users
#'
#' The rules set (the `custom` object in this case) could be exported to a shared file location using [saveRDS()] if you collaborate with multiple users. The custom rules set could then be imported using [readRDS()].
#'
#' ### Usage of multiple antimicrobials and antimicrobial group names
#'
#' You can define antimicrobial groups instead of single antimicrobials for the rule itself, which is the part *before* the tilde (~). Use [any()] or [all()] to specify the scope of the antimicrobial group:
#'
#' ```r
#' custom_mdro_guideline(
#' AMX == "R" ~ "My MDRO #1",
#' any(cephalosporins_2nd() == "R") ~ "My MDRO #2",
#' all(glycopeptides() == "R") ~ "My MDRO #3"
#' )
#' ```
#'
#' These `r length(DEFINED_AB_GROUPS)` antimicrobial groups are allowed in the rules (case-insensitive) and can be used in any combination:
#'
#' `r paste0(" * ", sapply(DEFINED_AB_GROUPS, function(x) paste0(tolower(gsub("^AB_", "", x)), "\\cr(", vector_and(ab_name(eval(parse(text = x), envir = asNamespace("AMR")), language = NULL, tolower = TRUE), quotes = FALSE), ")"), USE.NAMES = FALSE), "\n", collapse = "")`
#' @returns A [list] containing the custom rules
#' @rdname custom_mdro_guideline
#' @export
#' @examples
#' x <- custom_mdro_guideline(
#' CIP == "R" & age > 60 ~ "Elderly Type A",
#' ERY == "R" & age > 60 ~ "Elderly Type B"
#' )
#' x
#'
#' # run the custom rule set (verbose = TRUE will return a logbook instead of the data set):
#' out <- mdro(example_isolates, guideline = x)
#' table(out)
#'
#' out <- mdro(example_isolates, guideline = x, verbose = TRUE)
#' head(out)
#'
#' # you can create custom guidelines using selectors (see ?antimicrobial_selectors)
#' my_guideline <- custom_mdro_guideline(
#' AMX == "R" ~ "Custom MDRO 1",
#' all(cephalosporins_2nd() == "R") ~ "Custom MDRO 2"
#' )
#' my_guideline
#'
#' out <- mdro(example_isolates, guideline = my_guideline)
#' table(out)
custom_mdro_guideline <- function(..., as_factor = TRUE) {
meet_criteria(as_factor, allow_class = "logical", has_length = 1)
dots <- tryCatch(list(...),
error = function(e) "error"
)
stop_if(
identical(dots, "error"),
"rules must be a valid formula inputs (e.g., using '~'), see `?mdro`"
)
n_dots <- length(dots)
stop_if(n_dots == 0, "no custom rules were set. Please read the documentation using `?mdro`.")
out <- vector("list", n_dots)
for (i in seq_len(n_dots)) {
stop_ifnot(
inherits(dots[[i]], "formula"),
"rule ", i, " must be a valid formula input (e.g., using '~'), see `?mdro`"
)
# Query
qry <- dots[[i]][[2]]
if (inherits(qry, "call")) {
qry <- as.expression(qry)
}
qry <- as.character(qry)
# these will prevent vectorisation, so replace them:
qry <- gsub("&&", "&", qry, fixed = TRUE)
qry <- gsub("||", "|", qry, fixed = TRUE)
# support filter()-like writing: custom_mdro_guideline('CIP == "R", AMX == "S"' ~ "result 1")
qry <- gsub(" *, *", " & ", qry)
# format nicely, setting spaces around operators
qry <- gsub(" *([&|+-/*^><==]+) *", " \\1 ", qry)
qry <- gsub("'", "\"", qry, fixed = TRUE)
qry <- as.expression(qry)
out[[i]]$query <- qry
# Value
val <- tryCatch(eval(dots[[i]][[3]]), error = function(e) NULL)
stop_if(is.null(val), "rule ", i, " must return a valid value, it now returns an error: ", tryCatch(eval(dots[[i]][[3]]), error = function(e) e$message))
stop_if(length(val) > 1, "rule ", i, " must return a value of length 1, not ", length(val))
out[[i]]$value <- as.character(val)
}
names(out) <- paste0("rule", seq_len(n_dots))
out <- set_clean_class(out, new_class = c("custom_mdro_guideline", "list"))
attr(out, "values") <- unname(c("Negative", vapply(FUN.VALUE = character(1), unclass(out), function(x) x$value)))
attr(out, "as_factor") <- as_factor
out
}
#' @method c custom_mdro_guideline
#' @rdname custom_mdro_guideline
#' @export
c.custom_mdro_guideline <- function(x, ..., as_factor = NULL) {
if (length(list(...)) == 0) {
return(x)
}
if (!is.null(as_factor)) {
meet_criteria(as_factor, allow_class = "logical", has_length = 1)
} else {
as_factor <- attributes(x)$as_factor
}
for (g in list(...)) {
stop_ifnot(inherits(g, "custom_mdro_guideline"),
"for combining custom MDRO guidelines, all rules must be created with `custom_mdro_guideline()`",
call = FALSE
)
vals <- attributes(x)$values
if (!all(attributes(g)$values %in% vals)) {
vals <- unname(unique(c(vals, attributes(g)$values)))
}
attributes(g) <- NULL
x <- c(unclass(x), unclass(g))
attr(x, "values") <- vals
}
names(x) <- paste0("rule", seq_len(length(x)))
x <- set_clean_class(x, new_class = c("custom_mdro_guideline", "list"))
attr(x, "values") <- vals
attr(x, "as_factor") <- as_factor
x
}
#' @method as.list custom_mdro_guideline
#' @noRd
#' @export
as.list.custom_mdro_guideline <- function(x, ...) {
c(x, ...)
}
#' @method print custom_mdro_guideline
#' @rdname custom_mdro_guideline
#' @export
print.custom_mdro_guideline <- function(x, ...) {
cat("A set of custom MDRO rules:\n")
for (i in seq_len(length(x))) {
rule <- x[[i]]
rule$query <- format_custom_query_rule(rule$query)
cat(" ", i, ". ", font_bold("If "), font_blue(rule$query), font_bold(" then: "), font_red(rule$value), "\n", sep = "")
}
cat(" ", i + 1, ". ", font_bold("Otherwise: "), font_red(paste0("Negative")), "\n", sep = "")
cat("\nUnmatched rows will return ", font_red("NA"), ".\n", sep = "")
if (isTRUE(attributes(x)$as_factor)) {
cat("Results will be of class 'factor', with ordered levels: ", paste0(attributes(x)$values, collapse = " < "), "\n", sep = "")
} else {
cat("Results will be of class 'character'.\n")
}
}
run_custom_mdro_guideline <- function(df, guideline, info) {
n_dots <- length(guideline)
stop_if(n_dots == 0, "no custom guidelines set", call = -2)
out <- character(length = NROW(df))
reasons <- character(length = NROW(df))
for (i in seq_len(n_dots)) {
qry <- tryCatch(eval(parse(text = guideline[[i]]$query), envir = df, enclos = parent.frame()),
error = function(e) {
AMR_env$err_msg <- e$message
return("error")
}
)
if (identical(qry, "error")) {
warning_("in `custom_mdro_guideline()`: rule ", i,
" (`", as.character(guideline[[i]]$query), "`) was ignored because of this error message: ",
AMR_env$err_msg,
call = FALSE,
add_fn = font_red
)
next
}
stop_ifnot(is.logical(qry), "in custom_mdro_guideline(): rule ", i, " (`", guideline[[i]]$query,
"`) must return `TRUE` or `FALSE`, not ",
format_class(class(qry), plural = FALSE),
call = FALSE
)
new_mdros <- which(qry == TRUE & out == "")
if (isTRUE(info)) {
cat(word_wrap(
"- Custom MDRO rule ", i, ": `", as.character(guideline[[i]]$query),
"` (", length(new_mdros), " rows matched)"
), "\n", sep = "")
}
val <- guideline[[i]]$value
out[new_mdros] <- val
reasons[new_mdros] <- paste0(
"matched rule ",
gsub("rule", "", names(guideline)[i], fixed = TRUE), ": ", as.character(guideline[[i]]$query)
)
}
out[out == ""] <- "Negative"
reasons[out == "Negative"] <- "no rules matched"
if (isTRUE(attributes(guideline)$as_factor)) {
out <- factor(out, levels = attributes(guideline)$values, ordered = TRUE)
}
all_nonsusceptible_columns <- as.data.frame(t(df[, is.sir(df), drop = FALSE] == "R"))
all_nonsusceptible_columns <- vapply(
FUN.VALUE = character(1),
all_nonsusceptible_columns,
function(x) paste0(rownames(all_nonsusceptible_columns)[which(x)], collapse = ", ")
)
all_nonsusceptible_columns[is.na(out)] <- NA_character_
data.frame(
row_number = seq_len(NROW(df)),
MDRO = out,
reason = reasons,
all_nonsusceptible_columns = all_nonsusceptible_columns,
stringsAsFactors = FALSE
)
}

254
R/mdro.R
View File

@ -52,7 +52,9 @@
#'
#' **Note:** Every test that involves the Enterobacteriaceae family, will internally be performed using its newly named *order* Enterobacterales, since the Enterobacteriaceae family has been taxonomically reclassified by Adeolu *et al.* in 2016. Before that, Enterobacteriaceae was the only family under the Enterobacteriales (with an i) order. All species under the old Enterobacteriaceae family are still under the new Enterobacterales (without an i) order, but divided into multiple families. The way tests are performed now by this [mdro()] function makes sure that results from before 2016 and after 2016 are identical.
#'
#' @section Supported International / National Guidelines:
#' ### Supported International / National Guidelines
#'
#' Please suggest to implement guidelines by [letting us know](https://github.com/msberends/AMR/issues/new?template=2-feature-request.yml&title=Add%20new%20MDRO%20guideline).
#'
#' Currently supported guidelines are (case-insensitive):
#'
@ -64,13 +66,15 @@
#'
#' The European international guideline - EUCAST Expert Rules Version 3.3 "Intrinsic Resistance and Unusual Phenotypes" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2021/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.3_20211018.pdf))
#'
#' Also:
#'
#' * `guideline = "EUCAST 3.2"`
#'
#' The European international guideline - EUCAST Expert Rules Version 3.2 "Intrinsic Resistance and Unusual Phenotypes" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2020/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.2_20200225.pdf))
#' The former European international guideline - EUCAST Expert Rules Version 3.2 "Intrinsic Resistance and Unusual Phenotypes" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2020/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.2_20200225.pdf))
#'
#' * `guideline = "EUCAST 3.1"`
#'
#' The European international guideline - EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf))
#' The former European international guideline - EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf))
#'
#' * `guideline = "TB"`
#'
@ -90,55 +94,12 @@
#'
#' The former Dutch national guideline - Werkgroep Infectiepreventie (WIP), RIVM, last revision as of 2017: "Bijzonder Resistente Micro-Organismen (BRMO)"
#'
#' Please suggest to implement guidelines by letting us know: <https://github.com/msberends/AMR/issues/new>.
#' ### Using Custom Guidelines
#'
#' @section Using Custom Guidelines:
#' Using a custom MDRO guideline is of importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
#'
#' Custom guidelines can be set with the [custom_mdro_guideline()] function. This is of great importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
#' Custom guidelines can be set with the [custom_mdro_guideline()] function.
#'
#' If you are familiar with the [`case_when()`][dplyr::case_when()] function of the `dplyr` package, you will recognise the input method to set your own rules. Rules must be set using what \R considers to be the 'formula notation'. The rule is written *before* the tilde (`~`) and the consequence of the rule is written *after* the tilde:
#'
#' ```
#' custom <- custom_mdro_guideline(CIP == "R" & age > 60 ~ "Elderly Type A",
#' ERY == "R" & age > 60 ~ "Elderly Type B")
#' ```
#'
#' If a row/an isolate matches the first rule, the value after the first `~` (in this case *'Elderly Type A'*) will be set as MDRO value. Otherwise, the second rule will be tried and so on. The number of rules is unlimited.
#'
#' You can print the rules set in the console for an overview. Colours will help reading it if your console supports colours.
#'
#' ```
#' custom
#' #> A set of custom MDRO rules:
#' #> 1. CIP is "R" and age is higher than 60 -> Elderly Type A
#' #> 2. ERY is "R" and age is higher than 60 -> Elderly Type B
#' #> 3. Otherwise -> Negative
#' #>
#' #> Unmatched rows will return NA.
#' ```
#'
#' The outcome of the function can be used for the `guideline` argument in the [mdro()] function:
#'
#' ```
#' x <- mdro(example_isolates,
#' guideline = custom)
#' table(x)
#' #> Negative Elderly Type A Elderly Type B
#' #> 1070 198 732
#' ```
#'
#' Rules can also be combined with other custom rules by using [c()]:
#'
#' ```
#' x <- mdro(example_isolates,
#' guideline = c(custom,
#' custom_mdro_guideline(ERY == "R" & age > 50 ~ "Elderly Type C")))
#' table(x)
#' #> Negative Elderly Type A Elderly Type B Elderly Type C
#' #> 961 198 732 109
#' ```
#'
#' The rules set (the `custom` object in this case) could be exported to a shared file location using [saveRDS()] if you collaborate with multiple users. The custom rules set could then be imported using [readRDS()].
#' @inheritSection as.sir Interpretation of SIR
#' @return
#' - If `verbose` is set to `TRUE`:\cr
@ -153,33 +114,22 @@
#' Ordered [factor] with levels `Negative` < `Positive, unconfirmed` < `Positive`. The value `"Positive, unconfirmed"` means that, according to the guideline, it is not entirely sure if the isolate is multi-drug resistant and this should be confirmed with additional (e.g. genotypic) tests
#' @rdname mdro
#' @aliases MDR XDR PDR BRMO 3MRGN 4MRGN
#' @seealso [custom_mdro_guideline()]
#' @export
#' @source
#' See the supported guidelines above for the [list] of publications used for this function.
#' @examples
#' out <- mdro(example_isolates, guideline = "EUCAST")
#' out <- mdro(example_isolates)
#' str(out)
#' table(out)
#'
#' out <- mdro(example_isolates,
#' guideline = custom_mdro_guideline(
#' AMX == "R" ~ "Custom MDRO 1",
#' VAN == "R" ~ "Custom MDRO 2"
#' )
#' )
#' out <- mdro(example_isolates, guideline = "EUCAST 3.3")
#' table(out)
#'
#' \donttest{
#' if (require("dplyr")) {
#' example_isolates %>%
#' mdro() %>%
#' table()
#'
#' # no need to define `x` when used inside dplyr verbs:
#' example_isolates %>%
#' mutate(MDRO = mdro()) %>%
#' pull(MDRO) %>%
#' table()
#' count(MDRO)
#' }
#' }
mdro <- function(x = NULL,
@ -1947,7 +1897,6 @@ mdro <- function(x = NULL,
)
}
if (isTRUE(verbose)) {
# fill in empty reasons
x$reason[is.na(x$reason)] <- "not covered by guideline"
@ -1971,181 +1920,6 @@ mdro <- function(x = NULL,
}
}
#' @rdname mdro
#' @export
custom_mdro_guideline <- function(..., as_factor = TRUE) {
meet_criteria(as_factor, allow_class = "logical", has_length = 1)
dots <- tryCatch(list(...),
error = function(e) "error"
)
stop_if(
identical(dots, "error"),
"rules must be a valid formula inputs (e.g., using '~'), see `?mdro`"
)
n_dots <- length(dots)
stop_if(n_dots == 0, "no custom rules were set. Please read the documentation using `?mdro`.")
out <- vector("list", n_dots)
for (i in seq_len(n_dots)) {
stop_ifnot(
inherits(dots[[i]], "formula"),
"rule ", i, " must be a valid formula input (e.g., using '~'), see `?mdro`"
)
# Query
qry <- dots[[i]][[2]]
if (inherits(qry, "call")) {
qry <- as.expression(qry)
}
qry <- as.character(qry)
# these will prevent vectorisation, so replace them:
qry <- gsub("&&", "&", qry, fixed = TRUE)
qry <- gsub("||", "|", qry, fixed = TRUE)
# support filter()-like writing: custom_mdro_guideline('CIP == "R", AMX == "S"' ~ "result 1")
qry <- gsub(" *, *", " & ", qry)
# format nicely, setting spaces around operators
qry <- gsub(" *([&|+-/*^><==]+) *", " \\1 ", qry)
qry <- gsub("'", "\"", qry, fixed = TRUE)
out[[i]]$query <- as.expression(qry)
# Value
val <- tryCatch(eval(dots[[i]][[3]]), error = function(e) NULL)
stop_if(is.null(val), "rule ", i, " must return a valid value, it now returns an error: ", tryCatch(eval(dots[[i]][[3]]), error = function(e) e$message))
stop_if(length(val) > 1, "rule ", i, " must return a value of length 1, not ", length(val))
out[[i]]$value <- as.character(val)
}
names(out) <- paste0("rule", seq_len(n_dots))
out <- set_clean_class(out, new_class = c("custom_mdro_guideline", "list"))
attr(out, "values") <- unname(c("Negative", vapply(FUN.VALUE = character(1), unclass(out), function(x) x$value)))
attr(out, "as_factor") <- as_factor
out
}
#' @method c custom_mdro_guideline
#' @noRd
#' @export
c.custom_mdro_guideline <- function(x, ..., as_factor = NULL) {
if (length(list(...)) == 0) {
return(x)
}
if (!is.null(as_factor)) {
meet_criteria(as_factor, allow_class = "logical", has_length = 1)
} else {
as_factor <- attributes(x)$as_factor
}
for (g in list(...)) {
stop_ifnot(inherits(g, "custom_mdro_guideline"),
"for combining custom MDRO guidelines, all rules must be created with `custom_mdro_guideline()`",
call = FALSE
)
vals <- attributes(x)$values
if (!all(attributes(g)$values %in% vals)) {
vals <- unname(unique(c(vals, attributes(g)$values)))
}
attributes(g) <- NULL
x <- c(unclass(x), unclass(g))
attr(x, "values") <- vals
}
names(x) <- paste0("rule", seq_len(length(x)))
x <- set_clean_class(x, new_class = c("custom_mdro_guideline", "list"))
attr(x, "values") <- vals
attr(x, "as_factor") <- as_factor
x
}
#' @method as.list custom_mdro_guideline
#' @noRd
#' @export
as.list.custom_mdro_guideline <- function(x, ...) {
c(x, ...)
}
#' @method print custom_mdro_guideline
#' @export
#' @noRd
print.custom_mdro_guideline <- function(x, ...) {
cat("A set of custom MDRO rules:\n")
for (i in seq_len(length(x))) {
rule <- x[[i]]
rule$query <- format_custom_query_rule(rule$query)
cat(" ", i, ". ", font_bold("If "), font_blue(rule$query), font_bold(" then: "), font_red(rule$value), "\n", sep = "")
}
cat(" ", i + 1, ". ", font_bold("Otherwise: "), font_red(paste0("Negative")), "\n", sep = "")
cat("\nUnmatched rows will return ", font_red("NA"), ".\n", sep = "")
if (isTRUE(attributes(x)$as_factor)) {
cat("Results will be of class 'factor', with ordered levels: ", paste0(attributes(x)$values, collapse = " < "), "\n", sep = "")
} else {
cat("Results will be of class 'character'.\n")
}
}
run_custom_mdro_guideline <- function(df, guideline, info) {
n_dots <- length(guideline)
stop_if(n_dots == 0, "no custom guidelines set", call = -2)
out <- character(length = NROW(df))
reasons <- character(length = NROW(df))
for (i in seq_len(n_dots)) {
qry <- tryCatch(eval(parse(text = guideline[[i]]$query), envir = df, enclos = parent.frame()),
error = function(e) {
AMR_env$err_msg <- e$message
return("error")
}
)
if (identical(qry, "error")) {
warning_("in `custom_mdro_guideline()`: rule ", i,
" (`", as.character(guideline[[i]]$query), "`) was ignored because of this error message: ",
AMR_env$err_msg,
call = FALSE,
add_fn = font_red
)
next
}
stop_ifnot(is.logical(qry), "in custom_mdro_guideline(): rule ", i, " (`", guideline[[i]]$query,
"`) must return `TRUE` or `FALSE`, not ",
format_class(class(qry), plural = FALSE),
call = FALSE
)
new_mdros <- which(qry == TRUE & out == "")
if (isTRUE(info)) {
cat(word_wrap(
"- Custom MDRO rule ", i, ": `", as.character(guideline[[i]]$query),
"` (", length(new_mdros), " rows matched)"
), "\n", sep = "")
}
val <- guideline[[i]]$value
out[new_mdros] <- val
reasons[new_mdros] <- paste0(
"matched rule ",
gsub("rule", "", names(guideline)[i], fixed = TRUE), ": ", as.character(guideline[[i]]$query)
)
}
out[out == ""] <- "Negative"
reasons[out == "Negative"] <- "no rules matched"
if (isTRUE(attributes(guideline)$as_factor)) {
out <- factor(out, levels = attributes(guideline)$values, ordered = TRUE)
}
all_nonsusceptible_columns <- as.data.frame(t(df[, is.sir(df), drop = FALSE] == "R"))
all_nonsusceptible_columns <- vapply(
FUN.VALUE = character(1),
all_nonsusceptible_columns,
function(x) paste0(rownames(all_nonsusceptible_columns)[which(x)], collapse = ", ")
)
all_nonsusceptible_columns[is.na(out)] <- NA_character_
data.frame(
row_number = seq_len(NROW(df)),
MDRO = out,
reason = reasons,
all_nonsusceptible_columns = all_nonsusceptible_columns,
stringsAsFactors = FALSE
)
}
#' @rdname mdro
#' @export
brmo <- function(x = NULL, only_sir_columns = any(is.sir(x)), ...) {

View File

@ -1903,7 +1903,7 @@ pillar_shaft.sir <- function(x, ...) {
# colours will anyway not work when has_colour() == FALSE,
# but then the indentation should also not be applied
out[is.na(x)] <- font_grey(" NA")
out[x == "NI"] <- font_grey_bg(" NI ")
out[x == "NI"] <- font_grey_bg(font_black(" NI "))
out[x == "S"] <- font_green_bg(" S ")
out[x == "I"] <- font_orange_bg(" I ")
out[x == "SDD"] <- font_orange_bg(" SDD ")

View File

@ -89,7 +89,7 @@ navbar:
href: "articles/PCA.html"
- text: "Determine Multi-Drug Resistance (MDR)"
icon: "fa-skull-crossbones"
href: "articles/MDR.html"
href: "reference/mdro.html"
- text: "Work with WHONET Data"
icon: "fa-globe-americas"
href: "articles/WHONET.html"

View File

@ -197,7 +197,7 @@ This package was intended as a comprehensive toolbox for integrated AMR data ana
* Retrieving antimicrobial drug names, doses and forms of administration from clinical health care records ([manual](./reference/ab_from_text.html))
* Determining first isolates to be used for AMR data analysis ([manual](./reference/first_isolate.html))
* Calculating antimicrobial resistance ([tutorial](./articles/AMR.html))
* Determining multi-drug resistance (MDR) / multi-drug resistant organisms (MDRO) ([tutorial](./articles/MDR.html))
* Determining multi-drug resistance (MDR) / multi-drug resistant organisms (MDRO) ([tutorial](./reference/mdro.html))
* Calculating (empirical) susceptibility of both mono therapy and combination therapies ([tutorial](./articles/AMR.html))
* Apply AMR functions in predictive modelling ([tutorial](./articles/AMR_with_tidymodels.html))
* Getting properties for any microorganism (like Gram stain, species, genus or family) ([manual](./reference/mo_property.html))

View File

@ -397,7 +397,7 @@ data analysis. This package can be used for:
([manual](./reference/first_isolate.html))
- Calculating antimicrobial resistance ([tutorial](./articles/AMR.html))
- Determining multi-drug resistance (MDR) / multi-drug resistant
organisms (MDRO) ([tutorial](./articles/MDR.html))
organisms (MDRO) ([tutorial](./reference/mdro.html))
- Calculating (empirical) susceptibility of both mono therapy and
combination therapies ([tutorial](./articles/AMR.html))
- Apply AMR function in predictive modelling

View File

@ -72,6 +72,11 @@ eucast_rules(df, rules = "custom", custom_rules = y, info = FALSE, overwrite = T
}\if{html}{\out{</div>}}
}
\subsection{Sharing rules among multiple users}{
The rules set (the \code{y} object in this case) could be exported to a shared file location using \code{\link[=saveRDS]{saveRDS()}} if you collaborate with multiple users. The custom rules set could then be imported using \code{\link[=readRDS]{readRDS()}}.
}
\subsection{Usage of multiple antimicrobials and antimicrobial group names}{
You can define antimicrobial groups instead of single antimicrobials for the rule consequence, which is the part \emph{after} the tilde (~). In the examples above, the antimicrobial group \code{aminopenicillins} includes both ampicillin and amoxicillin.

View File

@ -0,0 +1,167 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/custom_mdro_guideline.R
\name{custom_mdro_guideline}
\alias{custom_mdro_guideline}
\alias{c.custom_mdro_guideline}
\alias{print.custom_mdro_guideline}
\title{Define Custom MDRO Guideline}
\usage{
custom_mdro_guideline(..., as_factor = TRUE)
\method{c}{custom_mdro_guideline}(x, ..., as_factor = NULL)
\method{print}{custom_mdro_guideline}(x, ...)
}
\arguments{
\item{...}{Guideline rules in \link[base:tilde]{formula} notation, see below for instructions, and in \emph{Examples}.}
\item{as_factor}{A \link{logical} to indicate whether the returned value should be an ordered \link{factor} (\code{TRUE}, default), or otherwise a \link{character} vector.}
\item{x}{A \link{data.frame} with antimicrobials columns, like \code{AMX} or \code{amox}. Can be left blank for automatic determination.}
}
\value{
A \link{list} containing the custom rules
}
\description{
Define custom a MDRO guideline for your organisation or specific analysis and use the output of this function in \code{\link[=mdro]{mdro()}}.
}
\details{
Using a custom MDRO guideline is of importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
}
\section{How it works}{
\subsection{Basics}{
If you are familiar with the \code{\link[dplyr:case_when]{case_when()}} function of the \code{dplyr} package, you will recognise the input method to set your own rules. Rules must be set using what \R considers to be the 'formula notation'. The rule itself is written \emph{before} the tilde (\code{~}) and the consequence of the rule is written \emph{after} the tilde:
\if{html}{\out{<div class="sourceCode r">}}\preformatted{custom <- custom_mdro_guideline(CIP == "R" & age > 60 ~ "Elderly Type A",
ERY == "R" & age > 60 ~ "Elderly Type B")
}\if{html}{\out{</div>}}
If a row/an isolate matches the first rule, the value after the first \code{~} (in this case \emph{'Elderly Type A'}) will be set as MDRO value. Otherwise, the second rule will be tried and so on. The number of rules is unlimited.
You can print the rules set in the console for an overview. Colours will help reading it if your console supports colours.
\if{html}{\out{<div class="sourceCode r">}}\preformatted{custom
#> A set of custom MDRO rules:
#> 1. If CIP is R and age is higher than 60 then: Elderly Type A
#> 2. If ERY is R and age is higher than 60 then: Elderly Type B
#> 3. Otherwise: Negative
#> Unmatched rows will return NA.
#> Results will be of class 'factor', with ordered levels: Negative < Elderly Type A < Elderly Type B
}\if{html}{\out{</div>}}
The outcome of the function can be used for the \code{guideline} argument in the \code{\link[=mdro]{mdro()}} function:
\if{html}{\out{<div class="sourceCode r">}}\preformatted{x <- mdro(example_isolates, guideline = custom)
#> Determining MDROs based on custom rules, resulting in factor levels: Negative < Elderly Type A < Elderly Type B.
#> - Custom MDRO rule 1: CIP == "R" & age > 60 (198 rows matched)
#> - Custom MDRO rule 2: ERY == "R" & age > 60 (732 rows matched)
#> => Found 930 custom defined MDROs out of 2000 isolates (46.5\%)
table(x)
#> x
#> Negative Elderly Type A Elderly Type B
#> 1070 198 732
}\if{html}{\out{</div>}}
Rules can also be combined with other custom rules by using \code{\link[=c]{c()}}:
\if{html}{\out{<div class="sourceCode r">}}\preformatted{x <- mdro(example_isolates,
guideline = c(custom,
custom_mdro_guideline(ERY == "R" & age > 50 ~ "Elderly Type C")))
#> Determining MDROs based on custom rules, resulting in factor levels: Negative < Elderly Type A < Elderly Type B < Elderly Type C.
#> - Custom MDRO rule 1: CIP == "R" & age > 60 (198 rows matched)
#> - Custom MDRO rule 2: ERY == "R" & age > 60 (732 rows matched)
#> - Custom MDRO rule 3: ERY == "R" & age > 50 (109 rows matched)
#> => Found 1039 custom defined MDROs out of 2000 isolates (52.0\%)
table(x)
#> x
#> Negative Elderly Type A Elderly Type B Elderly Type C
#> 961 198 732 109
}\if{html}{\out{</div>}}
}
\subsection{Sharing rules among multiple users}{
The rules set (the \code{custom} object in this case) could be exported to a shared file location using \code{\link[=saveRDS]{saveRDS()}} if you collaborate with multiple users. The custom rules set could then be imported using \code{\link[=readRDS]{readRDS()}}.
}
\subsection{Usage of multiple antimicrobials and antimicrobial group names}{
You can define antimicrobial groups instead of single antimicrobials for the rule itself, which is the part \emph{before} the tilde (~). Use \code{\link[=any]{any()}} or \code{\link[=all]{all()}} to specify the scope of the antimicrobial group:
\if{html}{\out{<div class="sourceCode r">}}\preformatted{custom_mdro_guideline(
AMX == "R" ~ "My MDRO #1",
any(cephalosporins_2nd() == "R") ~ "My MDRO #2",
all(glycopeptides() == "R") ~ "My MDRO #3"
)
}\if{html}{\out{</div>}}
These 35 antimicrobial groups are allowed in the rules (case-insensitive) and can be used in any combination:
\itemize{
\item aminoglycosides\cr(amikacin, amikacin/fosfomycin, apramycin, arbekacin, astromicin, bekanamycin, dibekacin, framycetin, gentamicin, gentamicin-high, habekacin, hygromycin, isepamicin, kanamycin, kanamycin-high, kanamycin/cephalexin, micronomicin, neomycin, netilmicin, pentisomicin, plazomicin, propikacin, ribostamycin, sisomicin, streptoduocin, streptomycin, streptomycin-high, tobramycin, and tobramycin-high)
\item aminopenicillins\cr(amoxicillin and ampicillin)
\item antifungals\cr(amorolfine, amphotericin B, amphotericin B-high, anidulafungin, butoconazole, caspofungin, ciclopirox, clotrimazole, econazole, fluconazole, flucytosine, fosfluconazole, griseofulvin, hachimycin, ibrexafungerp, isavuconazole, isoconazole, itraconazole, ketoconazole, manogepix, micafungin, miconazole, nystatin, oteseconazole, pimaricin, posaconazole, rezafungin, ribociclib, sulconazole, terbinafine, terconazole, and voriconazole)
\item antimycobacterials\cr(4-aminosalicylic acid, calcium aminosalicylate, capreomycin, clofazimine, delamanid, enviomycin, ethambutol, ethambutol/isoniazid, ethionamide, isoniazid, isoniazid/sulfamethoxazole/trimethoprim/pyridoxine, morinamide, p-aminosalicylic acid, pretomanid, protionamide, pyrazinamide, rifabutin, rifampicin, rifampicin/ethambutol/isoniazid, rifampicin/isoniazid, rifampicin/pyrazinamide/ethambutol/isoniazid, rifampicin/pyrazinamide/isoniazid, rifamycin, rifapentine, simvastatin/fenofibrate, sodium aminosalicylate, streptomycin/isoniazid, terizidone, thioacetazone, thioacetazone/isoniazid, tiocarlide, and viomycin)
\item betalactams\cr(amoxicillin, amoxicillin/clavulanic acid, amoxicillin/sulbactam, ampicillin, ampicillin/sulbactam, apalcillin, aspoxicillin, azidocillin, azlocillin, aztreonam, aztreonam/avibactam, aztreonam/nacubactam, bacampicillin, benzathine benzylpenicillin, benzathine phenoxymethylpenicillin, benzylpenicillin, benzylpenicillin screening test, biapenem, carbenicillin, carindacillin, carumonam, cefacetrile, cefaclor, cefadroxil, cefalexin, cefaloridine, cefalotin, cefamandole, cefapirin, cefatrizine, cefazedone, cefazolin, cefcapene, cefcapene pivoxil, cefdinir, cefditoren, cefditoren pivoxil, cefepime, cefepime/amikacin, cefepime/clavulanic acid, cefepime/enmetazobactam, cefepime/nacubactam, cefepime/tazobactam, cefepime/zidebactam, cefetamet, cefetamet pivoxil, cefetecol, cefetrizole, cefiderocol, cefixime, cefmenoxime, cefmetazole, cefodizime, cefonicid, cefoperazone, cefoperazone/sulbactam, ceforanide, cefoselis, cefotaxime, cefotaxime screening test, cefotaxime/clavulanic acid, cefotaxime/sulbactam, cefotetan, cefotiam, cefotiam hexetil, cefovecin, cefoxitin, cefoxitin screening test, cefozopran, cefpimizole, cefpiramide, cefpirome, cefpodoxime, cefpodoxime proxetil, cefpodoxime/clavulanic acid, cefprozil, cefquinome, cefroxadine, cefsulodin, cefsumide, ceftaroline, ceftaroline/avibactam, ceftazidime, ceftazidime/avibactam, ceftazidime/clavulanic acid, cefteram, cefteram pivoxil, ceftezole, ceftibuten, ceftiofur, ceftizoxime, ceftizoxime alapivoxil, ceftobiprole, ceftobiprole medocaril, ceftolozane/tazobactam, ceftriaxone, ceftriaxone/beta-lactamase inhibitor, cefuroxime, cefuroxime axetil, cephradine, ciclacillin, clometocillin, cloxacillin, dicloxacillin, doripenem, epicillin, ertapenem, flucloxacillin, hetacillin, imipenem, imipenem/EDTA, imipenem/relebactam, latamoxef, lenampicillin, loracarbef, mecillinam, meropenem, meropenem/nacubactam, meropenem/vaborbactam, metampicillin, meticillin, mezlocillin, mezlocillin/sulbactam, nafcillin, oxacillin, oxacillin screening test, panipenem, penamecillin, penicillin/novobiocin, penicillin/sulbactam, pheneticillin, phenoxymethylpenicillin, piperacillin, piperacillin/sulbactam, piperacillin/tazobactam, piridicillin, pivampicillin, pivmecillinam, procaine benzylpenicillin, propicillin, razupenem, ritipenem, ritipenem acoxil, sarmoxicillin, sulbenicillin, sultamicillin, talampicillin, tebipenem, temocillin, ticarcillin, ticarcillin/clavulanic acid, and tigemonam)
\item betalactams_with_inhibitor\cr(amoxicillin/clavulanic acid, amoxicillin/sulbactam, ampicillin/sulbactam, aztreonam/avibactam, aztreonam/nacubactam, cefepime/amikacin, cefepime/clavulanic acid, cefepime/enmetazobactam, cefepime/nacubactam, cefepime/tazobactam, cefepime/zidebactam, cefoperazone/sulbactam, cefotaxime/clavulanic acid, cefotaxime/sulbactam, cefpodoxime/clavulanic acid, ceftaroline/avibactam, ceftazidime/avibactam, ceftazidime/clavulanic acid, ceftolozane/tazobactam, ceftriaxone/beta-lactamase inhibitor, imipenem/relebactam, meropenem/nacubactam, meropenem/vaborbactam, mezlocillin/sulbactam, penicillin/novobiocin, penicillin/sulbactam, piperacillin/sulbactam, piperacillin/tazobactam, and ticarcillin/clavulanic acid)
\item carbapenems\cr(biapenem, doripenem, ertapenem, imipenem, imipenem/EDTA, imipenem/relebactam, meropenem, meropenem/nacubactam, meropenem/vaborbactam, panipenem, razupenem, ritipenem, ritipenem acoxil, and tebipenem)
\item cephalosporins\cr(cefacetrile, cefaclor, cefadroxil, cefalexin, cefaloridine, cefalotin, cefamandole, cefapirin, cefatrizine, cefazedone, cefazolin, cefcapene, cefcapene pivoxil, cefdinir, cefditoren, cefditoren pivoxil, cefepime, cefepime/amikacin, cefepime/clavulanic acid, cefepime/enmetazobactam, cefepime/nacubactam, cefepime/tazobactam, cefepime/zidebactam, cefetamet, cefetamet pivoxil, cefetecol, cefetrizole, cefiderocol, cefixime, cefmenoxime, cefmetazole, cefodizime, cefonicid, cefoperazone, cefoperazone/sulbactam, ceforanide, cefoselis, cefotaxime, cefotaxime screening test, cefotaxime/clavulanic acid, cefotaxime/sulbactam, cefotetan, cefotiam, cefotiam hexetil, cefovecin, cefoxitin, cefoxitin screening test, cefozopran, cefpimizole, cefpiramide, cefpirome, cefpodoxime, cefpodoxime proxetil, cefpodoxime/clavulanic acid, cefprozil, cefquinome, cefroxadine, cefsulodin, cefsumide, ceftaroline, ceftaroline/avibactam, ceftazidime, ceftazidime/avibactam, ceftazidime/clavulanic acid, cefteram, cefteram pivoxil, ceftezole, ceftibuten, ceftiofur, ceftizoxime, ceftizoxime alapivoxil, ceftobiprole, ceftobiprole medocaril, ceftolozane/tazobactam, ceftriaxone, ceftriaxone/beta-lactamase inhibitor, cefuroxime, cefuroxime axetil, cephradine, latamoxef, and loracarbef)
\item cephalosporins_1st\cr(cefacetrile, cefadroxil, cefalexin, cefaloridine, cefalotin, cefapirin, cefatrizine, cefazedone, cefazolin, cefroxadine, ceftezole, and cephradine)
\item cephalosporins_2nd\cr(cefaclor, cefamandole, cefmetazole, cefonicid, ceforanide, cefotetan, cefotiam, cefoxitin, cefoxitin screening test, cefprozil, cefuroxime, cefuroxime axetil, and loracarbef)
\item cephalosporins_3rd\cr(cefcapene, cefcapene pivoxil, cefdinir, cefditoren, cefditoren pivoxil, cefetamet, cefetamet pivoxil, cefixime, cefmenoxime, cefodizime, cefoperazone, cefoperazone/sulbactam, cefotaxime, cefotaxime screening test, cefotaxime/clavulanic acid, cefotaxime/sulbactam, cefotiam hexetil, cefovecin, cefpimizole, cefpiramide, cefpodoxime, cefpodoxime proxetil, cefpodoxime/clavulanic acid, cefsulodin, ceftazidime, ceftazidime/avibactam, ceftazidime/clavulanic acid, cefteram, cefteram pivoxil, ceftibuten, ceftiofur, ceftizoxime, ceftizoxime alapivoxil, ceftriaxone, ceftriaxone/beta-lactamase inhibitor, and latamoxef)
\item cephalosporins_4th\cr(cefepime, cefepime/amikacin, cefepime/clavulanic acid, cefepime/enmetazobactam, cefepime/nacubactam, cefepime/tazobactam, cefepime/zidebactam, cefetecol, cefoselis, cefozopran, cefpirome, and cefquinome)
\item cephalosporins_5th\cr(ceftaroline, ceftaroline/avibactam, ceftobiprole, ceftobiprole medocaril, and ceftolozane/tazobactam)
\item cephalosporins_except_caz\cr(cefacetrile, cefaclor, cefadroxil, cefalexin, cefaloridine, cefalotin, cefamandole, cefapirin, cefatrizine, cefazedone, cefazolin, cefcapene, cefcapene pivoxil, cefdinir, cefditoren, cefditoren pivoxil, cefepime, cefepime/amikacin, cefepime/clavulanic acid, cefepime/enmetazobactam, cefepime/nacubactam, cefepime/tazobactam, cefepime/zidebactam, cefetamet, cefetamet pivoxil, cefetecol, cefetrizole, cefiderocol, cefixime, cefmenoxime, cefmetazole, cefodizime, cefonicid, cefoperazone, cefoperazone/sulbactam, ceforanide, cefoselis, cefotaxime, cefotaxime screening test, cefotaxime/clavulanic acid, cefotaxime/sulbactam, cefotetan, cefotiam, cefotiam hexetil, cefovecin, cefoxitin, cefoxitin screening test, cefozopran, cefpimizole, cefpiramide, cefpirome, cefpodoxime, cefpodoxime proxetil, cefpodoxime/clavulanic acid, cefprozil, cefquinome, cefroxadine, cefsulodin, cefsumide, ceftaroline, ceftaroline/avibactam, ceftazidime/avibactam, ceftazidime/clavulanic acid, cefteram, cefteram pivoxil, ceftezole, ceftibuten, ceftiofur, ceftizoxime, ceftizoxime alapivoxil, ceftobiprole, ceftobiprole medocaril, ceftolozane/tazobactam, ceftriaxone, ceftriaxone/beta-lactamase inhibitor, cefuroxime, cefuroxime axetil, cephradine, latamoxef, and loracarbef)
\item fluoroquinolones\cr(besifloxacin, ciprofloxacin, ciprofloxacin/metronidazole, ciprofloxacin/ornidazole, ciprofloxacin/tinidazole, clinafloxacin, danofloxacin, delafloxacin, difloxacin, enoxacin, enrofloxacin, finafloxacin, fleroxacin, garenoxacin, gatifloxacin, gemifloxacin, grepafloxacin, lascufloxacin, levofloxacin, levofloxacin/ornidazole, levonadifloxacin, lomefloxacin, marbofloxacin, metioxate, miloxacin, moxifloxacin, nadifloxacin, nemonoxacin, nifuroquine, nitroxoline, norfloxacin, norfloxacin screening test, norfloxacin/metronidazole, norfloxacin/tinidazole, ofloxacin, ofloxacin/ornidazole, orbifloxacin, pazufloxacin, pefloxacin, pefloxacin screening test, pradofloxacin, premafloxacin, prulifloxacin, rufloxacin, sarafloxacin, sitafloxacin, sparfloxacin, temafloxacin, tilbroquinol, tioxacin, tosufloxacin, and trovafloxacin)
\item glycopeptides\cr(avoparcin, bleomycin, dalbavancin, norvancomycin, oritavancin, ramoplanin, teicoplanin, teicoplanin-macromethod, telavancin, vancomycin, and vancomycin-macromethod)
\item glycopeptides_except_lipo\cr(avoparcin, bleomycin, norvancomycin, ramoplanin, teicoplanin, teicoplanin-macromethod, vancomycin, and vancomycin-macromethod)
\item isoxazolylpenicillins\cr(cloxacillin, dicloxacillin, flucloxacillin, meticillin, oxacillin, and oxacillin screening test)
\item lincosamides\cr(clindamycin, lincomycin, and pirlimycin)
\item lipoglycopeptides\cr(dalbavancin, oritavancin, and telavancin)
\item macrolides\cr(acetylmidecamycin, acetylspiramycin, azithromycin, clarithromycin, dirithromycin, erythromycin, flurithromycin, gamithromycin, josamycin, kitasamycin, meleumycin, midecamycin, miocamycin, nafithromycin, oleandomycin, rokitamycin, roxithromycin, solithromycin, spiramycin, telithromycin, tildipirosin, tilmicosin, troleandomycin, tulathromycin, tylosin, and tylvalosin)
\item monobactams\cr(aztreonam, aztreonam/avibactam, aztreonam/nacubactam, carumonam, and tigemonam)
\item nitrofurans\cr(furazidin, furazolidone, nifurtoinol, nitrofurantoin, and nitrofurazone)
\item oxazolidinones\cr(cadazolid, cycloserine, linezolid, tedizolid, and thiacetazone)
\item penicillins\cr(amoxicillin, amoxicillin/clavulanic acid, amoxicillin/sulbactam, ampicillin, ampicillin/sulbactam, apalcillin, aspoxicillin, azidocillin, azlocillin, bacampicillin, benzathine benzylpenicillin, benzathine phenoxymethylpenicillin, benzylpenicillin, benzylpenicillin screening test, carbenicillin, carindacillin, ciclacillin, clometocillin, cloxacillin, dicloxacillin, epicillin, flucloxacillin, hetacillin, lenampicillin, mecillinam, metampicillin, meticillin, mezlocillin, mezlocillin/sulbactam, nafcillin, oxacillin, oxacillin screening test, penamecillin, penicillin/novobiocin, penicillin/sulbactam, pheneticillin, phenoxymethylpenicillin, piperacillin, piperacillin/sulbactam, piperacillin/tazobactam, piridicillin, pivampicillin, pivmecillinam, procaine benzylpenicillin, propicillin, sarmoxicillin, sulbenicillin, sultamicillin, talampicillin, temocillin, ticarcillin, and ticarcillin/clavulanic acid)
\item phenicols\cr(chloramphenicol, florfenicol, and thiamphenicol)
\item polymyxins\cr(colistin, polymyxin B, and polymyxin B/polysorbate 80)
\item quinolones\cr(besifloxacin, cinoxacin, ciprofloxacin, ciprofloxacin/metronidazole, ciprofloxacin/ornidazole, ciprofloxacin/tinidazole, clinafloxacin, danofloxacin, delafloxacin, difloxacin, enoxacin, enrofloxacin, finafloxacin, fleroxacin, flumequine, garenoxacin, gatifloxacin, gemifloxacin, grepafloxacin, lascufloxacin, levofloxacin, levofloxacin/ornidazole, levonadifloxacin, lomefloxacin, marbofloxacin, metioxate, miloxacin, moxifloxacin, nadifloxacin, nalidixic acid, nalidixic acid screening test, nemonoxacin, nifuroquine, nitroxoline, norfloxacin, norfloxacin screening test, norfloxacin/metronidazole, norfloxacin/tinidazole, ofloxacin, ofloxacin/ornidazole, orbifloxacin, oxolinic acid, pazufloxacin, pefloxacin, pefloxacin screening test, pipemidic acid, piromidic acid, pradofloxacin, premafloxacin, prulifloxacin, rosoxacin, rufloxacin, sarafloxacin, sitafloxacin, sparfloxacin, temafloxacin, tilbroquinol, tioxacin, tosufloxacin, and trovafloxacin)
\item rifamycins\cr(rifabutin, rifampicin, rifampicin/ethambutol/isoniazid, rifampicin/isoniazid, rifampicin/pyrazinamide/ethambutol/isoniazid, rifampicin/pyrazinamide/isoniazid, rifamycin, and rifapentine)
\item streptogramins\cr(pristinamycin and quinupristin/dalfopristin)
\item sulfonamides\cr(brodimoprim, sulfadiazine, sulfadiazine/tetroxoprim, sulfadimethoxine, sulfadimidine, sulfafurazole, sulfaisodimidine, sulfalene, sulfamazone, sulfamerazine, sulfamethizole, sulfamethoxazole, sulfamethoxypyridazine, sulfametomidine, sulfametoxydiazine, sulfamoxole, sulfanilamide, sulfaperin, sulfaphenazole, sulfapyridine, sulfathiazole, and sulfathiourea)
\item tetracyclines\cr(cetocycline, chlortetracycline, clomocycline, demeclocycline, doxycycline, eravacycline, lymecycline, metacycline, minocycline, omadacycline, oxytetracycline, penimepicycline, rolitetracycline, sarecycline, tetracycline, tetracycline screening test, and tigecycline)
\item tetracyclines_except_tgc\cr(cetocycline, chlortetracycline, clomocycline, demeclocycline, doxycycline, eravacycline, lymecycline, metacycline, minocycline, omadacycline, oxytetracycline, penimepicycline, rolitetracycline, sarecycline, tetracycline, and tetracycline screening test)
\item trimethoprims\cr(brodimoprim, sulfadiazine, sulfadiazine/tetroxoprim, sulfadiazine/trimethoprim, sulfadimethoxine, sulfadimidine, sulfadimidine/trimethoprim, sulfafurazole, sulfaisodimidine, sulfalene, sulfamazone, sulfamerazine, sulfamerazine/trimethoprim, sulfamethizole, sulfamethoxazole, sulfamethoxypyridazine, sulfametomidine, sulfametoxydiazine, sulfametrole/trimethoprim, sulfamoxole, sulfamoxole/trimethoprim, sulfanilamide, sulfaperin, sulfaphenazole, sulfapyridine, sulfathiazole, sulfathiourea, trimethoprim, and trimethoprim/sulfamethoxazole)
\item ureidopenicillins\cr(azlocillin, mezlocillin, piperacillin, and piperacillin/tazobactam)
}
}
}
\examples{
x <- custom_mdro_guideline(
CIP == "R" & age > 60 ~ "Elderly Type A",
ERY == "R" & age > 60 ~ "Elderly Type B"
)
x
# run the custom rule set (verbose = TRUE will return a logbook instead of the data set):
out <- mdro(example_isolates, guideline = x)
table(out)
out <- mdro(example_isolates, guideline = x, verbose = TRUE)
head(out)
# you can create custom guidelines using selectors (see ?antimicrobial_selectors)
my_guideline <- custom_mdro_guideline(
AMX == "R" ~ "Custom MDRO 1",
all(cephalosporins_2nd() == "R") ~ "Custom MDRO 2"
)
my_guideline
out <- mdro(example_isolates, guideline = my_guideline)
table(out)
}

View File

@ -8,24 +8,18 @@
\alias{BRMO}
\alias{3MRGN}
\alias{4MRGN}
\alias{custom_mdro_guideline}
\alias{brmo}
\alias{mrgn}
\alias{mdr_tb}
\alias{mdr_cmi2012}
\alias{eucast_exceptional_phenotypes}
\title{Determine Multidrug-Resistant Organisms (MDRO)}
\source{
See the supported guidelines above for the \link{list} of publications used for this function.
}
\usage{
mdro(x = NULL, guideline = "CMI 2012", col_mo = NULL, esbl = NA,
carbapenemase = NA, mecA = NA, mecC = NA, vanA = NA, vanB = NA,
info = interactive(), pct_required_classes = 0.5, combine_SI = TRUE,
verbose = FALSE, only_sir_columns = any(is.sir(x)), ...)
custom_mdro_guideline(..., as_factor = TRUE)
brmo(x = NULL, only_sir_columns = any(is.sir(x)), ...)
mrgn(x = NULL, only_sir_columns = any(is.sir(x)), verbose = FALSE, ...)
@ -94,9 +88,9 @@ These functions are context-aware. This means that the \code{x} argument can be
For the \code{pct_required_classes} argument, values above 1 will be divided by 100. This is to support both fractions (\code{0.75} or \code{3/4}) and percentages (\code{75}).
\strong{Note:} Every test that involves the Enterobacteriaceae family, will internally be performed using its newly named \emph{order} Enterobacterales, since the Enterobacteriaceae family has been taxonomically reclassified by Adeolu \emph{et al.} in 2016. Before that, Enterobacteriaceae was the only family under the Enterobacteriales (with an i) order. All species under the old Enterobacteriaceae family are still under the new Enterobacterales (without an i) order, but divided into multiple families. The way tests are performed now by this \code{\link[=mdro]{mdro()}} function makes sure that results from before 2016 and after 2016 are identical.
}
\section{Supported International / National Guidelines}{
\subsection{Supported International / National Guidelines}{
Please suggest to implement guidelines by \href{https://github.com/msberends/AMR/issues/new?template=2-feature-request.yml&title=Add\%20new\%20MDRO\%20guideline}{letting us know}.
Currently supported guidelines are (case-insensitive):
\itemize{
@ -106,12 +100,16 @@ Magiorakos AP, Srinivasan A \emph{et al.} "Multidrug-resistant, extensively drug
\item \code{guideline = "EUCAST 3.3"} (or simply \code{guideline = "EUCAST"})
The European international guideline - EUCAST Expert Rules Version 3.3 "Intrinsic Resistance and Unusual Phenotypes" (\href{https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2021/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.3_20211018.pdf}{link})
Also:
\itemize{
\item \code{guideline = "EUCAST 3.2"}
The European international guideline - EUCAST Expert Rules Version 3.2 "Intrinsic Resistance and Unusual Phenotypes" (\href{https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2020/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.2_20200225.pdf}{link})
The former European international guideline - EUCAST Expert Rules Version 3.2 "Intrinsic Resistance and Unusual Phenotypes" (\href{https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2020/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.2_20200225.pdf}{link})
\item \code{guideline = "EUCAST 3.1"}
The European international guideline - EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" (\href{https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf}{link})
The former European international guideline - EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" (\href{https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf}{link})
}
\item \code{guideline = "TB"}
The international guideline for multi-drug resistant tuberculosis - World Health Organization "Companion handbook to the WHO guidelines for the programmatic management of drug-resistant tuberculosis" (\href{https://www.who.int/publications/i/item/9789241548809}{link})
@ -129,56 +127,15 @@ Also:
The former Dutch national guideline - Werkgroep Infectiepreventie (WIP), RIVM, last revision as of 2017: "Bijzonder Resistente Micro-Organismen (BRMO)"
}
}
Please suggest to implement guidelines by letting us know: \url{https://github.com/msberends/AMR/issues/new}.
}
\section{Using Custom Guidelines}{
\subsection{Using Custom Guidelines}{
Using a custom MDRO guideline is of importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
Custom guidelines can be set with the \code{\link[=custom_mdro_guideline]{custom_mdro_guideline()}} function. This is of great importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
If you are familiar with the \code{\link[dplyr:case_when]{case_when()}} function of the \code{dplyr} package, you will recognise the input method to set your own rules. Rules must be set using what \R considers to be the 'formula notation'. The rule is written \emph{before} the tilde (\code{~}) and the consequence of the rule is written \emph{after} the tilde:
\if{html}{\out{<div class="sourceCode">}}\preformatted{custom <- custom_mdro_guideline(CIP == "R" & age > 60 ~ "Elderly Type A",
ERY == "R" & age > 60 ~ "Elderly Type B")
}\if{html}{\out{</div>}}
If a row/an isolate matches the first rule, the value after the first \code{~} (in this case \emph{'Elderly Type A'}) will be set as MDRO value. Otherwise, the second rule will be tried and so on. The number of rules is unlimited.
You can print the rules set in the console for an overview. Colours will help reading it if your console supports colours.
\if{html}{\out{<div class="sourceCode">}}\preformatted{custom
#> A set of custom MDRO rules:
#> 1. CIP is "R" and age is higher than 60 -> Elderly Type A
#> 2. ERY is "R" and age is higher than 60 -> Elderly Type B
#> 3. Otherwise -> Negative
#>
#> Unmatched rows will return NA.
}\if{html}{\out{</div>}}
The outcome of the function can be used for the \code{guideline} argument in the \code{\link[=mdro]{mdro()}} function:
\if{html}{\out{<div class="sourceCode">}}\preformatted{x <- mdro(example_isolates,
guideline = custom)
table(x)
#> Negative Elderly Type A Elderly Type B
#> 1070 198 732
}\if{html}{\out{</div>}}
Rules can also be combined with other custom rules by using \code{\link[=c]{c()}}:
\if{html}{\out{<div class="sourceCode">}}\preformatted{x <- mdro(example_isolates,
guideline = c(custom,
custom_mdro_guideline(ERY == "R" & age > 50 ~ "Elderly Type C")))
table(x)
#> Negative Elderly Type A Elderly Type B Elderly Type C
#> 961 198 732 109
}\if{html}{\out{</div>}}
The rules set (the \code{custom} object in this case) could be exported to a shared file location using \code{\link[=saveRDS]{saveRDS()}} if you collaborate with multiple users. The custom rules set could then be imported using \code{\link[=readRDS]{readRDS()}}.
Custom guidelines can be set with the \code{\link[=custom_mdro_guideline]{custom_mdro_guideline()}} function.
}
}
\section{Antimicrobials}{
To let the function automatically detect antimicrobial column names, do not provide any named arguments. It will then use \code{\link[=guess_ab_col]{guess_ab_col()}} to find them.
@ -194,29 +151,22 @@ This AMR package follows insight; use \code{\link[=susceptibility]{susceptibilit
}
\examples{
out <- mdro(example_isolates, guideline = "EUCAST")
out <- mdro(example_isolates)
str(out)
table(out)
out <- mdro(example_isolates,
guideline = custom_mdro_guideline(
AMX == "R" ~ "Custom MDRO 1",
VAN == "R" ~ "Custom MDRO 2"
)
)
out <- mdro(example_isolates, guideline = "EUCAST 3.3")
table(out)
\donttest{
if (require("dplyr")) {
example_isolates \%>\%
mdro() \%>\%
table()
# no need to define `x` when used inside dplyr verbs:
example_isolates \%>\%
mutate(MDRO = mdro()) \%>\%
pull(MDRO) \%>\%
table()
count(MDRO)
}
}
}
\seealso{
\code{\link[=custom_mdro_guideline]{custom_mdro_guideline()}}
}

View File

@ -1,163 +0,0 @@
---
title: "Determine multi-drug resistance (MDR)"
output:
rmarkdown::html_vignette:
toc: true
vignette: >
%\VignetteIndexEntry{Determine multi-drug resistance (MDR)}
%\VignetteEncoding{UTF-8}
%\VignetteEngine{knitr::rmarkdown}
editor_options:
chunk_output_type: console
---
```{r setup, include = FALSE, results = 'markup'}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
library(AMR)
```
With the function `mdro()`, you can determine which micro-organisms are multi-drug resistant organisms (MDRO).
### Type of input
The `mdro()` function takes a data set as input, such as a regular `data.frame`. It tries to automatically determine the right columns for info about your isolates, such as the name of the species and all columns with results of antimicrobial agents. See the help page for more info about how to set the right settings for your data with the command `?mdro`.
For WHONET data (and most other data), all settings are automatically set correctly.
### Guidelines
The `mdro()` function support multiple guidelines. You can select a guideline with the `guideline` parameter. Currently supported guidelines are (case-insensitive):
* `guideline = "CMI2012"` (default)
Magiorakos AP, Srinivasan A *et al.* "Multidrug-resistant, extensively drug-resistant and pandrug-resistant bacteria: an international expert proposal for interim standard definitions for acquired resistance." Clinical Microbiology and Infection (2012) ([link](https://www.clinicalmicrobiologyandinfection.com/article/S1198-743X(14)61632-3/fulltext))
* `guideline = "EUCAST3.2"` (or simply `guideline = "EUCAST"`)
The European international guideline - EUCAST Expert Rules Version 3.2 "Intrinsic Resistance and Unusual Phenotypes" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/2020/Intrinsic_Resistance_and_Unusual_Phenotypes_Tables_v3.2_20200225.pdf))
* `guideline = "EUCAST3.1"`
The European international guideline - EUCAST Expert Rules Version 3.1 "Intrinsic Resistance and Exceptional Phenotypes Tables" ([link](https://www.eucast.org/fileadmin/src/media/PDFs/EUCAST_files/Expert_Rules/Expert_rules_intrinsic_exceptional_V3.1.pdf))
* `guideline = "TB"`
The international guideline for multi-drug resistant tuberculosis - World Health Organization "Companion handbook to the WHO guidelines for the programmatic management of drug-resistant tuberculosis" ([link](https://www.who.int/tb/publications/pmdt_companionhandbook/en/))
* `guideline = "MRGN"`
The German national guideline - Mueller *et al.* (2015) Antimicrobial Resistance and Infection Control 4:7. DOI: 10.1186/s13756-015-0047-6
* `guideline = "BRMO"`
The Dutch national guideline - Rijksinstituut voor Volksgezondheid en Milieu "WIP-richtlijn BRMO (Bijzonder Resistente Micro-Organismen) (ZKH)" ([link](https://www.rivm.nl/wip-richtlijn-brmo-bijzonder-resistente-micro-organismen-zkh))
Please suggest your own (country-specific) guidelines by letting us know: <https://github.com/msberends/AMR/issues/new>.
#### Custom Guidelines
You can also use your own custom guideline. Custom guidelines can be set with the `custom_mdro_guideline()` function. This is of great importance if you have custom rules to determine MDROs in your hospital, e.g., rules that are dependent on ward, state of contact isolation or other variables in your data.
If you are familiar with `case_when()` of the `dplyr` package, you will recognise the input method to set your own rules. Rules must be set using what R considers to be the 'formula notation':
```{r}
custom <- custom_mdro_guideline(
CIP == "R" & age > 60 ~ "Elderly Type A",
ERY == "R" & age > 60 ~ "Elderly Type B"
)
```
If a row/an isolate matches the first rule, the value after the first `~` (in this case *'Elderly Type A'*) will be set as MDRO value. Otherwise, the second rule will be tried and so on. The maximum number of rules is unlimited.
You can print the rules set in the console for an overview. Colours will help reading it if your console supports colours.
```{r}
custom
```
The outcome of the function can be used for the `guideline` argument in the `mdro()` function:
```{r}
x <- mdro(example_isolates, guideline = custom)
table(x)
```
The rules set (the `custom` object in this case) could be exported to a shared file location using `saveRDS()` if you collaborate with multiple users. The custom rules set could then be imported using `readRDS()`.
### Examples
The `mdro()` function always returns an ordered `factor` for predefined guidelines. For example, the output of the default guideline by Magiorakos *et al.* returns a `factor` with levels 'Negative', 'MDR', 'XDR' or 'PDR' in that order.
The next example uses the `example_isolates` data set. This is a data set included with this package and contains full antibiograms of 2,000 microbial isolates. It reflects reality and can be used to practise AMR data analysis. If we test the MDR/XDR/PDR guideline on this data set, we get:
```{r, message = FALSE}
library(dplyr) # to support pipes: %>%
library(cleaner) # to create frequency tables
```
```{r, results = 'hide'}
example_isolates %>%
mdro() %>%
freq() # show frequency table of the result
```
```{r, echo = FALSE, results = 'asis', message = FALSE, warning = FALSE}
example_isolates %>%
mdro(info = FALSE) %>%
freq() # show frequency table of the result
```
For another example, I will create a data set to determine multi-drug resistant TB:
```{r}
# random_sir() is a helper function to generate
# a random vector with values S, I and R
my_TB_data <- data.frame(
rifampicin = random_sir(5000),
isoniazid = random_sir(5000),
gatifloxacin = random_sir(5000),
ethambutol = random_sir(5000),
pyrazinamide = random_sir(5000),
moxifloxacin = random_sir(5000),
kanamycin = random_sir(5000)
)
```
Because all column names are automatically verified for valid drug names or codes, this would have worked exactly the same way:
```{r, eval = FALSE}
my_TB_data <- data.frame(
RIF = random_sir(5000),
INH = random_sir(5000),
GAT = random_sir(5000),
ETH = random_sir(5000),
PZA = random_sir(5000),
MFX = random_sir(5000),
KAN = random_sir(5000)
)
```
The data set now looks like this:
```{r}
head(my_TB_data)
```
We can now add the interpretation of MDR-TB to our data set. You can use:
```r
mdro(my_TB_data, guideline = "TB")
```
or its shortcut `mdr_tb()`:
```{r}
my_TB_data$mdr <- mdr_tb(my_TB_data)
```
Create a frequency table of the results:
```{r, results = 'asis'}
freq(my_TB_data$mdr)
```

View File

@ -41,7 +41,7 @@ This package was intended as a comprehensive toolbox for integrated AMR data ana
* Retrieving antimicrobial drug names, doses and forms of administration from clinical health care records ([manual](https://amr-for-r.org/reference/ab_from_text.html))
* Determining first isolates to be used for AMR data analysis ([manual](https://amr-for-r.org/reference/first_isolate.html))
* Calculating antimicrobial resistance ([tutorial](https://amr-for-r.org/articles/AMR.html))
* Determining multi-drug resistance (MDR) / multi-drug resistant organisms (MDRO) ([tutorial](https://amr-for-r.org/articles/MDR.html))
* Determining multi-drug resistance (MDR) / multi-drug resistant organisms (MDRO) ([tutorial](https://amr-for-r.org/reference/mdro.html))
* Calculating (empirical) susceptibility of both mono therapy and combination therapies ([tutorial](https://amr-for-r.org/articles/AMR.html))
* Apply AMR functions in predictive modelling ([tutorial](https://amr-for-r.org/articles/AMR_with_tidymodels.html))
* Getting properties for any microorganism (like Gram stain, species, genus or family) ([manual](https://amr-for-r.org/reference/mo_property.html))