From aa8f6af185e4c589b337cc95afdb5e03a90d51ac Mon Sep 17 00:00:00 2001 From: Matthijs Berends Date: Sun, 23 Feb 2025 11:18:08 +0100 Subject: [PATCH] (v2.1.1.9155) new `mic_p50()` and `mic_p90()` - updated AMR intro --- DESCRIPTION | 4 +- NAMESPACE | 2 + NEWS.md | 4 +- PythonPackage/AMR/AMR.egg-info/PKG-INFO | 2 +- PythonPackage/AMR/AMR/__init__.py | 2 + PythonPackage/AMR/AMR/functions.py | 6 + .../AMR/dist/AMR-2.1.1.9154-py3-none-any.whl | Bin 10255 -> 0 bytes .../AMR/dist/AMR-2.1.1.9155-py3-none-any.whl | Bin 0 -> 10286 bytes PythonPackage/AMR/dist/amr-2.1.1.9154.tar.gz | Bin 10067 -> 0 bytes PythonPackage/AMR/dist/amr-2.1.1.9155.tar.gz | Bin 0 -> 10094 bytes PythonPackage/AMR/setup.py | 2 +- R/aa_helper_functions.R | 3 + R/antibiogram.R | 39 ++- R/bug_drug_combinations.R | 3 +- R/mic.R | 15 ++ R/plotting.R | 2 +- R/sir.R | 2 +- R/sir_calc.R | 6 +- ....txt => gpt_training_text_v2.1.1.9155.txt} | 243 +++++++++++++++--- index.md | 24 +- man/as.mic.Rd | 8 + man/plot.Rd | 125 +++++++++ tests/testthat/test-mic.R | 6 +- vignettes/AMR.Rmd | 82 +++++- 24 files changed, 481 insertions(+), 99 deletions(-) delete mode 100644 PythonPackage/AMR/dist/AMR-2.1.1.9154-py3-none-any.whl create mode 100644 PythonPackage/AMR/dist/AMR-2.1.1.9155-py3-none-any.whl delete mode 100644 PythonPackage/AMR/dist/amr-2.1.1.9154.tar.gz create mode 100644 PythonPackage/AMR/dist/amr-2.1.1.9155.tar.gz rename data-raw/{gpt_training_text_v2.1.1.9154.txt => gpt_training_text_v2.1.1.9155.txt} (98%) diff --git a/DESCRIPTION b/DESCRIPTION index 2c35b4b43..436912be3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AMR -Version: 2.1.1.9154 -Date: 2025-02-22 +Version: 2.1.1.9155 +Date: 2025-02-23 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 diff --git a/NAMESPACE b/NAMESPACE index 5b2dcbec3..fa2e38634 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -251,6 +251,8 @@ export(mdr_cmi2012) export(mdr_tb) export(mdro) export(mean_amr_distance) +export(mic_p50) +export(mic_p90) export(mo_authors) export(mo_class) export(mo_cleaning_regex) diff --git a/NEWS.md b/NEWS.md index ee37d9fab..434953dbb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# AMR 2.1.1.9154 +# AMR 2.1.1.9155 *(this beta version will eventually become v3.0. We're happy to reach a new major milestone soon, which will be all about the new One Health support! Install this beta using [the instructions here](https://msberends.github.io/AMR/#latest-development-version).)* @@ -38,7 +38,7 @@ This package now supports not only tools for AMR data analysis in clinical setti * **Other** * New function `top_n_microorganisms()` to filter a data set to the top *n* of any taxonomic property, e.g., filter to the top 3 species, filter to any species in the top 5 genera, or filter to the top 3 species in each of the top 5 genera * New function `mo_group_members()` to retrieve the member microorganisms of a microorganism group. For example, `mo_group_members("Strep group C")` returns a vector of all microorganisms that belong to that group. - + * New functions `mic_p50()` and `mic_p90()` to retrieve the 50th and 90th percentile of MIC values. ## Changed * SIR interpretation diff --git a/PythonPackage/AMR/AMR.egg-info/PKG-INFO b/PythonPackage/AMR/AMR.egg-info/PKG-INFO index 4583ab3ba..7e2f2d1fd 100644 --- a/PythonPackage/AMR/AMR.egg-info/PKG-INFO +++ b/PythonPackage/AMR/AMR.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.2 Name: AMR -Version: 2.1.1.9154 +Version: 2.1.1.9155 Summary: A Python wrapper for the AMR R package Home-page: https://github.com/msberends/AMR Author: Matthijs Berends diff --git a/PythonPackage/AMR/AMR/__init__.py b/PythonPackage/AMR/AMR/__init__.py index 4175abe4d..21e55385b 100644 --- a/PythonPackage/AMR/AMR/__init__.py +++ b/PythonPackage/AMR/AMR/__init__.py @@ -73,6 +73,8 @@ from .functions import is_disk from .functions import as_mic from .functions import is_mic from .functions import rescale_mic +from .functions import mic_p50 +from .functions import mic_p90 from .functions import as_mo from .functions import is_mo from .functions import mo_uncertainties diff --git a/PythonPackage/AMR/AMR/functions.py b/PythonPackage/AMR/AMR/functions.py index 6963838c7..6e91b0583 100644 --- a/PythonPackage/AMR/AMR/functions.py +++ b/PythonPackage/AMR/AMR/functions.py @@ -249,6 +249,12 @@ def is_mic(x): def rescale_mic(x, *args, **kwargs): """See our website of the R package for the manual: https://msberends.github.io/AMR/index.html""" return convert_to_python(amr_r.rescale_mic(x, *args, **kwargs)) +def mic_p50(x, *args, **kwargs): + """See our website of the R package for the manual: https://msberends.github.io/AMR/index.html""" + return convert_to_python(amr_r.mic_p50(x, *args, **kwargs)) +def mic_p90(x, *args, **kwargs): + """See our website of the R package for the manual: https://msberends.github.io/AMR/index.html""" + return convert_to_python(amr_r.mic_p90(x, *args, **kwargs)) def as_mo(x, *args, **kwargs): """See our website of the R package for the manual: https://msberends.github.io/AMR/index.html""" return convert_to_python(amr_r.as_mo(x, *args, **kwargs)) diff --git a/PythonPackage/AMR/dist/AMR-2.1.1.9154-py3-none-any.whl b/PythonPackage/AMR/dist/AMR-2.1.1.9154-py3-none-any.whl deleted file mode 100644 index bd4abbb05b08b3b04b74478c59a632b69ee8f5fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10255 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 literal 0 HcmV?d00001 diff --git a/PythonPackage/AMR/dist/amr-2.1.1.9154.tar.gz b/PythonPackage/AMR/dist/amr-2.1.1.9154.tar.gz deleted file mode 100644 index a168b57e9732e00bf91421634fd69f14aa296d9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10067 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 literal 0 HcmV?d00001 diff --git a/PythonPackage/AMR/setup.py b/PythonPackage/AMR/setup.py index f7fc7dc48..7caaa3f0a 100644 --- a/PythonPackage/AMR/setup.py +++ b/PythonPackage/AMR/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='AMR', - version='2.1.1.9154', + version='2.1.1.9155', packages=find_packages(), install_requires=[ 'rpy2', diff --git a/R/aa_helper_functions.R b/R/aa_helper_functions.R index 2378f68eb..a007be4dc 100644 --- a/R/aa_helper_functions.R +++ b/R/aa_helper_functions.R @@ -1390,6 +1390,9 @@ as_original_data_class <- function(df, old_class = NULL, extra_class = NULL) { fn <- function(x) base::as.data.frame(df, stringsAsFactors = FALSE) } out <- fn(df) + # don't keep row names + rownames(out) <- NULL + # add additional class if needed if (!is.null(extra_class)) { class(out) <- c(extra_class, class(out)) } diff --git a/R/antibiogram.R b/R/antibiogram.R index 0de1d7a3d..b9087166c 100755 --- a/R/antibiogram.R +++ b/R/antibiogram.R @@ -923,19 +923,16 @@ antibiogram.default <- function(x, } } - out <- as_original_data_class(new_df, class(x), extra_class = "antibiogram") + out <- structure(as_original_data_class(new_df, class(x), extra_class = "antibiogram"), + has_syndromic_group = has_syndromic_group, + combine_SI = combine_SI, + wisca = wisca, + conf_interval = conf_interval, + formatting_type = formatting_type, + wisca_parameters = as_original_data_class(wisca_parameters, class(x)), + long_numeric = as_original_data_class(long_numeric, class(x))) rownames(out) <- NULL - rownames(wisca_parameters) <- NULL - rownames(long_numeric) <- NULL - structure(out, - has_syndromic_group = has_syndromic_group, - combine_SI = combine_SI, - wisca = wisca, - conf_interval = conf_interval, - formatting_type = formatting_type, - wisca_parameters = as_original_data_class(wisca_parameters, class(x)), - long_numeric = as_original_data_class(long_numeric, class(x)) - ) + out } #' @method antibiogram grouped_df @@ -1041,14 +1038,16 @@ antibiogram.grouped_df <- function(x, close(progress) - structure(as_original_data_class(out, class(x), extra_class = "antibiogram"), - has_syndromic_group = FALSE, - combine_SI = isTRUE(combine_SI), - wisca = isTRUE(wisca), - conf_interval = conf_interval, - formatting_type = formatting_type, - wisca_parameters = as_original_data_class(wisca_parameters, class(x)), - long_numeric = as_original_data_class(long_numeric, class(x))) + out <- structure(as_original_data_class(out, class(x), extra_class = "antibiogram"), + has_syndromic_group = FALSE, + combine_SI = isTRUE(combine_SI), + wisca = isTRUE(wisca), + conf_interval = conf_interval, + formatting_type = formatting_type, + wisca_parameters = as_original_data_class(wisca_parameters, class(x)), + long_numeric = as_original_data_class(long_numeric, class(x))) + rownames(out) <- NULL + out } #' @export diff --git a/R/bug_drug_combinations.R b/R/bug_drug_combinations.R index 2d8b0c6fd..067868a11 100755 --- a/R/bug_drug_combinations.R +++ b/R/bug_drug_combinations.R @@ -185,8 +185,9 @@ bug_drug_combinations <- function(x, out <- as_original_data_class(out, class(x.bak)) # will remove tibble groups out <- out %pm>% pm_arrange(mo, ab) + class(out) <- c("bug_drug_combinations", if(data_has_groups) "grouped" else NULL, class(out)) rownames(out) <- NULL - structure(out, class = c("bug_drug_combinations", if(data_has_groups) "grouped" else NULL, class(out))) + out } #' @method format bug_drug_combinations diff --git a/R/mic.R b/R/mic.R index 1a9a54562..c460f110b 100644 --- a/R/mic.R +++ b/R/mic.R @@ -346,6 +346,21 @@ rescale_mic <- function(x, mic_range, keep_operators = "edges", as.mic = TRUE) { out } +#' @rdname as.mic +#' @details Use [mic_p50()] and [mic_p90()] to get the 50th and 90th percentile of MIC values. They return 'normal' [numeric] values. +#' @export +mic_p50 <- function(x, na.rm = FALSE, ...) { + x <- as.mic(x) + as.double(stats::quantile(x, probs = 0.5, na.rm = na.rm)) +} + +#' @rdname as.mic +#' @export +mic_p90 <- function(x, na.rm = FALSE, ...) { + x <- as.mic(x) + as.double(stats::quantile(x, probs = 0.9, na.rm = na.rm)) +} + #' @method as.double mic #' @export #' @noRd diff --git a/R/plotting.R b/R/plotting.R index 1787cf54f..9e7156f23 100755 --- a/R/plotting.R +++ b/R/plotting.R @@ -186,7 +186,7 @@ #' scale_colour_sir(language = "pt", #' name = "Support in 20 languages") #' } -#' +#' } #' #' # Plotting using base R's plot() --------------------------------------- #' diff --git a/R/sir.R b/R/sir.R index 737b82601..e0d2cb72a 100755 --- a/R/sir.R +++ b/R/sir.R @@ -1544,7 +1544,7 @@ sir_interpretation_history <- function(clean = FALSE) { if (pkg_is_available("tibble")) { out <- import_fn("as_tibble", "tibble")(out) } - structure(out, class = c("sir_log", class(out))) + as_original_data_class(out, class(out), extra_class = "sir_log") } #' @method print sir_log diff --git a/R/sir_calc.R b/R/sir_calc.R index d7de6d89f..9b3c130ca 100755 --- a/R/sir_calc.R +++ b/R/sir_calc.R @@ -383,8 +383,6 @@ sir_calc_df <- function(type, # "proportion", "count" or "both" # remove redundant columns out <- subset(out, select = -c(ci_min, ci_max, isolates)) } - - rownames(out) <- NULL - out <- as_original_data_class(out, class(data.bak)) # will remove tibble groups - structure(out, class = c("sir_df", class(out))) + + as_original_data_class(out, class(data.bak), extra_class = "sir_df") # will remove tibble groups } diff --git a/data-raw/gpt_training_text_v2.1.1.9154.txt b/data-raw/gpt_training_text_v2.1.1.9155.txt similarity index 98% rename from data-raw/gpt_training_text_v2.1.1.9154.txt rename to data-raw/gpt_training_text_v2.1.1.9155.txt index 6d9990a71..ccdfd9f03 100644 --- a/data-raw/gpt_training_text_v2.1.1.9154.txt +++ b/data-raw/gpt_training_text_v2.1.1.9155.txt @@ -1,6 +1,6 @@ This knowledge base contains all context you must know about the AMR package for R. You are a GPT trained to be an assistant for the AMR package in R. You are an incredible R specialist, especially trained in this package and in the tidyverse. -First and foremost, you are trained on version 2.1.1.9154. Remember this whenever someone asks which AMR package version you’re at. +First and foremost, you are trained on version 2.1.1.9155. Remember this whenever someone asks which AMR package version you’re at. Below are the contents of the file, the file, and all the files (documentation) in the package. Every file content is split using 100 hypens. ---------------------------------------------------------------------------------------------------- @@ -262,6 +262,8 @@ export(mdr_cmi2012) export(mdr_tb) export(mdro) export(mean_amr_distance) +export(mic_p50) +export(mic_p90) export(mo_authors) export(mo_class) export(mo_cleaning_regex) @@ -656,30 +658,18 @@ It will be downloaded and installed automatically. For RStudio, click on the men #### Latest development version -[![check-old](https://github.com/msberends/AMR/actions/workflows/check-old.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-old.yaml?query=branch%3Amain) -[![check-recent](https://github.com/msberends/AMR/actions/workflows/check-recent.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-recent.yaml?query=branch%3Amain) +[![check-old](https://github.com/msberends/AMR/actions/workflows/check-old-tinytest.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-old-tinytest.yaml) +[![check-recent](https://github.com/msberends/AMR/actions/workflows/check-current-testthat.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-current-testthat.yaml) [![CodeFactor](https://www.codefactor.io/repository/github/msberends/amr/badge)](https://www.codefactor.io/repository/github/msberends/amr) [![Codecov](https://codecov.io/gh/msberends/AMR/branch/main/graph/badge.svg)](https://codecov.io/gh/msberends/AMR?branch=main) Please read our [Developer Guideline here](https://github.com/msberends/AMR/wiki/Developer-Guideline). -The latest and unpublished development version can be installed from GitHub in two ways: +The latest and unpublished development version can be installed from the [rOpenSci R-universe platform](https://msberends.r-universe.dev/AMR): -1. Manually, using: - - ```r - install.packages("remotes") # if you haven't already - remotes::install_github("msberends/AMR") - ``` - -2. Automatically, using the [rOpenSci R-universe platform](https://ropensci.org/r-universe/), by adding [our R-universe address](https://msberends.r-universe.dev) to your list of repositories ('repos'): - - ```r - options(repos = c(getOption("repos"), - msberends = "https://msberends.r-universe.dev")) - ``` - - After this, you can install and update this `AMR` package like any official release (e.g., using `install.packages("AMR")` or in RStudio via *Tools* > *Check for Package Updates...*). +```r +install.packages("AMR", repos = "https://msberends.r-universe.dev") +``` ### Get started @@ -2873,6 +2863,8 @@ THE PART HEREAFTER CONTAINS CONTENTS FROM FILE 'man/as.mic.Rd': \alias{is.mic} \alias{NA_mic_} \alias{rescale_mic} +\alias{mic_p50} +\alias{mic_p90} \alias{droplevels.mic} \title{Transform Input to Minimum Inhibitory Concentrations (MIC)} \usage{ @@ -2884,6 +2876,10 @@ NA_mic_ rescale_mic(x, mic_range, keep_operators = "edges", as.mic = TRUE) +mic_p50(x, na.rm = FALSE, ...) + +mic_p90(x, na.rm = FALSE, ...) + \method{droplevels}{mic}(x, as.mic = FALSE, ...) } \arguments{ @@ -2953,6 +2949,8 @@ With \code{\link[=rescale_mic]{rescale_mic()}}, existing MIC ranges can be limit For \code{ggplot2}, use one of the \code{\link[=scale_x_mic]{scale_*_mic()}} functions to plot MIC values. They allows custom MIC ranges and to plot intermediate log2 levels for missing MIC values. \code{NA_mic_} is a missing value of the new \code{mic} class, analogous to e.g. base \R's \code{\link[base:NA]{NA_character_}}. + +Use \code{\link[=mic_p50]{mic_p50()}} and \code{\link[=mic_p90]{mic_p90()}} to get the 50th and 90th percentile of MIC values. They return 'normal' \link{numeric} values. } \examples{ mic_data <- as.mic(c(">=32", "1.0", "1", "1.00", 8, "<=0.128", "8", "16", "16")) @@ -7540,6 +7538,131 @@ The interpretation of "I" will be named "Increased exposure" for all EUCAST guid For interpreting MIC values as well as disk diffusion diameters, the default guideline is EUCAST 2024, unless the package option \code{\link[=AMR-options]{AMR_guideline}} is set. See \code{\link[=as.sir]{as.sir()}} for more information. } } +\examples{ +some_mic_values <- random_mic(size = 100) +some_disk_values <- random_disk(size = 100, mo = "Escherichia coli", ab = "cipro") +some_sir_values <- random_sir(50, prob_SIR = c(0.55, 0.05, 0.30)) + + +\donttest{ +# Plotting using ggplot2's autoplot() for MIC, disk, and SIR ----------- +if (require("ggplot2")) { + autoplot(some_mic_values) +} +if (require("ggplot2")) { + # when providing the microorganism and antibiotic, colours will show interpretations: + autoplot(some_mic_values, mo = "Escherichia coli", ab = "cipro") +} +if (require("ggplot2")) { + # support for 20 languages, various guidelines, and many options + autoplot(some_disk_values, mo = "Escherichia coli", ab = "cipro", + guideline = "CLSI 2024", language = "no", + title = "Disk diffusion from the North") +} + + +# Plotting using scale_x_mic() ----------------------------------------- +if (require("ggplot2")) { + mic_plot <- ggplot(data.frame(mics = as.mic(c(0.25, "<=4", 4, 8, 32, ">=32")), + counts = c(1, 1, 2, 2, 3, 3)), + aes(mics, counts)) + + geom_col() + mic_plot + + labs(title = "without scale_x_mic()") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic() + + labs(title = "with scale_x_mic()") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(keep_operators = "all") + + labs(title = "with scale_x_mic() keeping all operators") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(mic_range = c(1, 16)) + + labs(title = "with scale_x_mic() using a manual 'within' range") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(mic_range = c(0.032, 256)) + + labs(title = "with scale_x_mic() using a manual 'outside' range") +} + + +# Plotting using scale_y_mic() ----------------------------------------- +some_groups <- sample(LETTERS[1:5], 20, replace = TRUE) + +if (require("ggplot2")) { + ggplot(data.frame(mic = some_mic_values, + group = some_groups), + aes(group, mic)) + + geom_boxplot() + + geom_violin(linetype = 2, colour = "grey", fill = NA) + + scale_y_mic() +} +if (require("ggplot2")) { + ggplot(data.frame(mic = some_mic_values, + group = some_groups), + aes(group, mic)) + + geom_boxplot() + + geom_violin(linetype = 2, colour = "grey", fill = NA) + + scale_y_mic(mic_range = c(NA, 0.25)) +} + + +# Plotting using scale_x_sir() ----------------------------------------- +if (require("ggplot2")) { + ggplot(data.frame(x = c("I", "R", "S"), + y = c(45,323, 573)), + aes(x, y)) + + geom_col() + + scale_x_sir() +} + + +# Plotting using scale_y_mic() and scale_colour_sir() ------------------ +if (require("ggplot2")) { + plain <- ggplot(data.frame(mic = some_mic_values, + group = some_groups, + sir = as.sir(some_mic_values, + mo = "E. coli", + ab = "cipro")), + aes(x = group, y = mic, colour = sir)) + + theme_minimal() + + geom_boxplot(fill = NA, colour = "grey") + + geom_jitter(width = 0.25) + + plain +} +if (require("ggplot2")) { + # and now with our MIC and SIR scale functions: + plain + + scale_y_mic() + + scale_colour_sir() +} +if (require("ggplot2")) { + plain + + scale_y_mic(mic_range = c(0.005, 32), name = "Our MICs!") + + scale_colour_sir(language = "pt", + name = "Support in 20 languages") +} +} + +# Plotting using base R's plot() --------------------------------------- + +plot(some_mic_values) +# when providing the microorganism and antibiotic, colours will show interpretations: +plot(some_mic_values, mo = "S. aureus", ab = "ampicillin") + +plot(some_disk_values) +plot(some_disk_values, mo = "Escherichia coli", ab = "cipro") +plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") + +plot(some_sir_values) +} @@ -8516,8 +8639,10 @@ antibiogram(example_isolates, To create a combined antibiogram, use antibiotic codes or names with a plus `+` character like this: ```{r comb} -antibiogram(example_isolates, - antibiotics = c("TZP", "TZP+TOB", "TZP+GEN")) +combined_ab <- antibiogram(example_isolates, + antibiotics = c("TZP", "TZP+TOB", "TZP+GEN"), + ab_transform = NULL) +combined_ab ``` ### Syndromic Antibiogram @@ -8532,17 +8657,26 @@ antibiogram(example_isolates, ### Weighted-Incidence Syndromic Combination Antibiogram (WISCA) -To create a WISCA, you must state combination therapy in the `antibiotics` argument (similar to the Combination Antibiogram), define a syndromic group with the `syndromic_group` argument (similar to the Syndromic Antibiogram) in which cases are predefined based on clinical or demographic characteristics (e.g., endocarditis in 75+ females). This next example is a simplification without clinical characteristics, but just gives an idea of how a WISCA can be created: +To create a **Weighted-Incidence Syndromic Combination Antibiogram (WISCA)**, simply set `wisca = TRUE` in the `antibiogram()` function, or use the dedicated `wisca()` function. Unlike traditional antibiograms, WISCA provides syndrome-based susceptibility estimates, weighted by pathogen incidence and antimicrobial susceptibility patterns. ```{r wisca} -wisca <- antibiogram(example_isolates, - antibiotics = c("AMC", "AMC+CIP", "TZP", "TZP+TOB"), - mo_transform = "gramstain", - minimum = 10, # this should be >= 30, but now just as example - syndromic_group = ifelse(example_isolates$age >= 65 & - example_isolates$gender == "M", - "WISCA Group 1", "WISCA Group 2")) -wisca +example_isolates %>% + wisca(antibiotics = c("TZP", "TZP+TOB", "TZP+GEN"), + minimum = 10) # Recommended threshold: ≥30 +``` + +WISCA uses a **Bayesian decision model** to integrate data from multiple pathogens, improving empirical therapy guidance, especially for low-incidence infections. It is **pathogen-agnostic**, meaning results are syndrome-based rather than stratified by microorganism. + +For reliable results, ensure your data includes **only first isolates** (use `first_isolate()`) and consider filtering for **the top *n* species** (use `top_n_microorganisms()`), as WISCA outcomes are most meaningful when based on robust incidence estimates. + +For **patient- or syndrome-specific WISCA**, run the function on a grouped `tibble`, i.e., using `group_by()` first: + +```{r wisca_grouped} +example_isolates %>% + top_n_microorganisms(n = 10) %>% + group_by(age_group = age_groups(age, c(25, 50, 75)), + gender) %>% + wisca(antibiotics = c("TZP", "TZP+TOB", "TZP+GEN")) ``` ### Plotting antibiograms @@ -8550,7 +8684,7 @@ wisca Antibiograms can be plotted using `autoplot()` from the `ggplot2` packages, since this `AMR` package provides an extension to that function: ```{r} -autoplot(wisca) +autoplot(combined_ab) ``` To calculate antimicrobial resistance in a more sensible way, also by correcting for too few results, we use the `resistance()` and `susceptibility()` functions. @@ -8575,9 +8709,54 @@ our_data_1st %>% summarise(amoxicillin = resistance(AMX)) ``` +## Interpreting MIC and Disk Diffusion Values + +Minimal inhibitory concentration (MIC) values and disk diffusion diameters can be interpreted into clinical breakpoints (SIR) using `as.sir()`. Here’s an example with randomly generated MIC values for *Klebsiella pneumoniae* and ciprofloxacin: + +```{r mic_interpretation} +set.seed(123) +mic_values <- random_mic(100) +sir_values <- as.sir(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST 2024") + +my_data <- tibble(MIC = mic_values, SIR = sir_values) +my_data +``` + +This allows direct interpretation according to EUCAST or CLSI breakpoints, facilitating automated AMR data processing. + +## Plotting MIC and SIR Interpretations + +We can visualise MIC distributions and their SIR interpretations using `ggplot2`, using the new `scale_y_mic()` for the y-axis and `scale_colour_sir()` to colour-code SIR categories. + +```{r mic_plot} +# add a group +my_data$group <- rep(c("A", "B", "C", "D"), each = 25) + +ggplot(my_data, + aes(x = group, y = MIC, colour = SIR)) + + geom_jitter(width = 0.2, size = 2) + + geom_boxplot(fill = NA, colour = "grey40") + + scale_y_mic() + + scale_colour_sir() + + labs(title = "MIC Distribution and SIR Interpretation", + x = "Sample Groups", + y = "MIC (mg/L)") +``` + +This plot provides an intuitive way to assess susceptibility patterns across different groups while incorporating clinical breakpoints. + +For a more straightforward and less manual approach, `ggplot2`'s function `autoplot()` has been extended by this package to directly plot MIC and disk diffusion values: + +```{r autoplot} +autoplot(mic_values) + +# by providing `mo` and `ab`, colours will indicate the SIR interpretation: +autoplot(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST 2024") +``` + ---- -*Author: Dr. Matthijs Berends, 26th Feb 2023* +*Author: Dr. Matthijs Berends, 23rd Feb 2025* diff --git a/index.md b/index.md index 810324f3b..1dfddd85f 100644 --- a/index.md +++ b/index.md @@ -266,30 +266,18 @@ It will be downloaded and installed automatically. For RStudio, click on the men #### Latest development version -[![check-old](https://github.com/msberends/AMR/actions/workflows/check-old.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-old.yaml?query=branch%3Amain) -[![check-recent](https://github.com/msberends/AMR/actions/workflows/check-recent.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-recent.yaml?query=branch%3Amain) +[![check-old](https://github.com/msberends/AMR/actions/workflows/check-old-tinytest.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-old-tinytest.yaml) +[![check-recent](https://github.com/msberends/AMR/actions/workflows/check-current-testthat.yaml/badge.svg?branch=main)](https://github.com/msberends/AMR/actions/workflows/check-current-testthat.yaml) [![CodeFactor](https://www.codefactor.io/repository/github/msberends/amr/badge)](https://www.codefactor.io/repository/github/msberends/amr) [![Codecov](https://codecov.io/gh/msberends/AMR/branch/main/graph/badge.svg)](https://codecov.io/gh/msberends/AMR?branch=main) Please read our [Developer Guideline here](https://github.com/msberends/AMR/wiki/Developer-Guideline). -The latest and unpublished development version can be installed from GitHub in two ways: +The latest and unpublished development version can be installed from the [rOpenSci R-universe platform](https://msberends.r-universe.dev/AMR): -1. Manually, using: - - ```r - install.packages("remotes") # if you haven't already - remotes::install_github("msberends/AMR") - ``` - -2. Automatically, using the [rOpenSci R-universe platform](https://ropensci.org/r-universe/), by adding [our R-universe address](https://msberends.r-universe.dev) to your list of repositories ('repos'): - - ```r - options(repos = c(getOption("repos"), - msberends = "https://msberends.r-universe.dev")) - ``` - - After this, you can install and update this `AMR` package like any official release (e.g., using `install.packages("AMR")` or in RStudio via *Tools* > *Check for Package Updates...*). +```r +install.packages("AMR", repos = "https://msberends.r-universe.dev") +``` ### Get started diff --git a/man/as.mic.Rd b/man/as.mic.Rd index 5d8c6dbe2..6f6c03bb0 100644 --- a/man/as.mic.Rd +++ b/man/as.mic.Rd @@ -7,6 +7,8 @@ \alias{is.mic} \alias{NA_mic_} \alias{rescale_mic} +\alias{mic_p50} +\alias{mic_p90} \alias{droplevels.mic} \title{Transform Input to Minimum Inhibitory Concentrations (MIC)} \usage{ @@ -18,6 +20,10 @@ NA_mic_ rescale_mic(x, mic_range, keep_operators = "edges", as.mic = TRUE) +mic_p50(x, na.rm = FALSE, ...) + +mic_p90(x, na.rm = FALSE, ...) + \method{droplevels}{mic}(x, as.mic = FALSE, ...) } \arguments{ @@ -87,6 +93,8 @@ With \code{\link[=rescale_mic]{rescale_mic()}}, existing MIC ranges can be limit For \code{ggplot2}, use one of the \code{\link[=scale_x_mic]{scale_*_mic()}} functions to plot MIC values. They allows custom MIC ranges and to plot intermediate log2 levels for missing MIC values. \code{NA_mic_} is a missing value of the new \code{mic} class, analogous to e.g. base \R's \code{\link[base:NA]{NA_character_}}. + +Use \code{\link[=mic_p50]{mic_p50()}} and \code{\link[=mic_p90]{mic_p90()}} to get the 50th and 90th percentile of MIC values. They return 'normal' \link{numeric} values. } \examples{ mic_data <- as.mic(c(">=32", "1.0", "1", "1.00", 8, "<=0.128", "8", "16", "16")) diff --git a/man/plot.Rd b/man/plot.Rd index c3e175cd2..f7d74aaf5 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -196,3 +196,128 @@ The interpretation of "I" will be named "Increased exposure" for all EUCAST guid For interpreting MIC values as well as disk diffusion diameters, the default guideline is EUCAST 2024, unless the package option \code{\link[=AMR-options]{AMR_guideline}} is set. See \code{\link[=as.sir]{as.sir()}} for more information. } } +\examples{ +some_mic_values <- random_mic(size = 100) +some_disk_values <- random_disk(size = 100, mo = "Escherichia coli", ab = "cipro") +some_sir_values <- random_sir(50, prob_SIR = c(0.55, 0.05, 0.30)) + + +\donttest{ +# Plotting using ggplot2's autoplot() for MIC, disk, and SIR ----------- +if (require("ggplot2")) { + autoplot(some_mic_values) +} +if (require("ggplot2")) { + # when providing the microorganism and antibiotic, colours will show interpretations: + autoplot(some_mic_values, mo = "Escherichia coli", ab = "cipro") +} +if (require("ggplot2")) { + # support for 20 languages, various guidelines, and many options + autoplot(some_disk_values, mo = "Escherichia coli", ab = "cipro", + guideline = "CLSI 2024", language = "no", + title = "Disk diffusion from the North") +} + + +# Plotting using scale_x_mic() ----------------------------------------- +if (require("ggplot2")) { + mic_plot <- ggplot(data.frame(mics = as.mic(c(0.25, "<=4", 4, 8, 32, ">=32")), + counts = c(1, 1, 2, 2, 3, 3)), + aes(mics, counts)) + + geom_col() + mic_plot + + labs(title = "without scale_x_mic()") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic() + + labs(title = "with scale_x_mic()") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(keep_operators = "all") + + labs(title = "with scale_x_mic() keeping all operators") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(mic_range = c(1, 16)) + + labs(title = "with scale_x_mic() using a manual 'within' range") +} +if (require("ggplot2")) { + mic_plot + + scale_x_mic(mic_range = c(0.032, 256)) + + labs(title = "with scale_x_mic() using a manual 'outside' range") +} + + +# Plotting using scale_y_mic() ----------------------------------------- +some_groups <- sample(LETTERS[1:5], 20, replace = TRUE) + +if (require("ggplot2")) { + ggplot(data.frame(mic = some_mic_values, + group = some_groups), + aes(group, mic)) + + geom_boxplot() + + geom_violin(linetype = 2, colour = "grey", fill = NA) + + scale_y_mic() +} +if (require("ggplot2")) { + ggplot(data.frame(mic = some_mic_values, + group = some_groups), + aes(group, mic)) + + geom_boxplot() + + geom_violin(linetype = 2, colour = "grey", fill = NA) + + scale_y_mic(mic_range = c(NA, 0.25)) +} + + +# Plotting using scale_x_sir() ----------------------------------------- +if (require("ggplot2")) { + ggplot(data.frame(x = c("I", "R", "S"), + y = c(45,323, 573)), + aes(x, y)) + + geom_col() + + scale_x_sir() +} + + +# Plotting using scale_y_mic() and scale_colour_sir() ------------------ +if (require("ggplot2")) { + plain <- ggplot(data.frame(mic = some_mic_values, + group = some_groups, + sir = as.sir(some_mic_values, + mo = "E. coli", + ab = "cipro")), + aes(x = group, y = mic, colour = sir)) + + theme_minimal() + + geom_boxplot(fill = NA, colour = "grey") + + geom_jitter(width = 0.25) + + plain +} +if (require("ggplot2")) { + # and now with our MIC and SIR scale functions: + plain + + scale_y_mic() + + scale_colour_sir() +} +if (require("ggplot2")) { + plain + + scale_y_mic(mic_range = c(0.005, 32), name = "Our MICs!") + + scale_colour_sir(language = "pt", + name = "Support in 20 languages") +} +} + +# Plotting using base R's plot() --------------------------------------- + +plot(some_mic_values) +# when providing the microorganism and antibiotic, colours will show interpretations: +plot(some_mic_values, mo = "S. aureus", ab = "ampicillin") + +plot(some_disk_values) +plot(some_disk_values, mo = "Escherichia coli", ab = "cipro") +plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") + +plot(some_sir_values) +} diff --git a/tests/testthat/test-mic.R b/tests/testthat/test-mic.R index fcbee903c..cabe6f81c 100755 --- a/tests/testthat/test-mic.R +++ b/tests/testthat/test-mic.R @@ -28,9 +28,9 @@ # ==================================================================== # # used in multiple functions, also in plotting -expect_true(all(as.mic(COMMON_MIC_VALUES) %in% VALID_MIC_LEVELS)) -expect_true(all(paste0("<=", as.mic(COMMON_MIC_VALUES)) %in% VALID_MIC_LEVELS)) -expect_true(all(paste0(">=", as.mic(COMMON_MIC_VALUES)) %in% VALID_MIC_LEVELS)) +expect_true(all(as.mic(AMR:::COMMON_MIC_VALUES) %in% AMR:::VALID_MIC_LEVELS)) +expect_true(all(paste0("<=", as.mic(AMR:::COMMON_MIC_VALUES)) %in% AMR:::VALID_MIC_LEVELS)) +expect_true(all(paste0(">=", as.mic(AMR:::COMMON_MIC_VALUES)) %in% AMR:::VALID_MIC_LEVELS)) expect_true(as.mic(8) == as.mic("8")) expect_true(as.mic("1") > as.mic("<=0.0625")) diff --git a/vignettes/AMR.Rmd b/vignettes/AMR.Rmd index bcc4e80cb..390557c1f 100755 --- a/vignettes/AMR.Rmd +++ b/vignettes/AMR.Rmd @@ -288,8 +288,10 @@ antibiogram(example_isolates, To create a combined antibiogram, use antibiotic codes or names with a plus `+` character like this: ```{r comb} -antibiogram(example_isolates, - antibiotics = c("TZP", "TZP+TOB", "TZP+GEN")) +combined_ab <- antibiogram(example_isolates, + antibiotics = c("TZP", "TZP+TOB", "TZP+GEN"), + ab_transform = NULL) +combined_ab ``` ### Syndromic Antibiogram @@ -304,17 +306,26 @@ antibiogram(example_isolates, ### Weighted-Incidence Syndromic Combination Antibiogram (WISCA) -To create a WISCA, you must state combination therapy in the `antibiotics` argument (similar to the Combination Antibiogram), define a syndromic group with the `syndromic_group` argument (similar to the Syndromic Antibiogram) in which cases are predefined based on clinical or demographic characteristics (e.g., endocarditis in 75+ females). This next example is a simplification without clinical characteristics, but just gives an idea of how a WISCA can be created: +To create a **Weighted-Incidence Syndromic Combination Antibiogram (WISCA)**, simply set `wisca = TRUE` in the `antibiogram()` function, or use the dedicated `wisca()` function. Unlike traditional antibiograms, WISCA provides syndrome-based susceptibility estimates, weighted by pathogen incidence and antimicrobial susceptibility patterns. ```{r wisca} -wisca <- antibiogram(example_isolates, - antibiotics = c("AMC", "AMC+CIP", "TZP", "TZP+TOB"), - mo_transform = "gramstain", - minimum = 10, # this should be >= 30, but now just as example - syndromic_group = ifelse(example_isolates$age >= 65 & - example_isolates$gender == "M", - "WISCA Group 1", "WISCA Group 2")) -wisca +example_isolates %>% + wisca(antibiotics = c("TZP", "TZP+TOB", "TZP+GEN"), + minimum = 10) # Recommended threshold: ≥30 +``` + +WISCA uses a **Bayesian decision model** to integrate data from multiple pathogens, improving empirical therapy guidance, especially for low-incidence infections. It is **pathogen-agnostic**, meaning results are syndrome-based rather than stratified by microorganism. + +For reliable results, ensure your data includes **only first isolates** (use `first_isolate()`) and consider filtering for **the top *n* species** (use `top_n_microorganisms()`), as WISCA outcomes are most meaningful when based on robust incidence estimates. + +For **patient- or syndrome-specific WISCA**, run the function on a grouped `tibble`, i.e., using `group_by()` first: + +```{r wisca_grouped} +example_isolates %>% + top_n_microorganisms(n = 10) %>% + group_by(age_group = age_groups(age, c(25, 50, 75)), + gender) %>% + wisca(antibiotics = c("TZP", "TZP+TOB", "TZP+GEN")) ``` ### Plotting antibiograms @@ -322,7 +333,7 @@ wisca Antibiograms can be plotted using `autoplot()` from the `ggplot2` packages, since this `AMR` package provides an extension to that function: ```{r} -autoplot(wisca) +autoplot(combined_ab) ``` To calculate antimicrobial resistance in a more sensible way, also by correcting for too few results, we use the `resistance()` and `susceptibility()` functions. @@ -347,6 +358,51 @@ our_data_1st %>% summarise(amoxicillin = resistance(AMX)) ``` +## Interpreting MIC and Disk Diffusion Values + +Minimal inhibitory concentration (MIC) values and disk diffusion diameters can be interpreted into clinical breakpoints (SIR) using `as.sir()`. Here’s an example with randomly generated MIC values for *Klebsiella pneumoniae* and ciprofloxacin: + +```{r mic_interpretation} +set.seed(123) +mic_values <- random_mic(100) +sir_values <- as.sir(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST 2024") + +my_data <- tibble(MIC = mic_values, SIR = sir_values) +my_data +``` + +This allows direct interpretation according to EUCAST or CLSI breakpoints, facilitating automated AMR data processing. + +## Plotting MIC and SIR Interpretations + +We can visualise MIC distributions and their SIR interpretations using `ggplot2`, using the new `scale_y_mic()` for the y-axis and `scale_colour_sir()` to colour-code SIR categories. + +```{r mic_plot} +# add a group +my_data$group <- rep(c("A", "B", "C", "D"), each = 25) + +ggplot(my_data, + aes(x = group, y = MIC, colour = SIR)) + + geom_jitter(width = 0.2, size = 2) + + geom_boxplot(fill = NA, colour = "grey40") + + scale_y_mic() + + scale_colour_sir() + + labs(title = "MIC Distribution and SIR Interpretation", + x = "Sample Groups", + y = "MIC (mg/L)") +``` + +This plot provides an intuitive way to assess susceptibility patterns across different groups while incorporating clinical breakpoints. + +For a more straightforward and less manual approach, `ggplot2`'s function `autoplot()` has been extended by this package to directly plot MIC and disk diffusion values: + +```{r autoplot} +autoplot(mic_values) + +# by providing `mo` and `ab`, colours will indicate the SIR interpretation: +autoplot(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST 2024") +``` + ---- -*Author: Dr. Matthijs Berends, 26th Feb 2023* +*Author: Dr. Matthijs Berends, 23rd Feb 2025*