mirror of
https://github.com/msberends/AMR.git
synced 2026-03-07 14:14:37 +01:00
mdro(): infer base drug resistance from drug+inhibitor combination columns (#209)
When a base beta-lactam column (e.g., piperacillin/PIP) is absent but a corresponding drug+inhibitor combination (e.g., piperacillin/tazobactam/TZP) is present and resistant, resistance in the base drug is now correctly inferred. This is clinically sound: resistance in a combination implies the inhibitor provided no benefit, so the base drug is also resistant. Susceptibility in a combination is NOT propagated to the base drug (the inhibitor may be responsible for susceptibility), so only R values are inferred; missing base drugs remain NA otherwise. Implementation details: - Uses AB_BETALACTAMS_WITH_INHIBITOR to identify all beta-lactam+inhibitor combinations present in the user's data - Derives base drug AB codes by stripping the "/inhibitor" part from names - Creates synthetic proxy columns (.sir_proxy_<AB>) in x, set to "R" when any matching combination is R, otherwise NA - Proxy columns are added to cols_ab before drug variable assignment, so all existing guideline logic benefits without any changes - Multiple combos for the same base drug are OR-ed (any R → R) - Adds internal ab_without_inhibitor() helper for the name->base mapping - Verbose mode reports which combinations are used for inference Bumps version: 3.0.1.9028 -> 3.0.1.9029 https://claude.ai/code/session_01Cp154UtssHg84bw38xiiTG
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
Package: AMR
|
Package: AMR
|
||||||
Version: 3.0.1.9028
|
Version: 3.0.1.9029
|
||||||
Date: 2026-03-06
|
Date: 2026-03-06
|
||||||
Title: Antimicrobial Resistance Data Analysis
|
Title: Antimicrobial Resistance Data Analysis
|
||||||
Description: Functions to simplify and standardise antimicrobial resistance (AMR)
|
Description: Functions to simplify and standardise antimicrobial resistance (AMR)
|
||||||
|
|||||||
3
NEWS.md
3
NEWS.md
@@ -1,4 +1,4 @@
|
|||||||
# AMR 3.0.1.9028
|
# AMR 3.0.1.9029
|
||||||
|
|
||||||
### New
|
### New
|
||||||
* Integration with the **tidymodels** framework to allow seamless use of SIR, MIC and disk data in modelling pipelines via `recipes`
|
* Integration with the **tidymodels** framework to allow seamless use of SIR, MIC and disk data in modelling pipelines via `recipes`
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
* Two new `NA` objects, `NA_ab_` and `NA_mo_`, analogous to base R's `NA_character_` and `NA_integer_`, for use in pipelines that require typed missing values
|
* Two new `NA` objects, `NA_ab_` and `NA_mo_`, analogous to base R's `NA_character_` and `NA_integer_`, for use in pipelines that require typed missing values
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
* `mdro()`: when a base beta-lactam drug column is missing but a corresponding drug+inhibitor combination is present in the data and resistant (e.g., piperacillin/tazobactam = R while piperacillin is absent), the base drug is now correctly inferred as resistant. This ensures MDRO classification is not missed due to test-ordering differences in the laboratory. The reverse direction is also valid: susceptibility in a combination does not imply susceptibility in the base drug (the inhibitor may be responsible), so only resistance is propagated. Closes #209
|
||||||
* Fixed a bug in `as.ab()` where certain AB codes containing "PH" or "TH" (such as `ETH`, `MTH`, `PHE`, `PHN`, `STH`, `THA`, `THI1`) would incorrectly return `NA` when combined in a vector with any untranslatable value (#245)
|
* Fixed a bug in `as.ab()` where certain AB codes containing "PH" or "TH" (such as `ETH`, `MTH`, `PHE`, `PHN`, `STH`, `THA`, `THI1`) would incorrectly return `NA` when combined in a vector with any untranslatable value (#245)
|
||||||
* Fixed a bug in `antibiogram()` for when no antimicrobials are set
|
* Fixed a bug in `antibiogram()` for when no antimicrobials are set
|
||||||
* Fixed a bug in `as.sir()` where for numeric input the arguments `S`, `I`, and `R` would not be considered (#244)
|
* Fixed a bug in `as.sir()` where for numeric input the arguments `S`, `I`, and `R` would not be considered (#244)
|
||||||
|
|||||||
54
R/mdro.R
54
R/mdro.R
@@ -480,6 +480,50 @@ mdro <- function(x = NULL,
|
|||||||
}
|
}
|
||||||
cols_ab <- cols_ab[!duplicated(cols_ab)]
|
cols_ab <- cols_ab[!duplicated(cols_ab)]
|
||||||
|
|
||||||
|
# Infer resistance for missing base drugs from available drug+inhibitor combination columns.
|
||||||
|
# Clinical principle: resistance in drug+inhibitor (e.g., piperacillin/tazobactam = R)
|
||||||
|
# always implies resistance in the base drug (e.g., piperacillin = R), because the
|
||||||
|
# enzyme inhibitor adds nothing when the organism is truly resistant to the base drug.
|
||||||
|
# NOTE: susceptibility in a combination does NOT imply susceptibility in the base drug
|
||||||
|
# (the inhibitor may be responsible), so synthetic proxy columns only propagate R, not S/I.
|
||||||
|
.combos_in_data <- AB_BETALACTAMS_WITH_INHIBITOR[AB_BETALACTAMS_WITH_INHIBITOR %in% names(cols_ab)]
|
||||||
|
if (length(.combos_in_data) > 0) {
|
||||||
|
.base_drugs <- suppressMessages(
|
||||||
|
as.ab(gsub("/.*", "", ab_name(as.character(.combos_in_data), language = NULL)))
|
||||||
|
)
|
||||||
|
.unique_bases <- unique(.base_drugs[!is.na(.base_drugs)])
|
||||||
|
for (.base in .unique_bases) {
|
||||||
|
.base_code <- as.character(.base)
|
||||||
|
if (!.base_code %in% names(cols_ab)) {
|
||||||
|
# Base drug column absent; find all available combo columns for this base drug
|
||||||
|
.combos <- .combos_in_data[!is.na(.base_drugs) & as.character(.base_drugs) == .base_code]
|
||||||
|
.combo_cols <- unname(cols_ab[as.character(.combos)])
|
||||||
|
.combo_cols <- .combo_cols[!is.na(.combo_cols)]
|
||||||
|
if (length(.combo_cols) > 0) {
|
||||||
|
# Vectorised: if ANY combination is R, infer base drug as R; otherwise NA
|
||||||
|
.sir_chars <- as.data.frame(
|
||||||
|
lapply(x[, .combo_cols, drop = FALSE], function(col) as.character(as.sir(col))),
|
||||||
|
stringsAsFactors = FALSE
|
||||||
|
)
|
||||||
|
.new_col <- paste0(".sir_proxy_", .base_code)
|
||||||
|
x[[.new_col]] <- ifelse(rowSums(.sir_chars == "R", na.rm = TRUE) > 0L, "R", NA_character_)
|
||||||
|
cols_ab <- c(cols_ab, setNames(.new_col, .base_code))
|
||||||
|
if (isTRUE(verbose)) {
|
||||||
|
message_(
|
||||||
|
"Inferring resistance for ", ab_name(.base_code, language = NULL),
|
||||||
|
" from available drug+inhibitor combination(s): ",
|
||||||
|
paste(ab_name(as.character(.combos), language = NULL), collapse = ", "),
|
||||||
|
" (resistance in a combination always implies resistance in the base drug)",
|
||||||
|
add_fn = font_blue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cols_ab <- cols_ab[!duplicated(names(cols_ab))]
|
||||||
|
}
|
||||||
|
rm(list = intersect(ls(), c(".combos_in_data", ".base_drugs", ".unique_bases", ".base", ".base_code", ".combos", ".combo_cols", ".sir_chars", ".new_col")))
|
||||||
|
|
||||||
# nolint start
|
# nolint start
|
||||||
AMC <- cols_ab["AMC"]
|
AMC <- cols_ab["AMC"]
|
||||||
AMK <- cols_ab["AMK"]
|
AMK <- cols_ab["AMK"]
|
||||||
@@ -674,6 +718,16 @@ mdro <- function(x = NULL,
|
|||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ab_without_inhibitor <- function(ab_codes) {
|
||||||
|
# Get the base drug AB code from a drug+inhibitor combination.
|
||||||
|
# e.g., AMC (amoxicillin/clavulanic acid) -> AMX (amoxicillin)
|
||||||
|
# TZP (piperacillin/tazobactam) -> PIP (piperacillin)
|
||||||
|
# SAM (ampicillin/sulbactam) -> AMP (ampicillin)
|
||||||
|
combo_names <- ab_name(ab_codes, language = NULL)
|
||||||
|
base_names <- gsub("/.*", "", combo_names)
|
||||||
|
suppressMessages(as.ab(base_names))
|
||||||
|
}
|
||||||
|
|
||||||
# antimicrobial classes
|
# antimicrobial classes
|
||||||
# nolint start
|
# nolint start
|
||||||
aminoglycosides <- c(TOB, GEN)
|
aminoglycosides <- c(TOB, GEN)
|
||||||
|
|||||||
Reference in New Issue
Block a user