Merge pull request #344 from msberends/salmonella-serovars

Return the serovar in mo_shortname() for Salmonella, and apply the Typhi rule in mdro()
This commit is contained in:
Matthijs Berends authored and GitHub committed 2026-11-04 08:07:17 +01:00
commit db7377f517
7 files changed
+60 -9

No files matched your search

+2 -2
View File
@@ -1,6 +1,6 @@
Package: AMR
Version: 3.1.0.9014
Date: 2026-10-30
Version: 3.1.0.9015
Date: 2026-11-04
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
+6 -1
View File
@@ -1,7 +1,12 @@
# AMR 3.1.0.9014
# AMR 3.1.0.9015
This version fixes a critical bug in WISCA that biased all coverage estimates of `wisca()` and `antibiogram(..., wisca = TRUE)` downwards since AMR 3.0.0. Users are strongly advised to re-run their WISCA analyses.
### Breaking
* `mo_shortname()` now returns the serovar for *Salmonella* serovars, e.g., `"S. Typhi"` and `"S. Goldcoast"` instead of `"S. enterica"`. As this is the default for `bug_drug_combinations()` and short names in `antibiogram()`, serovars are no longer merged in their results. Use `FUN = function(x) paste(mo_genus(x), mo_species(x))` in `bug_drug_combinations()` to group them by species (#341)
* `mo_shortname()` now returns e.g. `"S. Group B"` for *Salmonella* groups instead of the abbreviations of streptococci, such as `"GBS"`, so that they are no longer merged with streptococci in `bug_drug_combinations()` and `antibiogram()`. *Streptococcus* groups E, I, J and L are now also abbreviated, e.g., `"GES"`, and `"B_STRPT_ABCG"` now returns `"S. Group A, B, C, G"` instead of the malformed `"GA, B, C, GS"` (#341)
* `mdro()` now applies the rule for *Salmonella* Typhi in the EUCAST guidelines, i.e., resistance to carbapenems (and fluoroquinolones in `"EUCAST3.1"`). This rule never applied before, so these isolates were wrongly classified as negative (#341)
### Changed
* WISCA (`wisca()` and `antibiogram(..., wisca = TRUE)`)
* Fixed a critical bug that systematically underestimated the coverage of every WISCA since AMR 3.0.0. Pathogens absent from a syndromic group, and pathogens without any test result for a regimen, were still included in the model with only prior information: each added a prior weight of 1 to the pathogen incidence, and untested pathogens kept their full incidence weight while counting as about 50% susceptible. The bias was largest for stratified analyses (e.g., up to 14 percentage points for meropenem in random strata of `example_isolates`). Now, only pathogens that occur in a group and have at least one test result for the regimen are included. Please re-run all WISCA analyses (#337)
+3 -3
View File
@@ -1156,7 +1156,7 @@ mdro <- function(x = NULL,
)
trans_tbl(
3,
which(x$genus == "Salmonella" & x$species == "Typhi"),
which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems, fluoroquinolones),
"any"
)
@@ -1251,7 +1251,7 @@ mdro <- function(x = NULL,
)
trans_tbl(
3,
which(x$genus == "Salmonella" & x$species == "Typhi"),
which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems),
"any"
)
@@ -1353,7 +1353,7 @@ mdro <- function(x = NULL,
)
trans_tbl(
3,
which(x$genus == "Salmonella" & x$species == "Typhi"),
which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems),
"any"
)
+8 -2
View File
@@ -44,7 +44,7 @@
#'
#' [mo_ref()] returns the abbreviated authority of the nomenclatural act that created the queried name combination. When `keep_synonyms = FALSE` (default), this is the authority of the currently accepted name. When `keep_synonyms = TRUE`, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned.
#'
#' The short name ([mo_shortname()]) returns the first character of the genus and the full species, such as `"E. coli"`, for species and subspecies. Exceptions are abbreviations of staphylococci (such as *"CoNS"*, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as *"GBS"*, Group B Streptococci). Please bear in mind that e.g. *E. coli* could mean *Escherichia coli* (domain of Bacteria) as well as *Entamoeba coli* (domain of Protozoa). Returning to the full name will be done using [as.mo()] internally, giving priority to bacteria and human pathogens, i.e. `"E. coli"` will always be considered *Escherichia coli*. As a result, `mo_fullname(mo_shortname("Entamoeba coli"))` returns `"Escherichia coli"`.
#' The short name ([mo_shortname()]) returns the first character of the genus and the full species, such as `"E. coli"`, for species and subspecies. Exceptions are *Salmonella* serovars, which return the serovar (such as `"S. Typhi"`), and abbreviations of staphylococci (such as *"CoNS"*, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as *"GBS"*, Group B Streptococci). Please bear in mind that e.g. *E. coli* could mean *Escherichia coli* (domain of Bacteria) as well as *Entamoeba coli* (domain of Protozoa). Returning to the full name will be done using [as.mo()] internally, giving priority to bacteria and human pathogens, i.e. `"E. coli"` will always be considered *Escherichia coli*. As a result, `mo_fullname(mo_shortname("Entamoeba coli"))` returns `"Escherichia coli"`.
#'
#' Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), [mo_kingdom()] and [mo_domain()] return different results for bacteria and archaea: [mo_kingdom()] returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while [mo_domain()] returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results.
#'
@@ -262,13 +262,19 @@ mo_shortname <- function(x, language = get_AMR_locale(), keep_synonyms = getOpti
genera <- mo_genus(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)
shortnames <- paste0(substr(genera, 1, 1), ". ", replace_empty(mo_species(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)))
# exceptions for Salmonella serovars, that are stored as subspecies of S. enterica: return e.g. "S. Typhi"
subspecies <- mo_subspecies(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)
serovars <- which(genera == "Salmonella" & subspecies %like_case% "^[A-Z]")
shortnames[serovars] <- paste0("S. ", subspecies[serovars])
# exceptions for where no species is known
shortnames[shortnames %like% ".[.] spp[.]"] <- genera[shortnames %like% ".[.] spp[.]"]
# exceptions for staphylococci
shortnames[shortnames == "S. coagulase-negative"] <- "CoNS"
shortnames[shortnames == "S. coagulase-positive"] <- "CoPS"
# exceptions for streptococci: Group A Streptococcus -> GAS
shortnames[shortnames %like_case% "S. Group [ABCDFGHK]"] <- paste0("G", gsub("S. Group ([ABCDFGHK])", "\\1", shortnames[shortnames %like_case% "S. Group [ABCDFGHK]"], perl = TRUE), "S")
streptococci_groups <- which(genera == "Streptococcus" & shortnames %like_case% "^S[.] Group [A-L]$")
shortnames[streptococci_groups] <- paste0("G", gsub("S. Group ([A-L])", "\\1", shortnames[streptococci_groups], perl = TRUE), "S")
# unknown species etc.
shortnames[shortnames %like% "unknown"] <- paste0("(", trimws2(gsub("[^a-zA-Z -]", "", shortnames[shortnames %like% "unknown"], perl = TRUE)), ")")
+1 -1
View File
@@ -349,7 +349,7 @@ All functions will, at default, \strong{not} keep old taxonomic properties, as s
\code{\link[=mo_ref]{mo_ref()}} returns the abbreviated authority of the nomenclatural act that created the queried name combination. When \code{keep_synonyms = FALSE} (default), this is the authority of the currently accepted name. When \code{keep_synonyms = TRUE}, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned.
The short name (\code{\link[=mo_shortname]{mo_shortname()}}) returns the first character of the genus and the full species, such as \code{"E. coli"}, for species and subspecies. Exceptions are abbreviations of staphylococci (such as \emph{"CoNS"}, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as \emph{"GBS"}, Group B Streptococci). Please bear in mind that e.g. \emph{E. coli} could mean \emph{Escherichia coli} (domain of Bacteria) as well as \emph{Entamoeba coli} (domain of Protozoa). Returning to the full name will be done using \code{\link[=as.mo]{as.mo()}} internally, giving priority to bacteria and human pathogens, i.e. \code{"E. coli"} will always be considered \emph{Escherichia coli}. As a result, \code{mo_fullname(mo_shortname("Entamoeba coli"))} returns \code{"Escherichia coli"}.
The short name (\code{\link[=mo_shortname]{mo_shortname()}}) returns the first character of the genus and the full species, such as \code{"E. coli"}, for species and subspecies. Exceptions are \emph{Salmonella} serovars, which return the serovar (such as \code{"S. Typhi"}), and abbreviations of staphylococci (such as \emph{"CoNS"}, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as \emph{"GBS"}, Group B Streptococci). Please bear in mind that e.g. \emph{E. coli} could mean \emph{Escherichia coli} (domain of Bacteria) as well as \emph{Entamoeba coli} (domain of Protozoa). Returning to the full name will be done using \code{\link[=as.mo]{as.mo()}} internally, giving priority to bacteria and human pathogens, i.e. \code{"E. coli"} will always be considered \emph{Escherichia coli}. As a result, \code{mo_fullname(mo_shortname("Entamoeba coli"))} returns \code{"Escherichia coli"}.
Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), \code{\link[=mo_kingdom]{mo_kingdom()}} and \code{\link[=mo_domain]{mo_domain()}} return different results for bacteria and archaea: \code{\link[=mo_kingdom]{mo_kingdom()}} returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while \code{\link[=mo_domain]{mo_domain()}} returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results.
+21
View File
@@ -56,6 +56,27 @@ test_that("test-mdro.R", {
mdro(example_isolates, guideline = "BRMO", info = FALSE)
)
# test EUCAST rule for Salmonella Typhi, which is stored as subspecies of S. enterica
typhi <- data.frame(
mo = as.mo(rep("Salmonella Typhi", 2)),
CIP = as.sir(c("R", "S")),
IPM = as.sir(c("S", "S")),
MEM = as.sir(c("S", "S")),
AMX = as.sir(c("S", "S"))
)
expect_equal(
as.character(suppressWarnings(mdro(typhi, guideline = "EUCAST3.1", col_mo = "mo", info = FALSE))),
c("Positive", "Negative")
)
typhi$CIP <- as.sir(c("S", "S"))
typhi$MEM <- as.sir(c("R", "S"))
for (gl in c("EUCAST3.2", "EUCAST3.3")) {
expect_equal(
as.character(suppressWarnings(mdro(typhi, guideline = gl, col_mo = "mo", info = FALSE))),
c("Positive", "Negative")
)
}
# test Dutch P. aeruginosa MDRO
expect_equal(
as.character(mdro(
+19
View File
@@ -45,6 +45,25 @@ test_that("test-mo_property.R", {
expect_equal(mo_shortname("Staphylococcus aureus", Becker = "all", language = "en"), "CoPS")
expect_equal(mo_shortname("Streptococcus agalactiae"), "S. agalactiae")
expect_equal(mo_shortname("Streptococcus agalactiae", Lancefield = TRUE), "GBS")
# Salmonella serovars return the serovar, and all stay distinct
expect_equal(
mo_shortname(c("Salmonella Typhi", "Salmonella Goldcoast", "Salmonella Paratyphi A", "Salmonella enterica", "Salmonella enterica enterica", NA), language = NULL),
c("S. Typhi", "S. Goldcoast", "S. Paratyphi A", "S. enterica", "S. enterica", NA)
)
# Salmonella groups must not be abbreviated as streptococci
expect_equal(
mo_shortname(c("Salmonella group B", "Streptococcus group B"), language = NULL),
c("S. Group B", "GBS")
)
# all Lancefield groups are abbreviated, also when they share a letter with a Salmonella group
expect_equal(
mo_shortname(paste0("B_STRPT_GRP", LETTERS[1:12]), language = NULL),
paste0("G", LETTERS[1:12], "S")
)
expect_equal(mo_shortname("B_SLMNL_GRPE", language = NULL), "S. Group E")
expect_equal(mo_shortname("B_STRPT_ABCG", language = NULL), "S. Group A, B, C, G")
serovars <- microorganisms$mo[microorganisms$genus == "Salmonella" & microorganisms$subspecies %like_case% "^[A-Z]" & microorganisms$status == "accepted"]
expect_false(anyDuplicated(mo_shortname(serovars, language = NULL)) > 0)
# check gram stain determination, to prevent we lag after a taxonomic renaming
current_grampos_phyla <- c(