From eb6d07e1fe019695f801a403929f6e519e7e9e77 Mon Sep 17 00:00:00 2001 From: Matthijs Berends Date: Thu, 15 Oct 2026 10:21:44 +0200 Subject: [PATCH] Include isolates of unknown species in grouped antibiograms and in wisca(), which were silently excluded with the warning 'invalid microorganism code, NA generated' --- NEWS.md | 1 + R/antibiogram.R | 8 +++++++- index.md | 10 ++++------ tests/testthat/test-antibiogram.R | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3ce36b0f6..e201f28ab 100644 --- a/NEWS.md +++ b/NEWS.md @@ -110,6 +110,7 @@ Planned as v3.1.0, mid October 2026. * Antibiograms (`antibiogram()` and `wisca()`) * `wisca()` now follows Bielicki *et al.* (2016) more strictly by using $\text{Beta}(1, 9999)$ for intrinsically resistant pairs, forcing near-zero susceptibility regardless of observed data (based on EUCAST Expected Resistant Phenotypes) * Fixed a bug where both functions failed without the `antimicrobials` argument if `tidyselect` was not available, e.g., for binary installations without `dplyr` + * Isolates of unknown species are now included in grouped antibiograms and in `wisca()`, as they already were in traditional antibiograms. They were previously excluded silently, with the warning "invalid microorganism code, NA generated". This can change WISCA coverage estimates. * Resistance and susceptibility calculation * New argument `guideline` (default: the `AMR_guideline` option, otherwise EUCAST) in `susceptibility()`, `resistance()`, `count_df()`, `proportion_df()`, `sir_df()`, `ggplot_sir()`, `geom_sir()`, `labels_sir_count()`, `antibiogram()`, `wisca()`, and `bug_drug_combinations()` to interpret the 'I' category correctly per guideline. Functions that return results per category also accept `guideline = NULL` to keep S, I, and R separate, while functions that return a single proportion or count give an informative error. * When EUCAST is assumed because neither the `guideline` argument nor the `AMR_guideline` option is set, a message explains how to change this, once per session and not in R Markdown or Quarto documents diff --git a/R/antibiogram.R b/R/antibiogram.R index 25a0e183c..e346c5b2d 100755 --- a/R/antibiogram.R +++ b/R/antibiogram.R @@ -578,7 +578,13 @@ antibiogram.default <- function(x, x$`.mo` <- mo_property(x$`.mo`, property = mo_transform, language = language) } } - x$`.mo`[x$`.mo` %in% c(NA, "UNKNOWN")] <- "(??)" + # keep unknown microorganisms, so that they are also included in grouped antibiograms and WISCA + if (is.mo(x$`.mo`)) { + # the 'mo' class only allows valid codes, of which "UNKNOWN" is one + x$`.mo`[is.na(x$`.mo`)] <- "UNKNOWN" + } else { + x$`.mo`[x$`.mo` %in% c(NA, "UNKNOWN")] <- "(??)" + } # get antimicrobials ab_trycatch <- tryCatch(colnames(suppressWarnings(x[, antimicrobials, drop = FALSE])), error = function(e) NULL) diff --git a/index.md b/index.md index c66f7a0ed..b8d032c54 100644 --- a/index.md +++ b/index.md @@ -220,12 +220,11 @@ the full explanation. wisca(example_isolates, antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), minimum = 10) # Recommended threshold: >=30 -#> Warning: invalid microorganism code, NA generated ``` | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin | |:------------------------|:-------------------------------------|:-------------------------------------| -| 69.8% (64.5-74.7%) | 93.6% (92.1-95%) | 89.9% (87-92.5%) | +| 70.2% (65-75.4%) | 93.6% (92-95.1%) | 89.9% (87-92.5%) | WISCA supports stratification by any clinical variable, so you can generate syndrome-specific or ward-specific coverage estimates: @@ -235,14 +234,13 @@ wisca(example_isolates, antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), syndromic_group = "ward", minimum = 10) # Recommended threshold: >=30 -#> Warning: invalid microorganism code, NA generated ``` | Syndromic Group | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin | |:----------------|:------------------------|:-------------------------------------|:-------------------------------------| -| Clinical | 74.6% (69.1-80.1%) | 93.7% (92.1-95.1%) | 90.3% (87-93.1%) | -| ICU | 56.9% (48.3-66%) | 86.7% (83.5-89.8%) | 83% (77.9-87.4%) | -| Outpatient | 57.7% (46.5-69.3%) | 76.6% (70.5-82.2%) | 67.8% (56.9-77.5%) | +| Clinical | 74.8% (69-80.3%) | 93.7% (92.1-95.1%) | 90.4% (86.8-93.3%) | +| ICU | 56.7% (48.2-65.4%) | 86.6% (83.3-89.6%) | 83.1% (78.2-87.5%) | +| Outpatient | 57.6% (46-69%) | 76.4% (69.8-81.7%) | 68% (57.7-77.5%) | **For AMR surveillance**, traditional antibiograms remain the right tool for tracking resistance per species over time: diff --git a/tests/testthat/test-antibiogram.R b/tests/testthat/test-antibiogram.R index 83b91be5b..0d9df6710 100644 --- a/tests/testthat/test-antibiogram.R +++ b/tests/testthat/test-antibiogram.R @@ -40,6 +40,22 @@ test_that("test-antibiogram.R", { expect_identical(get_gen(combine_SI = TRUE, guideline = "CLSI"), gen_eucast) expect_identical(get_gen(combine_SI = FALSE, guideline = "EUCAST"), gen_clsi) # identical since example_isolates contains no SDD + # isolates of unknown species must be included, also in WISCA + set.seed(1) + wisca_all <- suppressMessages(wisca(example_isolates, antimicrobials = "GEN", simulations = 100, info = FALSE, guideline = "EUCAST")) + set.seed(1) + wisca_known <- suppressMessages(wisca(example_isolates[example_isolates$mo != "UNKNOWN", ], antimicrobials = "GEN", simulations = 100, info = FALSE, guideline = "EUCAST")) + expect_true("UNKNOWN" %in% attributes(wisca_all)$wisca_parameters$mo) + expect_false(identical(as.data.frame(wisca_all), as.data.frame(wisca_known))) + if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) { + ab_grouped <- tryCatch( + example_isolates %>% group_by(ward) %>% antibiogram(antimicrobials = "GEN", minimum = 0, formatting_type = 1, guideline = "EUCAST"), + warning = function(w) conditionMessage(w) + ) + expect_inherits(ab_grouped, "antibiogram") + expect_true("UNKNOWN" %in% as.character(ab_grouped[[2]])) + } + # Traditional antibiogram ----------------------------------------------