mirror of
https://github.com/msberends/AMR.git
synced 2026-11-15 22:20:47 +01:00
Store all screening breakpoints under screening agents, and add these agents to antimicrobials (#328)
Implements the decisions ticked in #328, and the behaviour specified in https://github.com/msberends/AMR/pull/339#issuecomment-6577438074: `include_screening` now defaults to NULL, using screening breakpoints for screening agents only. With TRUE, a regular agent also takes these breakpoints, and with FALSE a screening agent gives NA with a note. - antimicrobials gains 13 screening agents, e.g. GEN-S, ERY-S and STR1-S - GEH and STH rows move to GEN-S and STR1-S, and as.sir() keeps accepting both codes - Topical cut-off values move to -S agents with site "Topical" - WHONET-based rows move as ticked, including CLSI cefoxitin for staphylococci and pefloxacin for Salmonella, and EUCAST 2012-2018 cefoxitin MIC - Clinical breakpoints that EUCAST also allows as a screening test get a copy under the -S agent
This commit is contained in:
1 parent
fab20c5eaa
commit
2c98dac7ae
12 files changed
+213
-37
No files matched your search
+1
-1
@@ -51,7 +51,7 @@
|
||||
#' - For many plotting functions (e.g., for MIC or disk diffusion values), supplying `mo` and `ab` enables automatic SIR-based interpretative colouring. These colours are derived from [as.sir()] in the background and therefore depend on the active `guideline` setting, which again uses `r AMR::clinical_breakpoints$guideline[1]` if not set explicitly.
|
||||
#' * `AMR_ignore_pattern` \cr A [regular expression][base::regex] to ignore (i.e., make `NA`) any match given in [as.mo()] and all [`mo_*`][mo_property()] functions.
|
||||
#' * `AMR_include_PKPD` \cr A [logical] to use in [as.sir()], to indicate that PK/PD clinical breakpoints must be applied as a last resort - the default is `TRUE`.
|
||||
#' * `AMR_include_screening` \cr A [logical] to use in [as.sir()], to indicate that clinical breakpoints for screening are allowed - the default is `FALSE`.
|
||||
#' * `AMR_include_screening` \cr A [logical] to use in [as.sir()], to indicate that clinical breakpoints for screening are allowed - the default is `NULL`, using these for screening agents only, such as `FOX-S`. See [as.sir()].
|
||||
#' * `AMR_interpretive_rules` \cr A [character] to set the default types of rules for [interpretive_rules()], [eucast_rules()] and [clsi_rules()], must be one or more of: `"breakpoints"`, `"expected_phenotypes"`, `"expert"`, `"other"`, `"custom"`, `"all"`, and defaults to `c("breakpoints", "expected_phenotypes")`. This option replaces the old option `AMR_eucastrules`.
|
||||
#' * `AMR_keep_synonyms` \cr A [logical] to use in [as.mo()] and all [`mo_*`][mo_property()] functions, to indicate if old, previously valid taxonomic names must be preserved and not be corrected to currently accepted names. The default is `FALSE`.
|
||||
#' * `AMR_locale` \cr A [character] to set the language for the `AMR` package, can be one of these supported language names or [ISO 639-1 codes](https://en.wikipedia.org/wiki/ISO_639-1): `r vector_or(paste0(sapply(LANGUAGES_SUPPORTED_NAMES, function(x) x[[1]]), " (" , LANGUAGES_SUPPORTED, ")"), quotes = FALSE, sort = FALSE)`. The default is the current system language (if supported, English otherwise).
|
||||
|
||||
@@ -66,7 +66,7 @@ VALID_SIR_LEVELS <- c("S", "SDD", "I", "R", "NI", "WT", "NWT", "NS")
|
||||
#' @param as_wt_nwt A [logical] to return `"WT"`/`"NWT"` instead of `"S"`/`"R"`. Defaults to `TRUE` when using ECOFFs, i.e., when `breakpoint_type` is set to `"ECOFF"`.
|
||||
#' @param add_intrinsic_resistance *(only useful when using a EUCAST guideline)* a [logical] to indicate whether intrinsic antibiotic resistance must also be considered for applicable bug-drug combinations, meaning that e.g. ampicillin will always return "R" in *Klebsiella* species. Determination is based on the [intrinsic_resistant] data set, that itself is based on `r format_eucast_version_nr(3.3)`.
|
||||
#' @param substitute_missing_r_breakpoint A [logical] to indicate that a missing clinical breakpoints for R (resistant) must be substituted with R - the default is `FALSE`. Some (especially CLSI) breakpoints only have a breakpoint for S, meaning that the outcome can only be `"S"` or `NA`. Setting this to `TRUE` will convert the `NA`s in these cases to `"R"`. Can also be set with the package option [`AMR_substitute_missing_r_breakpoint`][AMR-options].
|
||||
#' @param include_screening A [logical] to indicate that clinical breakpoints for screening are allowed - the default is `FALSE`. Can also be set with the package option [`AMR_include_screening`][AMR-options].
|
||||
#' @param include_screening A [logical] to indicate that clinical breakpoints for screening are allowed. At default (`NULL`, can be set with the package option [`AMR_include_screening`][AMR-options]), these are only used for screening agents, e.g. `FOX-S`, but not for regular agents, e.g. `FOX`. Use `TRUE` to also use them for regular agents (with `FOX` then taking the breakpoints of `FOX-S`), and `FALSE` to never use them, which gives `NA` with a note for a screening agent.
|
||||
#' @param include_PKPD A [logical] to indicate that PK/PD clinical breakpoints must be applied as a last resort - the default is `TRUE`. Can also be set with the package option [`AMR_include_PKPD`][AMR-options].
|
||||
#' @param breakpoint_type The type of breakpoints to use, either `r vector_or(clinical_breakpoints$type, documentation = TRUE)`. ECOFF stands for Epidemiological Cut-Off values. The default is `"human"`, which can also be set with the package option [`AMR_breakpoint_type`][AMR-options]. If `host` is set to values of veterinary species, this will automatically be set to `"animal"`.
|
||||
#' @param host A vector (or column name) with [character]s to indicate the host. Only useful for veterinary breakpoints, as it requires `breakpoint_type = "animal"`. The values can be any text resembling the animal species, even in any of the `r length(LANGUAGES_SUPPORTED)` supported languages of this package. For foreign languages, be sure to set the language with [set_AMR_locale()] (though it will be automatically guessed based on the system language).
|
||||
@@ -664,7 +664,7 @@ as.sir.mic <- function(x,
|
||||
add_intrinsic_resistance = FALSE,
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -711,7 +711,7 @@ as.sir.disk <- function(x,
|
||||
add_intrinsic_resistance = FALSE,
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -758,7 +758,7 @@ as.sir.data.frame <- function(x,
|
||||
add_intrinsic_resistance = FALSE,
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -777,7 +777,7 @@ as.sir.data.frame <- function(x,
|
||||
meet_criteria(add_intrinsic_resistance, allow_class = "logical", has_length = 1)
|
||||
meet_criteria(reference_data, allow_class = "data.frame")
|
||||
meet_criteria(substitute_missing_r_breakpoint, allow_class = "logical", has_length = 1)
|
||||
meet_criteria(include_screening, allow_class = "logical", has_length = 1)
|
||||
meet_criteria(include_screening, allow_class = "logical", has_length = 1, allow_NULL = TRUE)
|
||||
meet_criteria(include_PKPD, allow_class = "logical", has_length = 1)
|
||||
meet_criteria(breakpoint_type, allow_class = "character", is_in = reference_data$type, has_length = 1)
|
||||
meet_criteria(host, allow_class = c("character", "factor"), allow_NULL = TRUE, allow_NA = TRUE)
|
||||
@@ -1385,7 +1385,7 @@ as_sir_method <- function(method_short,
|
||||
meet_criteria(add_intrinsic_resistance, allow_class = "logical", has_length = 1, .call_depth = -2)
|
||||
meet_criteria(reference_data, allow_class = "data.frame", .call_depth = -2)
|
||||
meet_criteria(substitute_missing_r_breakpoint, allow_class = "logical", has_length = 1, .call_depth = -2)
|
||||
meet_criteria(include_screening, allow_class = "logical", has_length = 1, .call_depth = -2)
|
||||
meet_criteria(include_screening, allow_class = "logical", has_length = 1, allow_NULL = TRUE, .call_depth = -2)
|
||||
meet_criteria(include_PKPD, allow_class = "logical", has_length = 1, .call_depth = -2)
|
||||
reference_data <- check_reference_data(reference_data, .call_depth = -2)
|
||||
meet_criteria(breakpoint_type, allow_class = "character", is_in = reference_data$type, has_length = 1, .call_depth = -2)
|
||||
@@ -1638,17 +1638,29 @@ as_sir_method <- function(method_short,
|
||||
method_coerced <- toupper(method)
|
||||
ab_coerced <- as.ab(ab, info = FALSE)
|
||||
|
||||
# which agents hold the breakpoints of the requested agents: the high-level aminoglycoside tests have two codes, and screening breakpoints are also for the regular agent if the user allows this
|
||||
ab_lookup <- function(ab_requested) {
|
||||
ab_requested <- as.character(ab_requested)
|
||||
ab_stored <- ab_requested
|
||||
ab_stored[ab_stored == "GEH"] <- "GEN-S"
|
||||
ab_stored[ab_stored == "STH"] <- "STR1-S"
|
||||
from <- c(ab_requested, ab_requested)
|
||||
to <- c(ab_stored, if (isTRUE(include_screening)) ifelse(endsWith(ab_stored, "-S"), ab_stored, paste0(ab_stored, "-S")) else ab_stored)
|
||||
unique(data.frame(from = from, to = to, stringsAsFactors = FALSE))
|
||||
}
|
||||
ab_coerced_lookup <- ab_lookup(ab_coerced)
|
||||
if (!custom_breakpoints_set) {
|
||||
breakpoints <- reference_data %pm>%
|
||||
subset(guideline %in% guideline_coerced & method == method_coerced & ab %in% ab_coerced)
|
||||
subset(guideline %in% guideline_coerced & method == method_coerced & ab %in% ab_coerced_lookup$to)
|
||||
if (any(ab_coerced == "AMX") && nrow(breakpoints[breakpoints$ab == "AMX", , drop = FALSE]) == 0) {
|
||||
ab_coerced[ab_coerced == "AMX"] <- "AMP"
|
||||
ab_coerced_lookup <- ab_lookup(ab_coerced)
|
||||
breakpoints <- reference_data %pm>%
|
||||
subset(guideline %in% guideline_coerced & method == method_coerced & ab %in% ab_coerced)
|
||||
subset(guideline %in% guideline_coerced & method == method_coerced & ab %in% ab_coerced_lookup$to)
|
||||
}
|
||||
} else {
|
||||
breakpoints <- reference_data %pm>%
|
||||
subset(method == method_coerced & ab %in% ab_coerced)
|
||||
subset(method == method_coerced & ab %in% ab_coerced_lookup$to)
|
||||
}
|
||||
|
||||
|
||||
@@ -1712,10 +1724,23 @@ as_sir_method <- function(method_short,
|
||||
breakpoints <- breakpoints %pm>%
|
||||
subset(type %in% breakpoint_type_lookup)
|
||||
|
||||
is_screening_row <- function(df) {
|
||||
df$site %like% "screen" | df$ref_tbl %like% "screen" | as.character(df$ab) %like% "-S$"
|
||||
}
|
||||
screening_left_out <- FALSE
|
||||
if (isFALSE(include_screening)) {
|
||||
# remove screening rules from the breakpoints table
|
||||
breakpoints <- breakpoints %pm>%
|
||||
subset(site %unlike% "screen" & ref_tbl %unlike% "screen")
|
||||
# remove screening rules from the breakpoints table, on explicit request also for a screening agent
|
||||
screening_left_out <- any(is_screening_row(breakpoints) & as.character(breakpoints$ab) %like% "-S$")
|
||||
breakpoints <- breakpoints[!is_screening_row(breakpoints), , drop = FALSE]
|
||||
} else if (is.null(include_screening)) {
|
||||
# at default, screening breakpoints are only for an agent that indicates screening itself
|
||||
breakpoints <- breakpoints[!is_screening_row(breakpoints) | as.character(breakpoints$ab) %like% "-S$", , drop = FALSE]
|
||||
}
|
||||
# the breakpoints are from here on known by the requested agent
|
||||
if (nrow(breakpoints) > 0) {
|
||||
for (j in rev(seq_len(nrow(ab_coerced_lookup)))) {
|
||||
breakpoints$ab[as.character(breakpoints$ab) == ab_coerced_lookup$to[j]] <- ab_coerced_lookup$from[j]
|
||||
}
|
||||
}
|
||||
if (isFALSE(include_PKPD)) {
|
||||
# remove PKPD rules from the breakpoints table
|
||||
@@ -1738,6 +1763,35 @@ as_sir_method <- function(method_short,
|
||||
has_progress_bar <- !is.null(import_fn("progress_bar", "progress", error_on_fail = FALSE)) && nrow(df_unique) >= 10
|
||||
on.exit(close(p))
|
||||
|
||||
if (isTRUE(screening_left_out) && nrow(breakpoints) == 0) {
|
||||
# a note, as this may be by accident
|
||||
message_("Returning NAs for ", vector_and(unique(as.character(ab_coerced)), quotes = TRUE), ": this is a screening agent, while `include_screening = FALSE` leaves out screening breakpoints.")
|
||||
out <- data.frame(
|
||||
datetime = rep(Sys.time(), nrow(df)),
|
||||
index = seq_len(nrow(df)),
|
||||
method = rep(method_coerced, nrow(df)),
|
||||
ab_given = ab.bak,
|
||||
mo_given = as.character(mo.bak),
|
||||
host_given = as.character(host),
|
||||
input_given = as.character(df$input_original),
|
||||
ab = as.character(df$ab),
|
||||
mo = as.character(df$mo),
|
||||
host = as.character(df$host),
|
||||
input = as.character(df$input_clean),
|
||||
outcome = rep(NA_sir_, nrow(df)),
|
||||
notes = rep("Screening breakpoints were left out, since `include_screening = FALSE`.", nrow(df)),
|
||||
guideline = df$guideline,
|
||||
ref_table = rep(NA_character_, nrow(df)),
|
||||
uti = df$uti,
|
||||
breakpoint_S_R = rep(NA_character_, nrow(df)),
|
||||
site = rep(NA_character_, nrow(df)),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
AMR_env$sir_interpretation_history <- rbind_AMR(AMR_env$sir_interpretation_history, out[!is.na(out$input_given), , drop = FALSE])
|
||||
load_mo_uncertainties(metadata_mo)
|
||||
return(rep(NA_sir_, nrow(df)))
|
||||
}
|
||||
|
||||
if (nrow(breakpoints) == 0) {
|
||||
# apparently no breakpoints found
|
||||
if (isTRUE(info)) {
|
||||
|
||||
@@ -931,6 +931,32 @@ antimicrobials <- antimicrobials |>
|
||||
)
|
||||
|
||||
|
||||
# add the screening agents of #328, for all explicit screening tests in clinical_breakpoints; agreed by the maintainer in that issue
|
||||
for (agent in c("CHL", "CIP", "COL", "ERY", "FUS", "GEN", "LVX", "MUP", "NEO", "OFX", "RTP", "STR1", "TOB")) {
|
||||
antimicrobials <- antimicrobials |>
|
||||
mutate(ab = as.character(ab)) |>
|
||||
bind_rows(
|
||||
antimicrobials |>
|
||||
mutate(ab = as.character(ab)) |>
|
||||
filter(ab == agent) |>
|
||||
mutate(
|
||||
abbreviations = list(paste(tolower(ab), "screen")),
|
||||
ab = paste0(ab, "-S"),
|
||||
name = paste(name, "screening test"),
|
||||
cid = NA,
|
||||
atc = list(character(0)),
|
||||
atc_group1 = NA_character_,
|
||||
atc_group2 = NA_character_,
|
||||
synonyms = list(character(0)),
|
||||
oral_ddd = NA_real_,
|
||||
oral_units = NA_character_,
|
||||
iv_ddd = NA_real_,
|
||||
iv_units = NA_character_,
|
||||
loinc = list(character(0))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
# add pretomanid
|
||||
antimicrobials <- antimicrobials %>%
|
||||
mutate(ab = as.character(ab)) %>%
|
||||
|
||||
@@ -729,6 +729,30 @@ breakpoints_new <- breakpoints_new |>
|
||||
arrange(desc(guideline), mo, ab, type, host, method)
|
||||
|
||||
clinical_breakpoints <- merge_eucast_breakpoints(breakpoints_new, breakpoints_eucast, eucast_coverage)
|
||||
|
||||
# Screening tests ----
|
||||
# Every explicit screening test is stored under its screening agent, so that it can never be taken for a breakpoint of the regular agent (#328). The EUCAST rows from its own tables already are, see reproduction_of_clinical_breakpoints_eucast.R. This takes care of the WHONET-based rows, as decided in #328:
|
||||
# - all rows with a site that states screening
|
||||
# - high-level aminoglycoside tests: GEH and STH to GEN-S and STR1-S (as.sir() keeps accepting GEH and STH)
|
||||
# - cefoxitin MIC for S. aureus and S. lugdunensis in EUCAST 2012-2018, which from 2019 on is labelled as screening
|
||||
# - CLSI surrogate tests: cefoxitin for staphylococci, and pefloxacin for Salmonella
|
||||
# EUCAST 2011-2013 rows without a label stay under the regular agent, also as decided in #328.
|
||||
screening_year <- as.integer(gsub("[^0-9]", "", clinical_breakpoints$guideline))
|
||||
screening_genus <- mo_genus(clinical_breakpoints$mo, keep_synonyms = TRUE, language = NULL)
|
||||
is_human <- clinical_breakpoints$type == "human"
|
||||
is_screening <- (!is.na(clinical_breakpoints$site) & grepl("screen", clinical_breakpoints$site, ignore.case = TRUE) & !grepl("-S$", as.character(clinical_breakpoints$ab))) |
|
||||
(is_human & clinical_breakpoints$guideline %like% "EUCAST" & screening_year %in% 2012:2018 & clinical_breakpoints$ab == "FOX" & clinical_breakpoints$method == "MIC" & as.character(clinical_breakpoints$mo) %in% c("B_STPHY_AURS", "B_STPHY_LGDN")) |
|
||||
(is_human & clinical_breakpoints$guideline %like% "CLSI" & clinical_breakpoints$ab == "FOX" & screening_genus %in% "Staphylococcus") |
|
||||
(is_human & clinical_breakpoints$guideline %like% "CLSI" & screening_year >= 2015 & clinical_breakpoints$ab == "PEF" & screening_genus %in% "Salmonella")
|
||||
message("NOTE: ", sum(is_screening), " WHONET-based row(s) moved to their screening agent")
|
||||
clinical_breakpoints$ab <- as.character(clinical_breakpoints$ab)
|
||||
clinical_breakpoints$ab[is_screening] <- paste0(clinical_breakpoints$ab[is_screening], "-S")
|
||||
clinical_breakpoints$ab[clinical_breakpoints$ab == "GEH"] <- "GEN-S"
|
||||
clinical_breakpoints$ab[clinical_breakpoints$ab == "STH"] <- "STR1-S"
|
||||
if (any(!clinical_breakpoints$ab %in% AMR::antimicrobials$ab)) {
|
||||
stop("agent(s) missing from the antimicrobials data set: ", toString(unique(clinical_breakpoints$ab[!clinical_breakpoints$ab %in% AMR::antimicrobials$ab])))
|
||||
}
|
||||
clinical_breakpoints$ab <- as.ab(clinical_breakpoints$ab)
|
||||
usethis::use_data(clinical_breakpoints, overwrite = TRUE, compress = "xz", version = 2)
|
||||
rm(clinical_breakpoints)
|
||||
devtools::load_all(".")
|
||||
@@ -2276,15 +2276,17 @@ breakpoints_eucast <- breakpoints_eucast |>
|
||||
ab_input = gsub("\\s*\\(.*$", "", ab),
|
||||
ab_input = trimws(sub("\\s+(iv|i\\.v\\.|oral)$", "", ab_input, ignore.case = TRUE)),
|
||||
ab = as.ab(ab_input),
|
||||
# use dedicated screening test codes when available (e.g. FOX -> FOX-S).
|
||||
ab = as.ab(if_else(
|
||||
!is.na(site) &
|
||||
site == "Screen" &
|
||||
paste0(as.character(ab), "-S") %in% as.character(antibiotics$ab),
|
||||
paste0(as.character(ab), "-S"),
|
||||
as.character(ab)
|
||||
)
|
||||
))
|
||||
# every screening test is stored under its screening agent (e.g. FOX-S), so that it can never be taken for a breakpoint of the regular agent (#328)
|
||||
is_screening_row = !is.na(site) & grepl("Screen", site, fixed = TRUE),
|
||||
ab = as.ab(if_else(is_screening_row, paste0(as.character(ab), "-S"), as.character(ab))),
|
||||
# the agent now holds the screening meaning; the topical cut-off values remain distinguishable from systemic screening tests by their site
|
||||
site = if_else(site %in% "Topical, Screen", "Topical", site)
|
||||
) |>
|
||||
select(-is_screening_row)
|
||||
if (any(!is.na(breakpoints_eucast$ab_input) & grepl("Screen", breakpoints_eucast$site, fixed = TRUE) & is.na(breakpoints_eucast$ab))) {
|
||||
print(distinct(filter(breakpoints_eucast, grepl("Screen", site, fixed = TRUE), is.na(ab)), ab_input))
|
||||
stop("screening agent(s) missing from the antimicrobials data set, see above")
|
||||
}
|
||||
|
||||
# ab_previously_coerced also logs every intermediate word-by-word fallback
|
||||
# as.ab() tries internally while resolving a route-suffixed name (e.g. for
|
||||
@@ -2475,13 +2477,13 @@ derived_rows <- list()
|
||||
hlar_rows <- breakpoints_eucast |>
|
||||
filter(
|
||||
grepl("Negative test: Isolates with (gentamicin|streptomycin) MIC \u2264", note),
|
||||
ab %in% c("GEN", "STR1"), site %in% "Screen"
|
||||
ab %in% c("GEN-S", "STR1-S"), site %in% "Screen"
|
||||
)
|
||||
for (r in seq_len(nrow(hlar_rows))) {
|
||||
row <- hlar_rows[r, ]
|
||||
# the note of a row can also hold the test for the other agent, so the
|
||||
# agent is taken from the row itself
|
||||
agent <- c(GEN = "gentamicin", STR1 = "streptomycin")[as.character(row$ab)]
|
||||
agent <- c("GEN-S" = "gentamicin", "STR1-S" = "streptomycin")[as.character(row$ab)]
|
||||
if (is.na(agent)) stop("unexpected agent for a high-level aminoglycoside screening test: ", row$ab)
|
||||
neg <- regmatches(row$note, regexec(paste0("Negative test: Isolates with ", agent, " MIC \u2264([0-9]+) mg/L(?: or a zone diameter \u2265([0-9]+) mm)?"), row$note, perl = TRUE))[[1]]
|
||||
pos <- regmatches(row$note, regexec(paste0("Positive test: Isolates with ", agent, " MIC >([0-9]+) mg/L(?: or a zone diameter <([0-9]+) mm)?"), row$note, perl = TRUE))[[1]]
|
||||
@@ -2492,11 +2494,26 @@ for (r in seq_len(nrow(hlar_rows))) {
|
||||
if (!nzchar(value)) next # e.g. no zone diameter given in this version
|
||||
derived_rows[[length(derived_rows) + 1]] <- row |>
|
||||
mutate(
|
||||
ab = as.ab(if (agent == "gentamicin") "GEH" else "STH"), site = NA_character_, is_blocker = FALSE,
|
||||
site = "Screen", is_blocker = FALSE,
|
||||
breakpoint_S = value, breakpoint_R = value,
|
||||
note = sub("^\\[No breakpoint\\][^|]*(\\| )?", "", note)
|
||||
)
|
||||
}
|
||||
# EUCAST states that some clinical breakpoints can also be used as a screening test, e.g. "Erythromycin can be used to screen for macrolide resistance in staphylococci". These stay under the regular agent, with a copy under the screening agent (#328).
|
||||
also_screening <- breakpoints_eucast |>
|
||||
filter(
|
||||
!is_blocker, !grepl("-S$", as.character(ab)), !site %in% c("Screen", "Topical"),
|
||||
(ab == "ERY" & grepl("Erythromycin can be used to screen", note, fixed = TRUE)) |
|
||||
(ab == "TCY" & grepl("Tetracycline can be used to screen", note, fixed = TRUE)) |
|
||||
(ab == "PEN" & grepl("Benzylpenicillin( 1 unit| \\(MIC or disk diffusion\\))? can be used to screen", note)) |
|
||||
(ab == "NAL" & grepl("nalidixic acid disk diffusion test can be used to screen", note, fixed = TRUE)) |
|
||||
(ab == "NOR" & grepl("norfloxacin disk diffusion test can be used to screen", note, fixed = TRUE))
|
||||
) |>
|
||||
mutate(ab = as.ab(paste0(as.character(ab), "-S")), site = "Screen")
|
||||
message("NOTE: ", nrow(also_screening), " clinical breakpoint row(s) copied to their screening agent, as EUCAST also allows them as a screening test")
|
||||
breakpoints_eucast <- breakpoints_eucast |>
|
||||
bind_rows(also_screening)
|
||||
|
||||
fox_mic <- breakpoints_eucast |>
|
||||
filter(
|
||||
ab == "FOX-S", method == "MIC",
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -26,7 +26,7 @@ This is an overview of all the package-specific options you can set in the \code
|
||||
}
|
||||
\item \code{AMR_ignore_pattern} \cr A \link[base:regex]{regular expression} to ignore (i.e., make \code{NA}) any match given in \code{\link[=as.mo]{as.mo()}} and all \code{\link[=mo_property]{mo_*}} functions.
|
||||
\item \code{AMR_include_PKPD} \cr A \link{logical} to use in \code{\link[=as.sir]{as.sir()}}, to indicate that PK/PD clinical breakpoints must be applied as a last resort - the default is \code{TRUE}.
|
||||
\item \code{AMR_include_screening} \cr A \link{logical} to use in \code{\link[=as.sir]{as.sir()}}, to indicate that clinical breakpoints for screening are allowed - the default is \code{FALSE}.
|
||||
\item \code{AMR_include_screening} \cr A \link{logical} to use in \code{\link[=as.sir]{as.sir()}}, to indicate that clinical breakpoints for screening are allowed - the default is \code{NULL}, using these for screening agents only, such as \code{FOX-S}. See \code{\link[=as.sir]{as.sir()}}.
|
||||
\item \code{AMR_interpretive_rules} \cr A \link{character} to set the default types of rules for \code{\link[=interpretive_rules]{interpretive_rules()}}, \code{\link[=eucast_rules]{eucast_rules()}} and \code{\link[=clsi_rules]{clsi_rules()}}, must be one or more of: \code{"breakpoints"}, \code{"expected_phenotypes"}, \code{"expert"}, \code{"other"}, \code{"custom"}, \code{"all"}, and defaults to \code{c("breakpoints", "expected_phenotypes")}. This option replaces the old option \code{AMR_eucastrules}.
|
||||
\item \code{AMR_keep_synonyms} \cr A \link{logical} to use in \code{\link[=as.mo]{as.mo()}} and all \code{\link[=mo_property]{mo_*}} functions, to indicate if old, previously valid taxonomic names must be preserved and not be corrected to currently accepted names. The default is \code{FALSE}.
|
||||
\item \code{AMR_locale} \cr A \link{character} to set the language for the \code{AMR} package, can be one of these supported language names or \href{https://en.wikipedia.org/wiki/ISO_639-1}{ISO 639-1 codes}: English (en), Arabic (ar), Bengali (bn), Chinese (zh), Czech (cs), Danish (da), Dutch (nl), Finnish (fi), French (fr), German (de), Greek (el), Hindi (hi), Indonesian (id), Italian (it), Japanese (ja), Korean (ko), Norwegian (no), Polish (pl), Portuguese (pt), Romanian (ro), Russian (ru), Spanish (es), Swahili (sw), Swedish (sv), Turkish (tr), Ukrainian (uk), Urdu (ur), or Vietnamese (vi). The default is the current system language (if supported, English otherwise).
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ The \code{AMR} package is a peer-reviewed, \href{https://amr-for-r.org/#copyrigh
|
||||
|
||||
This work was published in the Journal of Statistical Software (Volume 104(3); \doi{10.18637/jss.v104.i03}) and formed the basis of two PhD theses (\doi{10.33612/diss.177417131} and \doi{10.33612/diss.192486375}).
|
||||
|
||||
After installing this package, R knows \href{https://amr-for-r.org/reference/microorganisms.html}{\strong{~103 000 distinct microbial species}} (updated April 2026) and all \href{https://amr-for-r.org/reference/antimicrobials.html}{\strong{~630 antimicrobial and antiviral drugs}} by name and code (including ATC, EARS-Net, ASIARS-Net, PubChem, LOINC and SNOMED CT), and knows all about valid SIR and MIC values. The integral clinical breakpoint guidelines from CLSI 2011-2026 and EUCAST 2011-2026 are included, even with epidemiological cut-off (ECOFF) values. It supports and can read any data format, including WHONET data. This package works on Windows, macOS and Linux with all versions of R since R-3.0 (April 2013). \strong{It was designed to work in any setting, including those with very limited resources}. It was created for both routine data analysis and academic research at the Faculty of Medical Sciences of the \href{https://www.rug.nl}{University of Groningen} and the \href{https://www.umcg.nl}{University Medical Center Groningen}.
|
||||
After installing this package, R knows \href{https://amr-for-r.org/reference/microorganisms.html}{\strong{~103 000 distinct microbial species}} (updated April 2026) and all \href{https://amr-for-r.org/reference/antimicrobials.html}{\strong{~640 antimicrobial and antiviral drugs}} by name and code (including ATC, EARS-Net, ASIARS-Net, PubChem, LOINC and SNOMED CT), and knows all about valid SIR and MIC values. The integral clinical breakpoint guidelines from CLSI 2011-2026 and EUCAST 2011-2026 are included, even with epidemiological cut-off (ECOFF) values. It supports and can read any data format, including WHONET data. This package works on Windows, macOS and Linux with all versions of R since R-3.0 (April 2013). \strong{It was designed to work in any setting, including those with very limited resources}. It was created for both routine data analysis and academic research at the Faculty of Medical Sciences of the \href{https://www.rug.nl}{University of Groningen} and the \href{https://www.umcg.nl}{University Medical Center Groningen}.
|
||||
|
||||
The \code{AMR} package is available in English, Arabic, Bengali, Chinese, Czech, Danish, Dutch, Finnish, French, German, Greek, Hindi, Indonesian, Italian, Japanese, Korean, Norwegian, Polish, Portuguese, Romanian, Russian, Spanish, Swahili, Swedish, Turkish, Ukrainian, Urdu, and Vietnamese. Antimicrobial drug (group) names and colloquial microorganism names are provided in these languages.
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
\alias{antimicrobials}
|
||||
\alias{antibiotics}
|
||||
\alias{antivirals}
|
||||
\title{Data Sets with 626 Antimicrobial Drugs}
|
||||
\title{Data Sets with 639 Antimicrobial Drugs}
|
||||
\format{
|
||||
\subsection{For the \link{antimicrobials} data set: a \link[tibble:tibble]{tibble} with 506 observations and 14 variables:}{
|
||||
\subsection{For the \link{antimicrobials} data set: a \link[tibble:tibble]{tibble} with 519 observations and 14 variables:}{
|
||||
\itemize{
|
||||
\item \code{ab}\cr antimicrobial ID as used in this package (such as \code{AMC}), using the official EARS-Net (European Antimicrobial Resistance Surveillance Network) codes where available. \emph{\strong{This is a unique identifier.}}
|
||||
\item \code{cid}\cr Compound ID as found in PubChem. \emph{\strong{This is a unique identifier.}}
|
||||
|
||||
+4
-4
@@ -47,7 +47,7 @@ is_sir_eligible(x, threshold = 0.05)
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint",
|
||||
FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -70,7 +70,7 @@ is_sir_eligible(x, threshold = 0.05)
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint",
|
||||
FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -93,7 +93,7 @@ is_sir_eligible(x, threshold = 0.05)
|
||||
reference_data = AMR::clinical_breakpoints,
|
||||
substitute_missing_r_breakpoint = getOption("AMR_substitute_missing_r_breakpoint",
|
||||
FALSE),
|
||||
include_screening = getOption("AMR_include_screening", FALSE),
|
||||
include_screening = getOption("AMR_include_screening", NULL),
|
||||
include_PKPD = getOption("AMR_include_PKPD", TRUE),
|
||||
breakpoint_type = getOption("AMR_breakpoint_type", "human"),
|
||||
host = NULL,
|
||||
@@ -168,7 +168,7 @@ The default \code{"standard"} setting ensures cautious handling of uncertain val
|
||||
|
||||
\item{substitute_missing_r_breakpoint}{A \link{logical} to indicate that a missing clinical breakpoints for R (resistant) must be substituted with R - the default is \code{FALSE}. Some (especially CLSI) breakpoints only have a breakpoint for S, meaning that the outcome can only be \code{"S"} or \code{NA}. Setting this to \code{TRUE} will convert the \code{NA}s in these cases to \code{"R"}. Can also be set with the package option \code{\link[=AMR-options]{AMR_substitute_missing_r_breakpoint}}.}
|
||||
|
||||
\item{include_screening}{A \link{logical} to indicate that clinical breakpoints for screening are allowed - the default is \code{FALSE}. Can also be set with the package option \code{\link[=AMR-options]{AMR_include_screening}}.}
|
||||
\item{include_screening}{A \link{logical} to indicate that clinical breakpoints for screening are allowed. At default (\code{NULL}, can be set with the package option \code{\link[=AMR-options]{AMR_include_screening}}), these are only used for screening agents, e.g. \code{FOX-S}, but not for regular agents, e.g. \code{FOX}. Use \code{TRUE} to also use them for regular agents (with \code{FOX} then taking the breakpoints of \code{FOX-S}), and \code{FALSE} to never use them, which gives \code{NA} with a note for a screening agent.}
|
||||
|
||||
\item{include_PKPD}{A \link{logical} to indicate that PK/PD clinical breakpoints must be applied as a last resort - the default is \code{TRUE}. Can also be set with the package option \code{\link[=AMR-options]{AMR_include_PKPD}}.}
|
||||
|
||||
|
||||
@@ -582,6 +582,40 @@ test_that("microorganisms.codes: unique codes for existing names", {
|
||||
expect_true(all(is.na(microorganisms.codes$source) | grepl("^WHONET \\(.+\\)$", microorganisms.codes$source)))
|
||||
})
|
||||
|
||||
test_that("clinical_breakpoints: screening tests are under screening agents (#328)", {
|
||||
skip_on_cran()
|
||||
cb <- as.data.frame(clinical_breakpoints, stringsAsFactors = FALSE)
|
||||
ab <- as.character(cb$ab)
|
||||
year <- as.integer(gsub("[^0-9]", "", cb$guideline))
|
||||
is_regular <- !grepl("-S$", ab)
|
||||
# no regular agent has a screening site
|
||||
expect_false(any(is_regular & grepl("screen", cb$site, ignore.case = TRUE)))
|
||||
expect_true(all(c("FOX-S", "OXA-S", "NAL-S", "NOR-S", "PEF-S", "PEN-S", "TCY-S", "ERY-S", "GEN-S", "STR1-S", "CIP-S", "MUP-S") %in% ab))
|
||||
# all codes of screening agents are valid, without an ATC code
|
||||
screening_agents <- subset(AMR::antimicrobials, grepl("-S$", ab))
|
||||
expect_true(all(unique(ab[!is_regular]) %in% screening_agents$ab))
|
||||
expect_true(all(grepl("screening test$", screening_agents$name)))
|
||||
# the two high-level codes hold no rows themselves, and are still valid
|
||||
expect_false(any(ab %in% c("GEH", "STH")))
|
||||
expect_true(all(c("GEH", "STH") %in% AMR::antimicrobials$ab))
|
||||
# topical cut-off values
|
||||
topical <- cb[cb$site %in% "Topical", , drop = FALSE]
|
||||
expect_true(all(grepl("-S$", as.character(topical$ab))))
|
||||
expect_false(any(cb$site %in% "Topical, Screen"))
|
||||
# explicit lists: moved
|
||||
human <- cb$type == "human"
|
||||
expect_false(any(human & cb$guideline %like% "EUCAST" & year %in% 2012:2018 & ab == "FOX" & cb$method == "MIC" & as.character(cb$mo) %in% c("B_STPHY_AURS", "B_STPHY_LGDN")))
|
||||
expect_false(any(human & cb$guideline %like% "CLSI" & ab == "FOX" & mo_genus(cb$mo, keep_synonyms = TRUE, language = NULL) %in% "Staphylococcus"))
|
||||
expect_false(any(human & cb$guideline %like% "CLSI" & year >= 2015 & ab == "PEF" & mo_genus(cb$mo, keep_synonyms = TRUE, language = NULL) %in% "Salmonella"))
|
||||
# explicit list: stays under the regular agent, also as decided in the issue
|
||||
expect_true(any(human & cb$guideline == "EUCAST 2013" & ab == "NAL" & cb$method == "DISK"))
|
||||
expect_true(any(human & cb$guideline == "CLSI 2026" & ab == "OXA" & as.character(cb$mo) == "B_STRPT_PNMN" & cb$method == "DISK"))
|
||||
# rows that are a clinical breakpoint and a screen are under both (from EUCAST 2023)
|
||||
ery <- function(agent) cb[human & cb$guideline == "EUCAST 2026" & ab == agent & as.character(cb$mo) == "B_MRXLL_CTRR" & cb$method == "DISK", c("breakpoint_S", "breakpoint_R"), drop = FALSE]
|
||||
expect_true(nrow(ery("ERY")) > 0)
|
||||
expect_identical(unname(as.list(ery("ERY-S"))), unname(as.list(ery("ERY"))))
|
||||
})
|
||||
|
||||
test_that("clinical_breakpoints: no EUCAST breakpoints in brackets (#327)", {
|
||||
skip_on_cran()
|
||||
expect_false(any(grepl("brackets", clinical_breakpoints$note, fixed = TRUE)))
|
||||
|
||||
@@ -433,7 +433,7 @@ test_that("test-sir.R", {
|
||||
)
|
||||
# an S category remains unaffected, e.g. EUCAST CIP in E. coli (S <= 0.25, R > 0.5)
|
||||
expect_equal(
|
||||
suppressMessages(as.sir(as.mic(c("<=0.5", "<0.5", "<=0.25")), mo = "Escherichia coli", ab = "CIP", guideline = "EUCAST 2026", capped_mic_handling = "standard")),
|
||||
suppressMessages(as.sir(as.mic(c("<=0.5", "<0.5", "<=0.25")), mo = "Escherichia coli", ab = "CIP", guideline = "EUCAST 2026", capped_mic_handling = "standard", site = "non-meningitis")),
|
||||
as.sir(c("NI", "S", "S"))
|
||||
)
|
||||
# capped values must not be forced to S or R if there is no breakpoint (blocking row, here: EUCAST CIP in S. aureus in brackets only)
|
||||
@@ -638,6 +638,26 @@ test_that("custom reference_data: host = NA acts as host-agnostic fallback", {
|
||||
expect_equal(as.character(result), "R")
|
||||
})
|
||||
|
||||
test_that("as.sir() uses screening breakpoints as specified (#328)", {
|
||||
skip_on_cran()
|
||||
sir <- function(ab, ...) {
|
||||
as.character(suppressMessages(suppressWarnings(as.sir(as.disk(20), mo = "S. aureus", ab = ab, guideline = "EUCAST 2026", info = FALSE, ...))))
|
||||
}
|
||||
expect_identical(sir("FOX"), NA_character_)
|
||||
expect_false(is.na(sir("FOX-S")))
|
||||
expect_false(is.na(sir("FOX-S", include_screening = TRUE)))
|
||||
expect_false(is.na(sir("FOX", include_screening = TRUE)))
|
||||
expect_identical(sir("FOX", include_screening = TRUE), sir("FOX-S"))
|
||||
expect_identical(sir("FOX", include_screening = FALSE), NA_character_)
|
||||
expect_identical(sir("FOX-S", include_screening = FALSE), NA_character_)
|
||||
# with a note, as this may be by accident
|
||||
expect_message(as.sir(as.disk(20), mo = "S. aureus", ab = "FOX-S", guideline = "EUCAST 2026", include_screening = FALSE, info = FALSE), "include_screening")
|
||||
sir_interpretation_history(clean = TRUE)
|
||||
sir("FOX-S", include_screening = FALSE)
|
||||
sir_history <- sir_interpretation_history(clean = TRUE)
|
||||
expect_match(sir_history$notes, "include_screening = FALSE", fixed = TRUE)
|
||||
})
|
||||
|
||||
test_that("as.sir() takes the site of a breakpoint into account", {
|
||||
skip_on_cran()
|
||||
sir <- function(x, mo, ab, ..., guideline = "EUCAST 2026") {
|
||||
@@ -788,7 +808,7 @@ test_that("Morganella is interpreted as reported, not as M. morganii (#330)", {
|
||||
|
||||
test_that("EUCAST blocking rows prevent falling back to broader breakpoints", {
|
||||
skip_on_cran()
|
||||
sir <- function(x, mo, ab, guideline, include_screening = FALSE, uti = NA) {
|
||||
sir <- function(x, mo, ab, guideline, include_screening = NULL, uti = NA) {
|
||||
as.character(suppressMessages(suppressWarnings(
|
||||
as.sir(x, mo = mo, ab = ab, guideline = guideline, include_screening = include_screening, uti = uti)
|
||||
)))
|
||||
@@ -813,7 +833,7 @@ test_that("EUCAST blocking rows prevent falling back to broader breakpoints", {
|
||||
# H. influenzae MIC breakpoints apply to H. parainfluenzae, zone diameters do not
|
||||
expect_identical(sir(as.mic(0.5), "Haemophilus parainfluenzae", "CIP", "EUCAST 2026"), "R")
|
||||
expect_identical(sir(as.disk(30), "Haemophilus parainfluenzae", "CIP", "EUCAST 2026"), NA_character_)
|
||||
# cefoxitin screen: CoNS not identified to species level use S >= 25 mm
|
||||
# cefoxitin screen: CoNS not identified to species level use S >= 25 mm, also with the regular agent if allowed (#328)
|
||||
expect_identical(sir(as.disk(23), "B_STPHY_CONS", "FOX", "EUCAST 2026", include_screening = TRUE), "R")
|
||||
expect_identical(sir(as.disk(23), "Staphylococcus hominis", "FOX", "EUCAST 2026", include_screening = TRUE), "S")
|
||||
expect_identical(sir(as.disk(26), "Staphylococcus epidermidis", "FOX", "EUCAST 2026", include_screening = TRUE), "R")
|
||||
@@ -824,8 +844,9 @@ test_that("EUCAST blocking rows prevent falling back to broader breakpoints", {
|
||||
expect_identical(sir(as.mic(0.0005), "Pseudomonas aeruginosa", "CIP", "EUCAST 2026"), "I")
|
||||
# EUCAST's 0.06 mg/L is the dilution 0.0625, i.e. 0.064 in this package
|
||||
expect_identical(sir(as.mic(0.064), "Haemophilus influenzae", "LVX", "EUCAST 2026"), "S")
|
||||
# high-level gentamicin resistance (screening test given in the notes)
|
||||
# high-level gentamicin resistance (screening test given in the notes), under both codes (#328)
|
||||
expect_identical(sir(as.mic(256), "Enterococcus faecium", "GEH", "EUCAST 2026"), "R")
|
||||
expect_identical(sir(as.mic(256), "Enterococcus faecium", "GEN-S", "EUCAST 2026"), "R")
|
||||
# breakpoints in brackets are not in the data set (#327), so a UTI breakpoint is used if uti is unknown and nothing else is available, which is noted every time
|
||||
sir_interpretation_history(clean = TRUE)
|
||||
expect_identical(sir(as.mic(4), "Acinetobacter baumannii", "AMK", "EUCAST 2026"), "S")
|
||||
|
||||
Reference in new issue
Block a user