diff --git a/DESCRIPTION b/DESCRIPTION index caee9f10d..e979dde4f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: AMR -Version: 3.0.1.9122 +Version: 3.0.1.9123 Date: 2026-10-12 Title: Antimicrobial Resistance Data Analysis Description: Functions to simplify and standardise antimicrobial resistance (AMR) diff --git a/NEWS.md b/NEWS.md index 622cdc00c..f2a6fd0b3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# AMR 3.0.1.9122 +# AMR 3.0.1.9123 Planned as v3.1.0, mid October 2026. @@ -25,7 +25,6 @@ Planned as v3.1.0, mid October 2026. * Faster parallel computing via the `future` package for `as.sir()` and `wisca()`: a non-sequential plan (e.g. `future::plan(future::multisession)`) must be active before using `parallel = TRUE`. * `as.mo()` no longer guesses unknown MO codes (these become `NA` with a warning), and MO codes from versions before v2.0.0 are no longer supported -* With multiple antimicrobials, `count_*()` and `proportion_*()` now follow the documented combination therapy rules: `resistance()` returns the proportion of failing combinations instead of resistance to any of the antimicrobials, and `count_all()` excludes isolates of which the combination result is unknown (#321) ### New * EUCAST 2026 and CLSI 2026 breakpoints: over 5,700 new breakpoints added to the `clinical_breakpoints` data set; EUCAST 2026 is now the default for all MIC and disk diffusion interpretations @@ -45,7 +44,7 @@ Planned as v3.1.0, mid October 2026. * New `wisca_plot()` to assess the susceptibility and incidence distributions from the Monte Carlo simulations * A registry of all microbial IDs (MO codes) since v2.0.0 now prevents existing IDs from being reused or mixed up; IDs of earlier releases are translated to the current ID of the same taxon * `add_custom_antimicrobial_synonyms()` to add local names, such as trade names, to existing antimicrobials (#307) -* `sir_combine()` to combine SIR results of multiple antimicrobials into one variable for combination therapy, using the same rules as `antibiogram()` (#321) +* `sir_combine()` to combine SIR results of multiple antimicrobials into one variable, for combination therapy (`method = "most_susceptible"`) or resistance within a group (`method = "most_resistant"`) (#321) ### Fixed * `as.mo()` returned *Salmonella bongori* or another subspecies for *Salmonella* serovars written with the species, such as "Salmonella enterica serovar Typhi" diff --git a/R/count.R b/R/count.R index d49aca89a..d6e74b4c8 100755 --- a/R/count.R +++ b/R/count.R @@ -42,7 +42,7 @@ #' @inheritSection as.sir Interpretation of SIR #' @details These functions are meant to count isolates. Use the [resistance()]/[susceptibility()] functions to calculate microbial resistance/susceptibility. #' -#' The function [n_sir()] is an alias of [count_all()]. They can be used to count all available isolates, i.e. where the input antimicrobial has an available result (S, I or R), or for multiple antimicrobials, where the result of the combination is available (see *Combination Therapy* below). Their use is equal to `dplyr`'s `n_distinct()`. Their function is equal to `count_susceptible(...) + count_resistant(...)`. +#' The function [n_sir()] is an alias of [count_all()]. They can be used to count all available isolates, i.e. where the input antimicrobial has an available result (S, I or R). For multiple antimicrobials, these are isolates with a result for any of the antimicrobials, or with `only_all_tested = TRUE`, for all of the antimicrobials (see *Combination Therapy* below). Their use is equal to `dplyr`'s `n_distinct()`. For one antimicrobial, their function is equal to `count_susceptible(...) + count_resistant(...)`. #' #' The function [count_df()] takes any variable from `data` that has an [`sir`] class (created with [as.sir()]) and counts the number of S's, I's and R's. It also supports grouped variables. The function [sir_df()] works exactly like [count_df()], but adds the percentage of S, I and R. #' @inheritSection sir_combine Combination Therapy diff --git a/R/sir_calc.R b/R/sir_calc.R index 4ba14bd75..cbfbf6050 100755 --- a/R/sir_calc.R +++ b/R/sir_calc.R @@ -132,20 +132,31 @@ sir_calc <- function(..., } if (ncol(x) == 1) { x <- x[, 1, drop = TRUE] - } else { - # combination therapy: determine the result of the combination for every isolate, see ?sir_combine - x <- sir_combine_engine(as.list(x), S_values = get_susceptible_values(guideline), only_all_tested = only_all_tested) + } else if (isTRUE(only_all_tested)) { + # combination therapy of isolates tested for all antimicrobials, see ?sir_combine + x <- sir_combine_engine(as.list(x), S_values = get_susceptible_values(guideline), only_all_tested = TRUE) } } else if (!is.sir(x)) { x <- as.sir(x) print_warning <- TRUE } - if ("SDD" %in% ab_result && "SDD" %in% x && message_not_thrown_before("sir_calc", only_count, ab_result, entire_session = TRUE)) { + if ("SDD" %in% ab_result && "SDD" %in% unlist(lapply(as.list(x), as.character)) && message_not_thrown_before("sir_calc", only_count, ab_result, entire_session = TRUE)) { message_("Note that {.fun ", ifelse(only_count, "count", "proportion"), "_", ifelse("S" %in% ab_result, "S", ""), "I", ifelse("R" %in% ab_result, "R", ""), "} will also include dose-dependent susceptibility, {.val SDD}. This note will be shown once for this session.", as_note = FALSE) } - numerator <- sum(x %in% ab_result, na.rm = TRUE) - denominator <- sum(x %in% denominator_vals, na.rm = TRUE) + if (is.data.frame(x)) { + # multiple antimicrobials with only_all_tested = FALSE: an isolate counts if any antimicrobial has a result in `ab_result`, and is considered tested if it has such a result or was tested for all antimicrobials, see ?sir_combine + # this equals sir_combine(method = "most_susceptible") for susceptibility and sir_combine(method = "most_resistant") for resistance + # 'NI' is a missing result + numerator_vals <- intersect(as.character(ab_result), denominator_vals) + is_counted <- Reduce(`|`, lapply(x, function(col) as.character(col) %in% numerator_vals)) + is_complete <- Reduce(`&`, lapply(x, function(col) as.character(col) %in% denominator_vals)) + numerator <- sum(is_counted) + denominator <- sum(is_counted | is_complete) + } else { + numerator <- sum(x %in% ab_result, na.rm = TRUE) + denominator <- sum(x %in% denominator_vals, na.rm = TRUE) + } if (print_warning == TRUE) { if (message_not_thrown_before("sir_calc")) { diff --git a/R/sir_combine.R b/R/sir_combine.R index 98a6bf012..307917b59 100644 --- a/R/sir_combine.R +++ b/R/sir_combine.R @@ -29,38 +29,60 @@ #' Combine SIR Results of Multiple Antimicrobials #' -#' @description Combine the SIR results of two or more antimicrobials into one SIR result per isolate, e.g. to create a variable for the combination therapy of piperacillin/tazobactam with gentamicin. The result is the best result of the antimicrobials, as a combination therapy covers the isolate if any of its antimicrobials is active. See *Combination Therapy* below for the exact rules. +#' @description Combine the SIR results of two or more antimicrobials into one SIR result per isolate. This can answer two different questions, set with the `method` argument: #' -#' The same rules are used by [antibiogram()] and [wisca()] for combination antibiograms, and by the [`count_*()`][count()] and [`proportion_*()`][proportion()] functions when more than one variable is used, so the result of [sir_combine()] can be used in any further analysis. +#' * `"most_susceptible"` (default): *Is at least one of these antimicrobials expected to work?* This is the result of a combination therapy, e.g. piperacillin/tazobactam with gentamicin, which covers the isolate if any of its antimicrobials is active. +#' * `"most_resistant"`: *Is resistance demonstrated to any of these antimicrobials?* This is the result for a group of antimicrobials, e.g. resistance to third-generation cephalosporins if an isolate is resistant to ceftazidime or ceftriaxone, as used in surveillance reports. +#' +#' See *Combination Therapy* below for the exact rules. The same rules are used by [antibiogram()] and [wisca()] for combination antibiograms, and by the [`count_*()`][count()] and [`proportion_*()`][proportion()] functions when more than one variable is used. #' @param ... Two or more vectors (or columns) with antimicrobial interpretations, or a data frame with such columns. They will be transformed internally with [as.sir()] if needed. All vectors must be of the same length. -#' @param only_all_tested A [logical] to indicate that isolates must be tested for all antimicrobials. If `TRUE`, the result is `NA` for isolates with a missing result for any of the antimicrobials. If `FALSE` (default), a missing result only leads to `NA` if a result for the missing antimicrobial could have changed the result of the combination, see *Combination Therapy* below. +#' @param method Either `"most_susceptible"` (default) to return the most susceptible result, as for combination therapy, or `"most_resistant"` to return the most resistant result, as for resistance within a group of antimicrobials. See *Combination Therapy* below. +#' @param only_all_tested A [logical] to indicate that isolates must be tested for all antimicrobials. If `TRUE`, the result is `NA` for isolates with a missing result for any of the antimicrobials. If `FALSE` (default), a missing result only leads to `NA` if a result for the missing antimicrobial could have changed the result, see *Combination Therapy* below. #' @param guideline Either `"EUCAST"` (default) or `"CLSI"`, to determine whether the 'I' category is considered susceptible. This only affects isolates with a missing result for any of the antimicrobials when `only_all_tested = FALSE`, see *Combination Therapy* below. The default can be changed with the `AMR_guideline` option, see [AMR-options]. #' @section Combination Therapy: -#' A combination of antimicrobials is considered as good as its most active antimicrobial: `"S"` is preferred over `"WT"`, and `"R"` over `"NWT"` and `"NS"`. The value `"NI"` (non-interpretable) is considered a missing result. +#' Results are ordered from susceptible to resistant: S (and WT), SDD, I, R (and NWT, NS). With `method = "most_susceptible"` (in [sir_combine()]), the most susceptible result of the antimicrobials is returned, and with `method = "most_resistant"`, the most resistant result. The value `"NI"` (non-interpretable) is considered a missing result. #' -#' A missing result can only be ignored if the result of the combination is susceptible regardless of the missing result, which depends on the `only_all_tested` argument and on the guideline: under EUCAST, the 'I' category is considered susceptible (*susceptible, increased exposure*), and under CLSI, it is not. +#' A missing result can only be ignored if it cannot change the conclusion: a susceptible result for `"most_susceptible"`, and a non-susceptible result for `"most_resistant"`. Under EUCAST, the 'I' category is considered susceptible (*susceptible, increased exposure*), and under CLSI, it is not. With `only_all_tested = TRUE`, any missing result leads to `NA`. #' -#' This is an example for two antimicrobials, Drug A and Drug B (the order of the antimicrobials does not matter): +#' This is an example for two antimicrobials, Drug A and Drug B, under EUCAST (the order of the antimicrobials does not matter): #' #' ``` #' ------------------------------------------------------------------------- -#' Result of the combination -#' ----------------------------------------------------- -#' Drug A Drug B only_all_tested = FALSE only_all_tested = TRUE -#' -------- -------- ---------------------------- ---------------------- -#' S S, I, R S S -#' I I, R I I -#' R R R R -#' S S -#' I I (EUCAST), (CLSI) -#' R -#' +#' Result of method = Result of method = +#' "most_susceptible" "most_resistant" +#' ----------------------- ----------------------------- +#' only_all_tested = only_all_tested = +#' Drug A Drug B FALSE TRUE FALSE TRUE +#' -------- -------- --------- --------- ----------- ------------- +#' S S S S S S +#' S I S S I I +#' S R S S R R +#' I R I I R R +#' R R R R R R +#' S S +#' I I +#' R R +#' #' ------------------------------------------------------------------------- #' ``` #' -#' The [`count_*()`][count()] and [`proportion_*()`][proportion()] functions, such as [susceptibility()] and [resistance()], apply exactly these rules when more than one variable is used: an isolate is considered tested if the result of the combination is not `NA`, and is then counted by the result of the combination. Consequently, [count_susceptible()] `+` [count_resistant()] `=` [count_all()], and [susceptibility()] `+` [resistance()] `= 1`, for both values of `only_all_tested`. The guideline is taken from their `guideline` argument if they have one, and otherwise from the `AMR_guideline` option. +#' Under CLSI, the combination of I with a missing result is `` for `"most_susceptible"` and I for `"most_resistant"`. #' -#' In [antibiogram()] and [wisca()], the 'I' and 'SDD' categories are considered susceptible if `combine_SI = TRUE` (default), and the result of the combination is then reduced to S or R. +#' The [`count_*()`][count()] and [`proportion_*()`][proportion()] functions apply these rules when more than one variable is used. With `only_all_tested = FALSE` (default), the functions for susceptibility, such as [susceptibility()] and [count_susceptible()], count an isolate as susceptible if any of the antimicrobials is susceptible (`"most_susceptible"`), and the functions for resistance, such as [resistance()] and [count_resistant()], count an isolate as resistant if any of the antimicrobials is resistant (`"most_resistant"`). An isolate is considered tested if it has such a result, or if it was tested for all antimicrobials. Therefore, for combinations with `only_all_tested = FALSE`: +#' +#' ``` +#' count_S() + count_I() + count_R() >= count_all() +#' proportion_S() + proportion_I() + proportion_R() >= 1 +#' ``` +#' +#' With `only_all_tested = TRUE`, all these functions count the result of the combination therapy (`"most_susceptible"`) of isolates tested for all antimicrobials, so that: +#' +#' ``` +#' count_S() + count_I() + count_R() = count_all() +#' proportion_S() + proportion_I() + proportion_R() = 1 +#' ``` +#' +#' In [antibiogram()] and [wisca()], the result of the combination therapy (`"most_susceptible"`) is used, the 'I' and 'SDD' categories are considered susceptible if `combine_SI = TRUE` (default), and the result is then reduced to S or R. #' @return A [`sir`] vector with the same length as the input. #' @seealso [count()] and [proportion()] for counting and calculating susceptibility of (combinations of) antimicrobials, and [antibiogram()] for combination antibiograms. #' @export @@ -68,13 +90,19 @@ #' drug_a <- as.sir(c("S", "R", "R", "I", "I", NA, "R", "SDD")) #' drug_b <- as.sir(c("R", "S", "R", "R", NA, NA, NA, "I")) #' +#' # combination therapy: is at least one of them expected to work? #' sir_combine(drug_a, drug_b) #' sir_combine(drug_a, drug_b, guideline = "CLSI") #' sir_combine(drug_a, drug_b, only_all_tested = TRUE) #' +#' # resistance within a group: is resistance demonstrated to any of them? +#' sir_combine(drug_a, drug_b, method = "most_resistant") +#' #' # the count and proportion functions use the same rules #' susceptibility(drug_a, drug_b, minimum = 0) #' susceptibility(sir_combine(drug_a, drug_b), minimum = 0) +#' resistance(drug_a, drug_b, minimum = 0) +#' resistance(sir_combine(drug_a, drug_b, method = "most_resistant"), minimum = 0) #' #' \donttest{ #' if (require("dplyr")) { @@ -85,6 +113,12 @@ #' select(mo, TZP, GEN, TZP_GEN) #' } #' if (require("dplyr")) { +#' # resistance to third-generation cephalosporins +#' example_isolates %>% +#' mutate(cephalosporins_3rd = sir_combine(CAZ, CRO, method = "most_resistant")) %>% +#' count(cephalosporins_3rd) +#' } +#' if (require("dplyr")) { #' # combine any number of antimicrobials #' example_isolates %>% #' mutate(any_aminoglycoside = sir_combine(GEN, TOB, AMK, KAN)) %>% @@ -105,7 +139,8 @@ #' ) #' } #' } -sir_combine <- function(..., only_all_tested = FALSE, guideline = getOption("AMR_guideline", "EUCAST")) { +sir_combine <- function(..., method = c("most_susceptible", "most_resistant"), only_all_tested = FALSE, guideline = getOption("AMR_guideline", "EUCAST")) { + method <- match.arg(method) meet_criteria(only_all_tested, allow_class = "logical", has_length = 1) meet_criteria(guideline, allow_class = "character", has_length = 1) if (guideline %like% "EUCAST") { @@ -129,13 +164,13 @@ sir_combine <- function(..., only_all_tested = FALSE, guideline = getOption("AMR stop_if(length(unique(col_lengths)) > 1, "all vectors or columns must be of the same length, but they have lengths ", vector_and(sort(unique(col_lengths)), quotes = FALSE), ".") cols <- lapply(cols, function(x) if (is.sir(x)) x else as.sir(x)) - out <- sir_combine_engine(cols, S_values = get_susceptible_values(guideline), only_all_tested = only_all_tested) + out <- sir_combine_engine(cols, S_values = get_susceptible_values(guideline), only_all_tested = only_all_tested, method = method) if (isFALSE(only_all_tested) && is.null(getOption("AMR_guideline")) && missing(guideline)) { # only notify about the assumed guideline if it affected the result - any_missing <- Reduce(`|`, lapply(cols, function(x) is.na(x) | as.character(x) == "NI")) - if (any(any_missing & as.character(out) %in% "I") && message_not_thrown_before("sir_combine", "eucast_default", entire_session = TRUE)) { - message_("{.help [{.fun sir_combine}](AMR::sir_combine)} assumes the EUCAST guideline and thus considers the 'I' category susceptible, so a missing result does not change a combination with an 'I' result. Set the {.arg guideline} argument or the {.code AMR_guideline} option to either \"CLSI\" or \"EUCAST\", see {.topic [AMR-options](AMR::AMR-options)}.") + out_clsi <- sir_combine_engine(cols, S_values = get_susceptible_values("CLSI"), only_all_tested = only_all_tested, method = method) + if (!identical(out, out_clsi) && message_not_thrown_before("sir_combine", "eucast_default", entire_session = TRUE)) { + message_("{.help [{.fun sir_combine}](AMR::sir_combine)} assumes the EUCAST guideline and thus considers the 'I' category susceptible, which affected the result for 'I' results combined with a missing result. Set the {.arg guideline} argument or the {.code AMR_guideline} option to either \"CLSI\" or \"EUCAST\", see {.topic [AMR-options](AMR::AMR-options)}.") message_("This message will be shown once per session.") } } @@ -151,11 +186,12 @@ get_susceptible_values <- function(guideline) { } } -sir_combine_engine <- function(cols, S_values, only_all_tested = FALSE) { - # this is the engine for combination therapy, used by sir_combine(), sir_calc() and antibiogram() +sir_combine_engine <- function(cols, S_values, only_all_tested = FALSE, method = "most_susceptible") { + # this is the engine for combining SIR results, used by sir_combine(), sir_calc() and antibiogram() # - cols: a list of vectors of equal length, with class 'sir' or with SIR values as character - # - S_values: the values considered susceptible, so that a missing result cannot change the result of the combination - # the order of preference: the first value is the best result, values with equal rank are resolved by this order + # - S_values: the values considered susceptible; all other values are considered non-susceptible + # - method: "most_susceptible" (combination therapy) or "most_resistant" (resistance within a group) + # the order of preference: values with equal rank are resolved by this order preference <- c("S", "WT", "SDD", "I", "R", "NWT", "NS") ranks <- c(1L, 1L, 2L, 3L, 4L, 4L, 4L) cols <- lapply(cols, as.character) @@ -164,18 +200,25 @@ sir_combine_engine <- function(cols, S_values, only_all_tested = FALSE) { } # 'NI' and NA get no rank, so they are missing results col_ranks <- lapply(cols, function(x) ranks[match(x, preference)]) - best <- do.call(pmin, c(col_ranks, list(na.rm = TRUE))) - out <- rep(NA_character_, length(best)) + if (method == "most_susceptible") { + selected <- do.call(pmin, c(col_ranks, list(na.rm = TRUE))) + # a missing result cannot change a susceptible result + decisive_values <- S_values + } else { + selected <- do.call(pmax, c(col_ranks, list(na.rm = TRUE))) + # a missing result cannot change a non-susceptible result + decisive_values <- setdiff(preference, S_values) + } + out <- rep(NA_character_, length(selected)) for (i in seq_along(preference)) { is_present <- Reduce(`|`, lapply(cols, function(x) x %in% preference[i])) - out[is.na(out) & best %in% ranks[i] & is_present] <- preference[i] + out[is.na(out) & selected %in% ranks[i] & is_present] <- preference[i] } any_missing <- Reduce(`|`, lapply(col_ranks, is.na)) if (isTRUE(only_all_tested)) { out[any_missing] <- NA_character_ } else { - # a missing result could have been better, unless the combination is already susceptible - out[any_missing & !out %in% S_values] <- NA_character_ + out[any_missing & !out %in% decisive_values] <- NA_character_ } as_sir_structure(out) } diff --git a/index.md b/index.md index 401da2dd4..49be060f7 100644 --- a/index.md +++ b/index.md @@ -170,8 +170,10 @@ example_isolates %>% #> ℹ Using column mo as input for `mo_fullname()` #> ℹ Using column mo as input for `mo_is_gram_negative()` #> ℹ Using column mo as input for `mo_is_intrinsic_resistant()` -#> ℹ Determining intrinsic resistance based on 'EUCAST Expected Resistant Phenotypes' v1.2 (2023). This note will be shown once per session. -#> ℹ For `aminoglycosides()` using columns GEN (gentamicin), TOB (tobramycin), AMK (amikacin), and KAN (kanamycin) +#> ℹ Determining intrinsic resistance based on 'EUCAST Expected Resistant Phenotypes' v1.2 (2023). +#> This note will be shown once per session. +#> ℹ For `aminoglycosides()` using columns GEN (gentamicin), TOB (tobramycin), AMK (amikacin), and KAN +#> (kanamycin) #> ℹ For `carbapenems()` using columns IPM (imipenem) and MEM (meropenem) #> # A tibble: 35 × 7 #> bacteria GEN TOB AMK KAN IPM MEM @@ -364,13 +366,15 @@ out <- example_isolates %>% # calculate AMR using resistance(), over all aminoglycosides and polymyxins: summarise(across(c(aminoglycosides(), polymyxins()), resistance)) -#> ℹ For `aminoglycosides()` using columns GEN (gentamicin), TOB (tobramycin), AMK (amikacin), and KAN (kanamycin) +#> ℹ For `aminoglycosides()` using columns GEN (gentamicin), TOB (tobramycin), AMK (amikacin), and KAN +#> (kanamycin) #> ℹ For `polymyxins()` using column COL (colistin) #> Warning: There was 1 warning in `summarise()`. #> ℹ In argument: `across(c(aminoglycosides(), polymyxins()), resistance)`. #> ℹ In group 3: `ward = "Outpatient"`. #> Caused by warning: -#> ! Introducing NA: only 23 results available for KAN in group: ward = "Outpatient" (whilst `minimum = 30`). +#> ! Introducing NA: only 23 results available for KAN in group: ward = "Outpatient" (whilst `minimum = +#> 30`). out #> # A tibble: 3 × 6 #> ward GEN TOB AMK KAN COL diff --git a/man/antibiogram.Rd b/man/antibiogram.Rd index 8e563d409..38f2c0df8 100644 --- a/man/antibiogram.Rd +++ b/man/antibiogram.Rd @@ -271,30 +271,46 @@ For the full mathematical derivation and worked examples, see the \href{https:// \section{Combination Therapy}{ -A combination of antimicrobials is considered as good as its most active antimicrobial: \code{"S"} is preferred over \code{"WT"}, and \code{"R"} over \code{"NWT"} and \code{"NS"}. The value \code{"NI"} (non-interpretable) is considered a missing result. +Results are ordered from susceptible to resistant: S (and WT), SDD, I, R (and NWT, NS). With \code{method = "most_susceptible"} (in \code{\link[=sir_combine]{sir_combine()}}), the most susceptible result of the antimicrobials is returned, and with \code{method = "most_resistant"}, the most resistant result. The value \code{"NI"} (non-interpretable) is considered a missing result. -A missing result can only be ignored if the result of the combination is susceptible regardless of the missing result, which depends on the \code{only_all_tested} argument and on the guideline: under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. +A missing result can only be ignored if it cannot change the conclusion: a susceptible result for \code{"most_susceptible"}, and a non-susceptible result for \code{"most_resistant"}. Under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. With \code{only_all_tested = TRUE}, any missing result leads to \code{NA}. -This is an example for two antimicrobials, Drug A and Drug B (the order of the antimicrobials does not matter): +This is an example for two antimicrobials, Drug A and Drug B, under EUCAST (the order of the antimicrobials does not matter): \if{html}{\out{
}}\preformatted{------------------------------------------------------------------------- - Result of the combination - ----------------------------------------------------- - Drug A Drug B only_all_tested = FALSE only_all_tested = TRUE --------- -------- ---------------------------- ---------------------- - S S, I, R S S - I I, R I I - R R R R - S S - I I (EUCAST), (CLSI) - R - + Result of method = Result of method = + "most_susceptible" "most_resistant" + ----------------------- ----------------------------- + only_all_tested = only_all_tested = + Drug A Drug B FALSE TRUE FALSE TRUE +-------- -------- --------- --------- ----------- ------------- + S S S S S S + S I S S I I + S R S S R R + I R I I R R + R R R R R R + S S + I I + R R + ------------------------------------------------------------------------- }\if{html}{\out{
}} -The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=resistance]{resistance()}}, apply exactly these rules when more than one variable is used: an isolate is considered tested if the result of the combination is not \code{NA}, and is then counted by the result of the combination. Consequently, \code{\link[=count_susceptible]{count_susceptible()}} \code{+} \code{\link[=count_resistant]{count_resistant()}} \code{=} \code{\link[=count_all]{count_all()}}, and \code{\link[=susceptibility]{susceptibility()}} \code{+} \code{\link[=resistance]{resistance()}} \verb{= 1}, for both values of \code{only_all_tested}. The guideline is taken from their \code{guideline} argument if they have one, and otherwise from the \code{AMR_guideline} option. +Under CLSI, the combination of I with a missing result is \verb{} for \code{"most_susceptible"} and I for \code{"most_resistant"}. -In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result of the combination is then reduced to S or R. +The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions apply these rules when more than one variable is used. With \code{only_all_tested = FALSE} (default), the functions for susceptibility, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=count_susceptible]{count_susceptible()}}, count an isolate as susceptible if any of the antimicrobials is susceptible (\code{"most_susceptible"}), and the functions for resistance, such as \code{\link[=resistance]{resistance()}} and \code{\link[=count_resistant]{count_resistant()}}, count an isolate as resistant if any of the antimicrobials is resistant (\code{"most_resistant"}). An isolate is considered tested if it has such a result, or if it was tested for all antimicrobials. Therefore, for combinations with \code{only_all_tested = FALSE}: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() >= count_all() + proportion_S() + proportion_I() + proportion_R() >= 1 +}\if{html}{\out{
}} + +With \code{only_all_tested = TRUE}, all these functions count the result of the combination therapy (\code{"most_susceptible"}) of isolates tested for all antimicrobials, so that: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() = count_all() + proportion_S() + proportion_I() + proportion_R() = 1 +}\if{html}{\out{
}} + +In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the result of the combination therapy (\code{"most_susceptible"}) is used, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result is then reduced to S or R. } \examples{ diff --git a/man/count.Rd b/man/count.Rd index d747cc7ce..4a6b97c84 100644 --- a/man/count.Rd +++ b/man/count.Rd @@ -79,7 +79,7 @@ These functions can be used to count resistant/susceptible microbial isolates. A \details{ These functions are meant to count isolates. Use the \code{\link[=resistance]{resistance()}}/\code{\link[=susceptibility]{susceptibility()}} functions to calculate microbial resistance/susceptibility. -The function \code{\link[=n_sir]{n_sir()}} is an alias of \code{\link[=count_all]{count_all()}}. They can be used to count all available isolates, i.e. where the input antimicrobial has an available result (S, I or R), or for multiple antimicrobials, where the result of the combination is available (see \emph{Combination Therapy} below). Their use is equal to \code{dplyr}'s \code{n_distinct()}. Their function is equal to \code{count_susceptible(...) + count_resistant(...)}. +The function \code{\link[=n_sir]{n_sir()}} is an alias of \code{\link[=count_all]{count_all()}}. They can be used to count all available isolates, i.e. where the input antimicrobial has an available result (S, I or R). For multiple antimicrobials, these are isolates with a result for any of the antimicrobials, or with \code{only_all_tested = TRUE}, for all of the antimicrobials (see \emph{Combination Therapy} below). Their use is equal to \code{dplyr}'s \code{n_distinct()}. For one antimicrobial, their function is equal to \code{count_susceptible(...) + count_resistant(...)}. The function \code{\link[=count_df]{count_df()}} takes any variable from \code{data} that has an \code{\link{sir}} class (created with \code{\link[=as.sir]{as.sir()}}) and counts the number of S's, I's and R's. It also supports grouped variables. The function \code{\link[=sir_df]{sir_df()}} works exactly like \code{\link[=count_df]{count_df()}}, but adds the percentage of S, I and R. } @@ -92,30 +92,46 @@ This AMR package follows insight; use \code{\link[=susceptibility]{susceptibilit \section{Combination Therapy}{ -A combination of antimicrobials is considered as good as its most active antimicrobial: \code{"S"} is preferred over \code{"WT"}, and \code{"R"} over \code{"NWT"} and \code{"NS"}. The value \code{"NI"} (non-interpretable) is considered a missing result. +Results are ordered from susceptible to resistant: S (and WT), SDD, I, R (and NWT, NS). With \code{method = "most_susceptible"} (in \code{\link[=sir_combine]{sir_combine()}}), the most susceptible result of the antimicrobials is returned, and with \code{method = "most_resistant"}, the most resistant result. The value \code{"NI"} (non-interpretable) is considered a missing result. -A missing result can only be ignored if the result of the combination is susceptible regardless of the missing result, which depends on the \code{only_all_tested} argument and on the guideline: under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. +A missing result can only be ignored if it cannot change the conclusion: a susceptible result for \code{"most_susceptible"}, and a non-susceptible result for \code{"most_resistant"}. Under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. With \code{only_all_tested = TRUE}, any missing result leads to \code{NA}. -This is an example for two antimicrobials, Drug A and Drug B (the order of the antimicrobials does not matter): +This is an example for two antimicrobials, Drug A and Drug B, under EUCAST (the order of the antimicrobials does not matter): \if{html}{\out{
}}\preformatted{------------------------------------------------------------------------- - Result of the combination - ----------------------------------------------------- - Drug A Drug B only_all_tested = FALSE only_all_tested = TRUE --------- -------- ---------------------------- ---------------------- - S S, I, R S S - I I, R I I - R R R R - S S - I I (EUCAST), (CLSI) - R - + Result of method = Result of method = + "most_susceptible" "most_resistant" + ----------------------- ----------------------------- + only_all_tested = only_all_tested = + Drug A Drug B FALSE TRUE FALSE TRUE +-------- -------- --------- --------- ----------- ------------- + S S S S S S + S I S S I I + S R S S R R + I R I I R R + R R R R R R + S S + I I + R R + ------------------------------------------------------------------------- }\if{html}{\out{
}} -The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=resistance]{resistance()}}, apply exactly these rules when more than one variable is used: an isolate is considered tested if the result of the combination is not \code{NA}, and is then counted by the result of the combination. Consequently, \code{\link[=count_susceptible]{count_susceptible()}} \code{+} \code{\link[=count_resistant]{count_resistant()}} \code{=} \code{\link[=count_all]{count_all()}}, and \code{\link[=susceptibility]{susceptibility()}} \code{+} \code{\link[=resistance]{resistance()}} \verb{= 1}, for both values of \code{only_all_tested}. The guideline is taken from their \code{guideline} argument if they have one, and otherwise from the \code{AMR_guideline} option. +Under CLSI, the combination of I with a missing result is \verb{} for \code{"most_susceptible"} and I for \code{"most_resistant"}. -In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result of the combination is then reduced to S or R. +The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions apply these rules when more than one variable is used. With \code{only_all_tested = FALSE} (default), the functions for susceptibility, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=count_susceptible]{count_susceptible()}}, count an isolate as susceptible if any of the antimicrobials is susceptible (\code{"most_susceptible"}), and the functions for resistance, such as \code{\link[=resistance]{resistance()}} and \code{\link[=count_resistant]{count_resistant()}}, count an isolate as resistant if any of the antimicrobials is resistant (\code{"most_resistant"}). An isolate is considered tested if it has such a result, or if it was tested for all antimicrobials. Therefore, for combinations with \code{only_all_tested = FALSE}: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() >= count_all() + proportion_S() + proportion_I() + proportion_R() >= 1 +}\if{html}{\out{
}} + +With \code{only_all_tested = TRUE}, all these functions count the result of the combination therapy (\code{"most_susceptible"}) of isolates tested for all antimicrobials, so that: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() = count_all() + proportion_S() + proportion_I() + proportion_R() = 1 +}\if{html}{\out{
}} + +In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the result of the combination therapy (\code{"most_susceptible"}) is used, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result is then reduced to S or R. } \examples{ diff --git a/man/proportion.Rd b/man/proportion.Rd index 8f08d00b5..e416f990b 100644 --- a/man/proportion.Rd +++ b/man/proportion.Rd @@ -135,30 +135,46 @@ This AMR package follows insight; use \code{\link[=susceptibility]{susceptibilit \section{Combination Therapy}{ -A combination of antimicrobials is considered as good as its most active antimicrobial: \code{"S"} is preferred over \code{"WT"}, and \code{"R"} over \code{"NWT"} and \code{"NS"}. The value \code{"NI"} (non-interpretable) is considered a missing result. +Results are ordered from susceptible to resistant: S (and WT), SDD, I, R (and NWT, NS). With \code{method = "most_susceptible"} (in \code{\link[=sir_combine]{sir_combine()}}), the most susceptible result of the antimicrobials is returned, and with \code{method = "most_resistant"}, the most resistant result. The value \code{"NI"} (non-interpretable) is considered a missing result. -A missing result can only be ignored if the result of the combination is susceptible regardless of the missing result, which depends on the \code{only_all_tested} argument and on the guideline: under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. +A missing result can only be ignored if it cannot change the conclusion: a susceptible result for \code{"most_susceptible"}, and a non-susceptible result for \code{"most_resistant"}. Under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. With \code{only_all_tested = TRUE}, any missing result leads to \code{NA}. -This is an example for two antimicrobials, Drug A and Drug B (the order of the antimicrobials does not matter): +This is an example for two antimicrobials, Drug A and Drug B, under EUCAST (the order of the antimicrobials does not matter): \if{html}{\out{
}}\preformatted{------------------------------------------------------------------------- - Result of the combination - ----------------------------------------------------- - Drug A Drug B only_all_tested = FALSE only_all_tested = TRUE --------- -------- ---------------------------- ---------------------- - S S, I, R S S - I I, R I I - R R R R - S S - I I (EUCAST), (CLSI) - R - + Result of method = Result of method = + "most_susceptible" "most_resistant" + ----------------------- ----------------------------- + only_all_tested = only_all_tested = + Drug A Drug B FALSE TRUE FALSE TRUE +-------- -------- --------- --------- ----------- ------------- + S S S S S S + S I S S I I + S R S S R R + I R I I R R + R R R R R R + S S + I I + R R + ------------------------------------------------------------------------- }\if{html}{\out{
}} -The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=resistance]{resistance()}}, apply exactly these rules when more than one variable is used: an isolate is considered tested if the result of the combination is not \code{NA}, and is then counted by the result of the combination. Consequently, \code{\link[=count_susceptible]{count_susceptible()}} \code{+} \code{\link[=count_resistant]{count_resistant()}} \code{=} \code{\link[=count_all]{count_all()}}, and \code{\link[=susceptibility]{susceptibility()}} \code{+} \code{\link[=resistance]{resistance()}} \verb{= 1}, for both values of \code{only_all_tested}. The guideline is taken from their \code{guideline} argument if they have one, and otherwise from the \code{AMR_guideline} option. +Under CLSI, the combination of I with a missing result is \verb{} for \code{"most_susceptible"} and I for \code{"most_resistant"}. -In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result of the combination is then reduced to S or R. +The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions apply these rules when more than one variable is used. With \code{only_all_tested = FALSE} (default), the functions for susceptibility, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=count_susceptible]{count_susceptible()}}, count an isolate as susceptible if any of the antimicrobials is susceptible (\code{"most_susceptible"}), and the functions for resistance, such as \code{\link[=resistance]{resistance()}} and \code{\link[=count_resistant]{count_resistant()}}, count an isolate as resistant if any of the antimicrobials is resistant (\code{"most_resistant"}). An isolate is considered tested if it has such a result, or if it was tested for all antimicrobials. Therefore, for combinations with \code{only_all_tested = FALSE}: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() >= count_all() + proportion_S() + proportion_I() + proportion_R() >= 1 +}\if{html}{\out{
}} + +With \code{only_all_tested = TRUE}, all these functions count the result of the combination therapy (\code{"most_susceptible"}) of isolates tested for all antimicrobials, so that: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() = count_all() + proportion_S() + proportion_I() + proportion_R() = 1 +}\if{html}{\out{
}} + +In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the result of the combination therapy (\code{"most_susceptible"}) is used, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result is then reduced to S or R. } \examples{ diff --git a/man/sir_combine.Rd b/man/sir_combine.Rd index 9a1a98c69..8fab9ae3a 100644 --- a/man/sir_combine.Rd +++ b/man/sir_combine.Rd @@ -6,6 +6,7 @@ \usage{ sir_combine( ..., + method = c("most_susceptible", "most_resistant"), only_all_tested = FALSE, guideline = getOption("AMR_guideline", "EUCAST") ) @@ -13,7 +14,9 @@ sir_combine( \arguments{ \item{...}{Two or more vectors (or columns) with antimicrobial interpretations, or a data frame with such columns. They will be transformed internally with \code{\link[=as.sir]{as.sir()}} if needed. All vectors must be of the same length.} -\item{only_all_tested}{A \link{logical} to indicate that isolates must be tested for all antimicrobials. If \code{TRUE}, the result is \code{NA} for isolates with a missing result for any of the antimicrobials. If \code{FALSE} (default), a missing result only leads to \code{NA} if a result for the missing antimicrobial could have changed the result of the combination, see \emph{Combination Therapy} below.} +\item{method}{Either \code{"most_susceptible"} (default) to return the most susceptible result, as for combination therapy, or \code{"most_resistant"} to return the most resistant result, as for resistance within a group of antimicrobials. See \emph{Combination Therapy} below.} + +\item{only_all_tested}{A \link{logical} to indicate that isolates must be tested for all antimicrobials. If \code{TRUE}, the result is \code{NA} for isolates with a missing result for any of the antimicrobials. If \code{FALSE} (default), a missing result only leads to \code{NA} if a result for the missing antimicrobial could have changed the result, see \emph{Combination Therapy} below.} \item{guideline}{Either \code{"EUCAST"} (default) or \code{"CLSI"}, to determine whether the 'I' category is considered susceptible. This only affects isolates with a missing result for any of the antimicrobials when \code{only_all_tested = FALSE}, see \emph{Combination Therapy} below. The default can be changed with the \code{AMR_guideline} option, see \link{AMR-options}.} } @@ -21,49 +24,75 @@ sir_combine( A \code{\link{sir}} vector with the same length as the input. } \description{ -Combine the SIR results of two or more antimicrobials into one SIR result per isolate, e.g. to create a variable for the combination therapy of piperacillin/tazobactam with gentamicin. The result is the best result of the antimicrobials, as a combination therapy covers the isolate if any of its antimicrobials is active. See \emph{Combination Therapy} below for the exact rules. +Combine the SIR results of two or more antimicrobials into one SIR result per isolate. This can answer two different questions, set with the \code{method} argument: +\itemize{ +\item \code{"most_susceptible"} (default): \emph{Is at least one of these antimicrobials expected to work?} This is the result of a combination therapy, e.g. piperacillin/tazobactam with gentamicin, which covers the isolate if any of its antimicrobials is active. +\item \code{"most_resistant"}: \emph{Is resistance demonstrated to any of these antimicrobials?} This is the result for a group of antimicrobials, e.g. resistance to third-generation cephalosporins if an isolate is resistant to ceftazidime or ceftriaxone, as used in surveillance reports. +} -The same rules are used by \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}} for combination antibiograms, and by the \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions when more than one variable is used, so the result of \code{\link[=sir_combine]{sir_combine()}} can be used in any further analysis. +See \emph{Combination Therapy} below for the exact rules. The same rules are used by \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}} for combination antibiograms, and by the \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions when more than one variable is used. } \section{Combination Therapy}{ -A combination of antimicrobials is considered as good as its most active antimicrobial: \code{"S"} is preferred over \code{"WT"}, and \code{"R"} over \code{"NWT"} and \code{"NS"}. The value \code{"NI"} (non-interpretable) is considered a missing result. +Results are ordered from susceptible to resistant: S (and WT), SDD, I, R (and NWT, NS). With \code{method = "most_susceptible"} (in \code{\link[=sir_combine]{sir_combine()}}), the most susceptible result of the antimicrobials is returned, and with \code{method = "most_resistant"}, the most resistant result. The value \code{"NI"} (non-interpretable) is considered a missing result. -A missing result can only be ignored if the result of the combination is susceptible regardless of the missing result, which depends on the \code{only_all_tested} argument and on the guideline: under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. +A missing result can only be ignored if it cannot change the conclusion: a susceptible result for \code{"most_susceptible"}, and a non-susceptible result for \code{"most_resistant"}. Under EUCAST, the 'I' category is considered susceptible (\emph{susceptible, increased exposure}), and under CLSI, it is not. With \code{only_all_tested = TRUE}, any missing result leads to \code{NA}. -This is an example for two antimicrobials, Drug A and Drug B (the order of the antimicrobials does not matter): +This is an example for two antimicrobials, Drug A and Drug B, under EUCAST (the order of the antimicrobials does not matter): \if{html}{\out{
}}\preformatted{------------------------------------------------------------------------- - Result of the combination - ----------------------------------------------------- - Drug A Drug B only_all_tested = FALSE only_all_tested = TRUE --------- -------- ---------------------------- ---------------------- - S S, I, R S S - I I, R I I - R R R R - S S - I I (EUCAST), (CLSI) - R - + Result of method = Result of method = + "most_susceptible" "most_resistant" + ----------------------- ----------------------------- + only_all_tested = only_all_tested = + Drug A Drug B FALSE TRUE FALSE TRUE +-------- -------- --------- --------- ----------- ------------- + S S S S S S + S I S S I I + S R S S R R + I R I I R R + R R R R R R + S S + I I + R R + ------------------------------------------------------------------------- }\if{html}{\out{
}} -The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=resistance]{resistance()}}, apply exactly these rules when more than one variable is used: an isolate is considered tested if the result of the combination is not \code{NA}, and is then counted by the result of the combination. Consequently, \code{\link[=count_susceptible]{count_susceptible()}} \code{+} \code{\link[=count_resistant]{count_resistant()}} \code{=} \code{\link[=count_all]{count_all()}}, and \code{\link[=susceptibility]{susceptibility()}} \code{+} \code{\link[=resistance]{resistance()}} \verb{= 1}, for both values of \code{only_all_tested}. The guideline is taken from their \code{guideline} argument if they have one, and otherwise from the \code{AMR_guideline} option. +Under CLSI, the combination of I with a missing result is \verb{} for \code{"most_susceptible"} and I for \code{"most_resistant"}. -In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result of the combination is then reduced to S or R. +The \code{\link[=count]{count_*()}} and \code{\link[=proportion]{proportion_*()}} functions apply these rules when more than one variable is used. With \code{only_all_tested = FALSE} (default), the functions for susceptibility, such as \code{\link[=susceptibility]{susceptibility()}} and \code{\link[=count_susceptible]{count_susceptible()}}, count an isolate as susceptible if any of the antimicrobials is susceptible (\code{"most_susceptible"}), and the functions for resistance, such as \code{\link[=resistance]{resistance()}} and \code{\link[=count_resistant]{count_resistant()}}, count an isolate as resistant if any of the antimicrobials is resistant (\code{"most_resistant"}). An isolate is considered tested if it has such a result, or if it was tested for all antimicrobials. Therefore, for combinations with \code{only_all_tested = FALSE}: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() >= count_all() + proportion_S() + proportion_I() + proportion_R() >= 1 +}\if{html}{\out{
}} + +With \code{only_all_tested = TRUE}, all these functions count the result of the combination therapy (\code{"most_susceptible"}) of isolates tested for all antimicrobials, so that: + +\if{html}{\out{
}}\preformatted{ count_S() + count_I() + count_R() = count_all() + proportion_S() + proportion_I() + proportion_R() = 1 +}\if{html}{\out{
}} + +In \code{\link[=antibiogram]{antibiogram()}} and \code{\link[=wisca]{wisca()}}, the result of the combination therapy (\code{"most_susceptible"}) is used, the 'I' and 'SDD' categories are considered susceptible if \code{combine_SI = TRUE} (default), and the result is then reduced to S or R. } \examples{ drug_a <- as.sir(c("S", "R", "R", "I", "I", NA, "R", "SDD")) drug_b <- as.sir(c("R", "S", "R", "R", NA, NA, NA, "I")) +# combination therapy: is at least one of them expected to work? sir_combine(drug_a, drug_b) sir_combine(drug_a, drug_b, guideline = "CLSI") sir_combine(drug_a, drug_b, only_all_tested = TRUE) +# resistance within a group: is resistance demonstrated to any of them? +sir_combine(drug_a, drug_b, method = "most_resistant") + # the count and proportion functions use the same rules susceptibility(drug_a, drug_b, minimum = 0) susceptibility(sir_combine(drug_a, drug_b), minimum = 0) +resistance(drug_a, drug_b, minimum = 0) +resistance(sir_combine(drug_a, drug_b, method = "most_resistant"), minimum = 0) \donttest{ if (require("dplyr")) { @@ -73,6 +102,12 @@ if (require("dplyr")) { mutate(TZP_GEN = sir_combine(TZP, GEN)) \%>\% select(mo, TZP, GEN, TZP_GEN) } +if (require("dplyr")) { + # resistance to third-generation cephalosporins + example_isolates \%>\% + mutate(cephalosporins_3rd = sir_combine(CAZ, CRO, method = "most_resistant")) \%>\% + count(cephalosporins_3rd) +} if (require("dplyr")) { # combine any number of antimicrobials example_isolates \%>\% diff --git a/tests/testthat/test-count.R b/tests/testthat/test-count.R index 452bf8be2..a39483386 100644 --- a/tests/testthat/test-count.R +++ b/tests/testthat/test-count.R @@ -68,17 +68,12 @@ test_that("test-count.R", { expect_equal(example_isolates %>% count_susceptible(AMC, GEN, only_all_tested = TRUE), 1687) expect_equal(example_isolates %>% count_susceptible(AMC, GEN, only_all_tested = FALSE), 1764) expect_equal(example_isolates %>% count_all(AMC, GEN, only_all_tested = TRUE), 1798) - expect_equal(example_isolates %>% count_all(AMC, GEN, only_all_tested = FALSE), 1875) + expect_equal(example_isolates %>% count_all(AMC, GEN, only_all_tested = FALSE), 1936) expect_identical( example_isolates %>% count_all(AMC, GEN, only_all_tested = TRUE), example_isolates %>% count_susceptible(AMC, GEN, only_all_tested = TRUE) + example_isolates %>% count_resistant(AMC, GEN, only_all_tested = TRUE) ) - expect_identical( - example_isolates %>% count_all(AMC, GEN, only_all_tested = FALSE), - example_isolates %>% count_susceptible(AMC, GEN, only_all_tested = FALSE) + - example_isolates %>% count_resistant(AMC, GEN, only_all_tested = FALSE) - ) # count of cases expect_equal( diff --git a/tests/testthat/test-proportion.R b/tests/testthat/test-proportion.R index 80561eac0..c7bdeaa2a 100755 --- a/tests/testthat/test-proportion.R +++ b/tests/testthat/test-proportion.R @@ -95,7 +95,7 @@ test_that("test-proportion.R", { combination_n = n_sir(CIP, GEN) ) %>% pull(combination_n), - c(979, 462, 101) + c(1181, 577, 116) ) # proportion_df diff --git a/tests/testthat/test-sir_combine.R b/tests/testthat/test-sir_combine.R index f5957007b..7ba556f05 100644 --- a/tests/testthat/test-sir_combine.R +++ b/tests/testthat/test-sir_combine.R @@ -69,6 +69,36 @@ test_that("test-sir_combine.R", { expect_identical(sir_combine(a, b, guideline = "CLSI"), sir_combine(b, a, guideline = "CLSI")) } + # most resistant result of two non-missing values, written by hand + worst <- matrix( + c( + "S", "SDD", "I", "R", "S", "NWT", "NS", + "SDD", "SDD", "I", "R", "SDD", "NWT", "NS", + "I", "I", "I", "R", "I", "NWT", "NS", + "R", "R", "R", "R", "R", "R", "R", + "S", "SDD", "I", "R", "WT", "NWT", "NS", + "NWT", "NWT", "NWT", "R", "NWT", "NWT", "NWT", + "NS", "NS", "NS", "R", "NS", "NWT", "NS" + ), + nrow = 7, byrow = TRUE, dimnames = list(vals, vals) + ) + expect_identical(worst, t(worst)) + worst_missing_eucast <- c(S = NA, SDD = NA, I = NA, R = "R", WT = NA, NWT = "NWT", NS = "NS") + worst_missing_clsi <- c(S = NA, SDD = NA, I = "I", R = "R", WT = NA, NWT = "NWT", NS = "NS") + for (missing_val in c(NA, "NI")) { + a <- c(rep(vals, each = length(vals)), vals, rep(missing_val, length(vals)), missing_val) + b <- c(rep(vals, times = length(vals)), rep(missing_val, length(vals)), vals, missing_val) + expected_complete <- worst[cbind(rep(vals, each = length(vals)), rep(vals, times = length(vals)))] + a <- as.sir(a) + b <- as.sir(b) + expect_identical(sir_combine(a, b, method = "most_resistant", guideline = "EUCAST"), as.sir(unname(c(expected_complete, worst_missing_eucast[vals], worst_missing_eucast[vals], NA)))) + expect_identical(sir_combine(a, b, method = "most_resistant", guideline = "CLSI"), as.sir(unname(c(expected_complete, worst_missing_clsi[vals], worst_missing_clsi[vals], NA)))) + expect_identical(sir_combine(a, b, method = "most_resistant", only_all_tested = TRUE), as.sir(unname(c(expected_complete, rep(NA, 2 * length(vals) + 1))))) + expect_identical(sir_combine(a, b, method = "most_resistant"), sir_combine(b, a, method = "most_resistant")) + } + expect_identical(sir_combine(as.sir("S"), as.sir("R"), method = "most_r"), as.sir("R")) + expect_error(sir_combine(as.sir("S"), as.sir("R"), method = "worst")) + # three or more antimicrobials x1 <- as.sir(c("R", "R", "R", NA, "I", "R", "NI")) x2 <- as.sir(c("R", "I", "R", "R", "R", NA, "R")) @@ -101,25 +131,41 @@ test_that("test-sir_combine.R", { r1 <- random_sir_values() r2 <- random_sir_values() r3 <- random_sir_values() - for (oat in c(FALSE, TRUE)) { - for (gl in c("EUCAST", "CLSI")) { - combined <- sir_combine(r1, r2, r3, only_all_tested = oat, guideline = gl) - expect_identical(count_all(r1, r2, r3, only_all_tested = oat), sum(!is.na(sir_combine(r1, r2, r3, only_all_tested = oat, guideline = "EUCAST")))) - expect_identical(count_susceptible(r1, r2, r3, only_all_tested = oat, guideline = gl), count_susceptible(combined, guideline = gl)) - expect_identical(count_resistant(r1, r2, r3, only_all_tested = oat, guideline = gl), count_resistant(combined, guideline = gl)) - expect_equal(susceptibility(r1, r2, r3, only_all_tested = oat, guideline = gl, minimum = 0), susceptibility(combined, guideline = gl, minimum = 0)) - expect_equal(resistance(r1, r2, r3, only_all_tested = oat, guideline = gl, minimum = 0), resistance(combined, guideline = gl, minimum = 0)) - expect_equal(susceptibility(r1, r2, r3, only_all_tested = oat, guideline = gl, minimum = 0) + resistance(r1, r2, r3, only_all_tested = oat, guideline = gl, minimum = 0), 1) - expect_identical(count_susceptible(combined, guideline = gl) + count_resistant(combined, guideline = gl), sum(!is.na(combined))) - } - combined <- sir_combine(r1, r2, only_all_tested = oat, guideline = "EUCAST") - expect_identical(count_SI(r1, r2, only_all_tested = oat) + count_R(r1, r2, only_all_tested = oat), count_all(r1, r2, only_all_tested = oat)) - expect_identical(count_S(r1, r2, only_all_tested = oat), sum(combined %in% "S")) - expect_identical(count_I(r1, r2, only_all_tested = oat), sum(combined %in% c("I", "SDD"))) - expect_identical(count_all(r1, r2, only_all_tested = oat), sum(!is.na(combined))) - expect_identical(count_R(r1, r2, only_all_tested = oat), sum(combined %in% c("R", "NWT", "NS"))) - expect_equal(proportion_SI(r1, r2, only_all_tested = oat, minimum = 0) + proportion_R(r1, r2, only_all_tested = oat, minimum = 0), 1) + for (gl in c("EUCAST", "CLSI")) { + # only_all_tested = FALSE: susceptibility follows "most_susceptible" and resistance follows "most_resistant", as in AMR 3.0.1 + most_s <- sir_combine(r1, r2, r3, guideline = gl) + most_r <- sir_combine(r1, r2, r3, method = "most_resistant", guideline = gl) + expect_identical(count_susceptible(r1, r2, r3, guideline = gl), count_susceptible(most_s, guideline = gl)) + expect_identical(count_resistant(r1, r2, r3, guideline = gl), count_resistant(most_r, guideline = gl)) + expect_equal(susceptibility(r1, r2, r3, guideline = gl, minimum = 0), susceptibility(most_s, guideline = gl, minimum = 0)) + expect_equal(resistance(r1, r2, r3, guideline = gl, minimum = 0), resistance(most_r, guideline = gl, minimum = 0)) + expect_true(susceptibility(r1, r2, r3, guideline = gl, minimum = 0) + resistance(r1, r2, r3, guideline = gl, minimum = 0) >= 1) + # only_all_tested = TRUE: all functions follow "most_susceptible" of isolates tested for all antimicrobials + combined <- sir_combine(r1, r2, r3, only_all_tested = TRUE, guideline = gl) + expect_identical(count_all(r1, r2, r3, only_all_tested = TRUE), sum(!is.na(combined))) + expect_identical(count_susceptible(r1, r2, r3, only_all_tested = TRUE, guideline = gl), count_susceptible(combined, guideline = gl)) + expect_identical(count_resistant(r1, r2, r3, only_all_tested = TRUE, guideline = gl), count_resistant(combined, guideline = gl)) + expect_equal(susceptibility(r1, r2, r3, only_all_tested = TRUE, guideline = gl, minimum = 0) + resistance(r1, r2, r3, only_all_tested = TRUE, guideline = gl, minimum = 0), 1) } + # count_all() with only_all_tested = FALSE counts isolates with any result, 'NI' is a missing result + is_tested <- function(x) !is.na(x) & x != "NI" + expect_identical(count_all(r1, r2, r3), sum(is_tested(r1) | is_tested(r2) | is_tested(r3))) + combined <- sir_combine(r1, r2, only_all_tested = TRUE) + expect_identical(count_S(r1, r2, only_all_tested = TRUE), sum(combined %in% "S")) + expect_identical(count_I(r1, r2, only_all_tested = TRUE), sum(combined %in% c("I", "SDD"))) + expect_identical(count_R(r1, r2, only_all_tested = TRUE), sum(combined %in% c("R", "NWT", "NS"))) + expect_identical(count_SI(r1, r2, only_all_tested = TRUE) + count_R(r1, r2, only_all_tested = TRUE), count_all(r1, r2, only_all_tested = TRUE)) + # behaviour of AMR 3.0.1 for all nine combinations of S, R and NA + a <- as.sir(c("S", "R", NA, "S", "R", NA, "S", "R", NA)) + b <- as.sir(c("S", "S", "S", "R", "R", "R", NA, NA, NA)) + expect_identical(count_all(a, b), 8L) + expect_identical(count_susceptible(a, b), 5L) + expect_identical(count_resistant(a, b), 5L) + expect_equal(susceptibility(a, b, minimum = 0), 5 / 6) + expect_equal(resistance(a, b, minimum = 0), 5 / 6) + expect_identical(count_all(a, b, only_all_tested = TRUE), 4L) + expect_identical(count_susceptible(a, b, only_all_tested = TRUE), 3L) + expect_identical(count_resistant(a, b, only_all_tested = TRUE), 1L) # antibiogram() gives the same result as the frozen implementation from before sir_combine() existed old_combination <- function(df, S_values, only_all_tested) {