1
0
mirror of https://github.com/msberends/AMR.git synced 2026-05-14 07:50:46 +02:00
Files
AMR/tests/testthat/test-interpretive_rules.R
Matthijs Berends 24f24ecaf8 Generalise interpretive rules for multi-guideline support (#268) (#283)
* Generalise interpretive rules for multi-guideline support (#268)

- Rename data-raw/eucast_rules.tsv → interpretive_rules.tsv; add rule.provider
  column (value: "EUCAST") to distinguish future CLSI rows
- Rename EUCAST_RULES_DF → INTERPRETIVE_RULES_DF in _pre_commit_checks.R;
  filter by rule.provider == guideline when applying rules in interpretive_rules()
- Rename custom_eucast_rules() → custom_interpretive_rules() with new S3 class
  "custom_interpretive_rules"; old function becomes a deprecated wrapper in
  zz_deprecated.R; backward-compat S3 dispatch shims added for old class
- Remove stop_if(guideline == "CLSI", ...) so clsi_rules() no longer errors
- Add .onLoad shim in zzz.R to create INTERPRETIVE_RULES_DF from EUCAST_RULES_DF
  for transitional compatibility until sysdata.rda is regenerated

https://claude.ai/code/session_01D46BTsfJSPo3HnLWp3PRkP

* Fix namespace load failure: remove assignInNamespace from .onLoad (#268)

assignInNamespace cannot add NEW bindings to a locked package namespace
(R locks namespace bindings before .onLoad runs). Replace the .onLoad
shim with a runtime fallback inside interpretive_rules(): if
INTERPRETIVE_RULES_DF is absent (pre-regeneration sysdata.rda), derive
it from EUCAST_RULES_DF by adding the rule.provider column. This also
fixes the screening_abx line to reuse the already-resolved
interpretive_rules_df_total instead of a bare INTERPRETIVE_RULES_DF
reference.

https://claude.ai/code/session_01D46BTsfJSPo3HnLWp3PRkP

* fixes

* fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-01 18:38:51 +01:00

251 lines
8.3 KiB
R
Executable File

# ==================================================================== #
# 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 #
# ==================================================================== #
test_that("test-interpretive_rules.R", {
skip_on_cran()
# thoroughly check input table
expect_equal(
sort(colnames(AMR:::INTERPRETIVE_RULES_DF)),
sort(c(
"rule.provider",
"if_mo_property", "like.is.one_of", "this_value",
"and_these_antibiotics", "have_these_values",
"then_change_these_antibiotics", "to_value",
"reference.rule", "reference.rule_group",
"reference.version",
"note"
))
)
MOs_mentioned <- unique(AMR:::INTERPRETIVE_RULES_DF$this_value)
MOs_mentioned <- sort(trimws(unlist(strsplit(MOs_mentioned[!AMR:::is_valid_regex(MOs_mentioned)], ",", fixed = TRUE))))
MOs_test <- suppressWarnings(
trimws(paste(
mo_genus(MOs_mentioned, keep_synonyms = TRUE, language = NULL),
mo_species(MOs_mentioned, keep_synonyms = TRUE, language = NULL),
mo_subspecies(MOs_mentioned, keep_synonyms = TRUE, language = NULL)
))
)
MOs_test[MOs_test == ""] <- mo_fullname(MOs_mentioned[MOs_test == ""], keep_synonyms = TRUE, language = NULL)
expect_equal(MOs_mentioned, MOs_test)
expect_error(suppressWarnings(interpretive_rules(example_isolates, col_mo = "Non-existing")))
expect_error(interpretive_rules(x = "text"))
expect_error(interpretive_rules(data.frame(a = "test")))
expect_error(interpretive_rules(data.frame(mo = "test"), rules = "invalid rules set"))
# expect_warning(interpretive_rules(data.frame(mo = "Escherichia coli", vancomycin = "S", stringsAsFactors = TRUE)))
expect_identical(
colnames(example_isolates),
colnames(suppressWarnings(interpretive_rules(example_isolates, info = FALSE)))
)
expect_output(suppressMessages(interpretive_rules(example_isolates, info = TRUE)))
a <- data.frame(
mo = c(
"Klebsiella pneumoniae",
"Pseudomonas aeruginosa",
"Enterobacter cloacae"
),
amox = "-", # Amoxicillin
stringsAsFactors = FALSE
)
b <- data.frame(
mo = c(
"Klebsiella pneumoniae",
"Pseudomonas aeruginosa",
"Enterobacter cloacae"
),
amox = "R", # Amoxicillin
stringsAsFactors = FALSE
)
expect_identical(suppressWarnings(interpretive_rules(a, "mo", info = FALSE)), b)
expect_output(suppressMessages(suppressWarnings(interpretive_rules(a, "mo", info = TRUE))))
a <- data.frame(
mo = c(
"Staphylococcus aureus",
"Streptococcus group A"
),
COL = "-", # Colistin
stringsAsFactors = FALSE
)
b <- data.frame(
mo = c(
"Staphylococcus aureus",
"Streptococcus group A"
),
COL = "R", # Colistin
stringsAsFactors = FALSE
)
expect_equal(suppressWarnings(interpretive_rules(a, "mo", info = FALSE)), b)
# piperacillin must be R in Enterobacteriaceae when tica is R
if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) {
expect_equal(
suppressWarnings(
example_isolates %>%
filter(mo_family(mo) == "Enterobacteriaceae") %>%
mutate(
TIC = as.sir("R"),
PIP = as.sir("S")
) %>%
interpretive_rules(col_mo = "mo", version_expertrules = 3.1, rules = "expert", info = FALSE, overwrite = TRUE) %>%
pull(PIP) %>%
unique() %>%
as.character()
),
"R"
)
}
# azithromycin and clarythromycin must be equal to Erythromycin
a <- suppressWarnings(as.sir(interpretive_rules(
data.frame(
mo = example_isolates$mo,
ERY = example_isolates$ERY,
AZM = as.sir("R"),
CLR = factor("R"),
stringsAsFactors = FALSE
),
version_expertrules = 3.1,
rules = "expert",
overwrite = TRUE,
only_sir_columns = FALSE
)$CLR))
b <- example_isolates$ERY
expect_identical(
a[!is.na(b)],
b[!is.na(b)]
)
# amox is inferred by benzylpenicillin in Kingella kingae
expect_equal(
suppressWarnings(
as.list(interpretive_rules(
data.frame(
mo = as.mo("Kingella kingae"),
PEN = "S",
AMX = "-",
stringsAsFactors = FALSE
),
info = FALSE
))$AMX
),
"S"
)
# also test norf
if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) {
expect_output(suppressWarnings(interpretive_rules(example_isolates %>% mutate(NOR = "S", NAL = "S"), info = TRUE)))
}
# check verbose output
expect_output(suppressWarnings(interpretive_rules(example_isolates, verbose = TRUE, rules = "all", info = TRUE)))
# AmpC de-repressed cephalo mutants
expect_identical(
interpretive_rules(
data.frame(
mo = c("Escherichia coli", "Enterobacter cloacae"),
cefotax = as.sir(c("S", "S"))
),
ampc_cephalosporin_resistance = TRUE,
rules = "expert",
overwrite = TRUE,
info = FALSE
)$cefotax,
as.sir(c("S", "R"))
)
expect_identical(
interpretive_rules(
data.frame(
mo = c("Escherichia coli", "Enterobacter cloacae"),
cefotax = as.sir(c("S", "S"))
),
ampc_cephalosporin_resistance = NA,
rules = "expert",
overwrite = TRUE,
info = FALSE
)$cefotax,
as.sir(c("S", NA))
)
expect_identical(
interpretive_rules(
data.frame(
mo = c("Escherichia coli", "Enterobacter cloacae"),
cefotax = as.sir(c("S", "S"))
),
rules = "expert",
ampc_cephalosporin_resistance = NULL,
overwrite = TRUE,
info = FALSE
)$cefotax,
as.sir(c("S", "S"))
)
# EUCAST dosage -----------------------------------------------------------
expect_equal(nrow(eucast_dosage(c("tobra", "genta", "cipro"))), 3)
expect_inherits(eucast_dosage(c("tobra", "genta", "cipro")), "data.frame")
x <- custom_interpretive_rules(
AMC == "R" & genus == "Klebsiella" ~ aminopenicillins == "R",
AMC == "I" & genus == "Klebsiella" ~ aminopenicillins == "I",
AMX == "S" ~ AMC == "S"
)
expect_output(print(x))
expect_output(print(c(x, x)))
expect_output(print(as.list(x, x)))
# this custom rules makes 8 changes
expect_equal(
nrow(interpretive_rules(example_isolates,
rules = "custom",
custom_rules = x,
info = FALSE,
overwrite = TRUE,
verbose = TRUE
)),
8,
tolerance = 0.5
)
# clsi_rules() no longer errors (returns data unchanged until CLSI rows are added)
expect_identical(
suppressWarnings(clsi_rules(example_isolates, info = FALSE)),
example_isolates
)
})