Compare commits

22 Commits
Author SHA1 Message Date
dr. M.S. (Matthijs) Berends 4547a87a88 (v3.1.0.9022) Document grouped first isolates (#345) 2026-11-14 22:39:38 +01:00
dr. M.S. (Matthijs) Berends 49a0f37713 (v3.1.0.9021) Show the number of tested isolates on susceptible-only bars, and split ggplot_sir() by antimicrobial if it is not shown in the plot 2026-11-09 22:10:01 +01:00
dr. M.S. (Matthijs) Berends 035c7e7871 (v3.1.0.9020) Plot only the susceptible proportion in ggplot_sir() and geom_sir() if the interpretation is not shown, following the guideline 2026-11-09 21:57:55 +01:00
dr. M.S. (Matthijs) Berends 054066b855 (v3.1.0.9019) Fix the data labels of labels_sir_count() being placed on the wrong bars with position = "dodge" or "stack" 2026-11-09 21:52:40 +01:00
dr. M.S. (Matthijs) Berends 77b6cb1713 (v3.1.0.9018) Italicise taxonomic names in all plotting functions using labels_taxonomy(), and fix ggplot_pca() with explicit groups 2026-11-09 21:28:37 +01:00
dr. M.S. (Matthijs) Berends cfb55a4c3e (v3.1.0.9017) Add labels_taxonomy() for taxonomic names in italics in plot labels, add type = "plotmath" to italicise_taxonomy(), and only italicise species names after a genus 2026-11-09 20:45:07 +01:00
Matthijs Berends db7377f517 Merge pull request #344 from msberends/salmonella-serovars
Return the serovar in mo_shortname() for Salmonella, and apply the Typhi rule in mdro()
2026-11-04 08:07:17 +01:00
dr. M.S. (Matthijs) Berends 3b8b213616 Return the serovar in mo_shortname() for Salmonella, keep Salmonella groups apart from streptococci, abbreviate all Lancefield groups, and apply the Typhi rule in mdro() (#341) 2026-11-04 07:57:20 +01:00
dr. M.S. (Matthijs) Berends 08fd165a60 (v3.1.0.9014) Fix the CI: replace a test unsupported by tinytest, run R 3.0 on Ubuntu 24.04, and use Python 3.12 for the PyPI package, since ubuntu-latest moved to Ubuntu 26.04 2026-10-30 14:55:00 +01:00
dr. M.S. (Matthijs) Berends 7f3c75ece7 (v3.1.0.9013) Add species groups and complexes to the intrinsic_resistant data set for every antimicrobial to which all their species are intrinsically resistant (fixes #338) 2026-10-30 13:37:25 +01:00
dr. M.S. (Matthijs) Berends aab0fcc479 (v3.1.0.9012) Use the native R pipe throughout the examples on the website home page, and fix a typo 2026-10-30 12:52:30 +01:00
dr. M.S. (Matthijs) Berends 944078c4bc (v3.1.0.9011) Use top_n_microorganisms() in the WISCA examples of the index, manual and vignette, and reword the WISCA prior weight warning to also cover small groups (#337) 2026-10-30 12:45:56 +01:00
dr. M.S. (Matthijs) Berends fdfdadae14 (v3.1.0.9010) Fix the systematic underestimation of WISCA coverage by only including pathogens that occur in a group and were tested for the regimen, and warn when the prior weight of rare pathogens is large (fixes #337) 2026-10-30 12:35:12 +01:00
dr. M.S. (Matthijs) Berends 92bb8931c5 (v3.1.0.9009) Fix the misaligned arrows in the message on taxonomically renamed microorganisms in colour-supporting consoles, by padding on the visible width instead of using format() (fixes #336) 2026-10-30 09:59:01 +01:00
Matthijs Berends 08e68037db Merge pull request #333 from uzumakienes/uzumakienes-patch-1
Fix copyright link and add MycoBank to the welcome vignette
2026-10-23 19:42:19 +02:00
uzumakienes 904656241f Bump version and add NEWS entry 2026-10-23 16:46:42 +03:00
uzumakienes da44c3a569 Fix copyright link and add MycoBank in vignette 2026-10-23 16:46:27 +03:00
dr. M.S. (Matthijs) Berends 4e806b3d40 (v3.1.0.9005) Fix the CI check on R-oldrel-5 by letting pak ignore readxl on R versions older than 4.2, and by not forcing suggested packages in R CMD check 2026-10-23 14:15:49 +02:00
dr. M.S. (Matthijs) BerendsandClaude Sonnet 5.5 5fc152ae94 (v3.1.0.9004) Document the columns argument of the tidymodels steps to fix the Rd usage warning in R CMD check
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 18:23:06 +02:00
dr. M.S. (Matthijs) Berends bd208429cd (v3.1.0.9003) Rewrite the manual index on the website for findability: sections ordered by task (cleaning data, interpreting results as SIR, analysing data), titles before function names, a filter field that also finds topics mentioning a term, and colours prepared for dark mode; also make the tidymodels extensions public in the manual and use consistent help page titles 2026-10-17 09:36:10 +02:00
dr. M.S. (Matthijs) Berends 52d9c4a9e7 (v3.1.0.9002) Add a release checklist (release.md) with all steps of a regular CRAN release 2026-10-16 14:23:01 +02:00
dr. M.S. (Matthijs) Berends 5a4387a141 (v3.1.0.9001) Update the MO code registry with the codes of release v3.1.0, and the versions in the bug report template 2026-10-16 14:19:48 +02:00
69 changed files with 104705 additions and 73548 deletions

No files matched your search

+1
View File
@@ -12,6 +12,7 @@
^CLAUDE\.md$ ^CLAUDE\.md$
^\.claude$ ^\.claude$
^cran-comments\.md$ ^cran-comments\.md$
^release\.md$
^CRAN-RELEASE$ ^CRAN-RELEASE$
index\.html index\.html
README\.html README\.html
+2 -2
View File
@@ -42,7 +42,7 @@ body:
multiple: false multiple: false
options: options:
- '' - ''
- Latest CRAN version (3.0.1) - Latest CRAN version (3.1.0)
- One of the latest GitHub versions (3.0.1.9xxx) - One of the latest GitHub versions (3.1.0.9xxx)
validations: validations:
required: true required: true
@@ -97,7 +97,8 @@ jobs:
- uses: r-lib/actions/setup-r-dependencies@v2 - uses: r-lib/actions/setup-r-dependencies@v2
with: with:
extra-packages: ${{ matrix.config.tests && 'any::pkgload' || 'any::rcmdcheck' }} # pak solves all suggested packages as one set and fails as a whole if one of them requires a newer R, so such packages are ignored on older R versions (with `pkg=?ignore-before-r=x.y.z`) and the checks run without them
extra-packages: ${{ matrix.config.tests && 'any::pkgload' || 'any::rcmdcheck' }} readxl=?ignore-before-r=4.2.0
needs: check needs: check
upgrade: 'TRUE' upgrade: 'TRUE'
@@ -120,6 +121,9 @@ jobs:
- uses: r-lib/actions/check-r-package@v2 - uses: r-lib/actions/check-r-package@v2
if: ${{ !matrix.config.tests }} if: ${{ !matrix.config.tests }}
env:
# suggested packages ignored on older R versions (see above) must not fail the check
_R_CHECK_FORCE_SUGGESTS_: false
with: with:
upload-snapshots: true upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
+2 -1
View File
@@ -57,7 +57,8 @@ jobs:
# - {os: ubuntu-latest, r: '3.3', allowfail: false} # - {os: ubuntu-latest, r: '3.3', allowfail: false}
# - {os: ubuntu-latest, r: '3.2', allowfail: false} # - {os: ubuntu-latest, r: '3.2', allowfail: false}
# - {os: ubuntu-latest, r: '3.1', allowfail: false} # - {os: ubuntu-latest, r: '3.1', allowfail: false}
- {os: ubuntu-latest, r: '3.0', allowfail: false} # R 3.0 cannot be resolved for ubuntu-latest (Ubuntu 26.04), so use Ubuntu 24.04
- {os: ubuntu-24.04, r: '3.0', allowfail: false}
env: env:
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
+2 -1
View File
@@ -48,7 +48,8 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.9' # Python 3.9 is not available for ubuntu-latest (Ubuntu 26.04)
python-version: '3.12'
- name: Install build dependencies - name: Install build dependencies
run: | run: |
+3 -3
View File
@@ -1,6 +1,6 @@
Package: AMR Package: AMR
Version: 3.1.0 Version: 3.1.0.9022
Date: 2026-10-16 Date: 2026-11-14
Title: Antimicrobial Resistance Data Analysis Title: Antimicrobial Resistance Data Analysis
Description: Functions to simplify and standardise antimicrobial resistance (AMR) Description: Functions to simplify and standardise antimicrobial resistance (AMR)
data analysis and to work with microbial and antimicrobial properties by data analysis and to work with microbial and antimicrobial properties by
@@ -73,4 +73,4 @@ License: GPL-2 | file LICENSE
Encoding: UTF-8 Encoding: UTF-8
LazyData: true LazyData: true
Roxygen: list(markdown = TRUE) Roxygen: list(markdown = TRUE)
Config/roxygen2/version: 8.1.0 Config/roxygen2/version: 8.1.1
+1
View File
@@ -270,6 +270,7 @@ export(italicize_taxonomy)
export(key_antimicrobials) export(key_antimicrobials)
export(kurtosis) export(kurtosis)
export(labels_sir_count) export(labels_sir_count)
export(labels_taxonomy)
export(left_join_microorganisms) export(left_join_microorganisms)
export(like) export(like)
export(lincosamides) export(lincosamides)
+41
View File
@@ -1,3 +1,44 @@
# AMR 3.1.0.9022
This version fixes a critical bug in WISCA that biased all coverage estimates of `wisca()` and `antibiogram(..., wisca = TRUE)` downwards since AMR 3.0.0. Users are strongly advised to re-run their WISCA analyses.
### Breaking
* `mo_shortname()` now returns the serovar for *Salmonella* serovars, e.g., `"S. Typhi"` and `"S. Goldcoast"` instead of `"S. enterica"`. As this is the default for `bug_drug_combinations()` and short names in `antibiogram()`, serovars are no longer merged in their results. Use `FUN = function(x) paste(mo_genus(x), mo_species(x))` in `bug_drug_combinations()` to group them by species (#341)
* `mo_shortname()` now returns e.g. `"S. Group B"` for *Salmonella* groups instead of the abbreviations of streptococci, such as `"GBS"`, so that they are no longer merged with streptococci in `bug_drug_combinations()` and `antibiogram()`. *Streptococcus* groups E, I, J and L are now also abbreviated, e.g., `"GES"`, and `"B_STRPT_ABCG"` now returns `"S. Group A, B, C, G"` instead of the malformed `"GA, B, C, GS"` (#341)
* `mdro()` now applies the rule for *Salmonella* Typhi in the EUCAST guidelines, i.e., resistance to carbapenems (and fluoroquinolones in `"EUCAST3.1"`). This rule never applied before, so these isolates were wrongly classified as negative (#341)
### New
* **Taxonomic names in plots**
* New function `labels_taxonomy()` to print taxonomic names in axis and legend labels in italics, which can directly be used as labeller in `ggplot2` (e.g., `scale_x_discrete(labels = labels_taxonomy)`) and in base R plots
* `italicise_taxonomy()` gained `type = "plotmath"`, which returns an expression vector for titles, axes and legends in plots
* All plotting functions now print taxonomic names in italics using `labels_taxonomy()`: the facets and titles of `autoplot()` and `plot()` for antibiograms, the legend of `wisca_plot()` (which previously italicised all its entries, including e.g. "CoNS"), the axes, legends and facets of `ggplot_sir()` and `facet_sir()`, the labels and legend of `ggplot_pca()`, and the subtitles of `plot()` and `autoplot()` for MIC and disk diffusion values (which previously italicised the whole name, e.g. "Coagulase-negative Staphylococcus (CoNS)")
### Changed
* `italicise_taxonomy()`
* Species and subspecies names are now only italicised after a genus, an abbreviated genus, or another species or subspecies name, so that ordinary words such as "coli" or "major" stay upright. *Salmonella* serovars (such as Typhimurium) and groups are no longer italicised, and "subsp.", "ssp." and "var." stay upright between italicised names
* Fixed punctuation being italicised along with taxonomic names, e.g., `"(S. aureus)"` now gives `"(*S. aureus*)"` instead of `"(S. *aureus)*"`
* `NA` values now return `NA` instead of `"NA"`
* Taxonomic names are now looked up once per session, making the function considerably faster for long vectors
* `ggplot_sir()`, `geom_sir()` and `labels_sir_count()`
* Fixed the data labels being placed on the wrong bars with `position = "dodge"` or `position = "stack"`. They are now grouped like the bars, using the new `fill` argument of `labels_sir_count()`, which `ggplot_sir()` sets automatically
* If the interpretation is not shown in the plot, e.g., with `fill = "species"`, only the susceptible proportion is now plotted, following the guideline: S+SDD+I for EUCAST, S+SDD for CLSI, and S with `guideline = NULL`. Previously, the bars of all interpretations were drawn on top of each other. The y axis title of `ggplot_sir()` then states the category, e.g., "Proportion SI", and the data labels show the number of tested isolates
* `ggplot_sir()` now splits the plot by antimicrobial if there is more than one and the antimicrobial is not shown in the plot, e.g., with `x = "species"`, as their bars were previously stacked on top of each other. This uses `facet = "antibiotic"` if `facet` is not set, and otherwise a grid of `facet` by antimicrobial
* `ggplot_pca()`
* Fixed an error when setting `groups` together with `labels`, and setting `groups` without `labels` now colours the points by these groups instead of using them as labels, as documented
* WISCA (`wisca()` and `antibiogram(..., wisca = TRUE)`)
* Fixed a critical bug that systematically underestimated the coverage of every WISCA since AMR 3.0.0. Pathogens absent from a syndromic group, and pathogens without any test result for a regimen, were still included in the model with only prior information: each added a prior weight of 1 to the pathogen incidence, and untested pathogens kept their full incidence weight while counting as about 50% susceptible. The bias was largest for stratified analyses (e.g., up to 14 percentage points for meropenem in random strata of `example_isolates`). Now, only pathogens that occur in a group and have at least one test result for the regimen are included. Please re-run all WISCA analyses (#337)
* `retrieve_wisca_parameters()` now returns only the parameters actually used in the model, and `n_total` in the `long_numeric` attribute now counts only isolates of the pathogens included for that regimen
* A new warning is given when the prior weight of 1 per pathogen (Bielicki *et al.*, 2016) accounts for more than 5% of the pathogen incidence, i.e., with rare pathogens or small groups, as this can strongly lower the estimated coverage; consider `top_n_microorganisms()` or fewer and larger groups in that case
* Microorganism coercion (`as.mo()`)
* Fixed the misaligned arrows in the message on taxonomically renamed microorganisms in colour-supporting consoles, for old names whose author reference lacks "*et al.*" (#336)
* `intrinsic_resistant` data set
* Species groups and complexes are now listed for every antimicrobial to which all their species are intrinsically resistant. This adds 62 bug-drug combinations for the *Burkholderia cepacia* complex, coryneform bacteria, the *Enterobacter cloacae* complex (e.g., amoxicillin and amoxicillin/clavulanic acid), the *Klebsiella pneumoniae* complex, and coagulase-negative staphylococci, which affects `mo_is_intrinsic_resistant()`, `not_intrinsic_resistant()`, and the priors of `wisca()` (#338)
### Other
* The documentation of `first_isolate()` now explains how to determine first isolates per any additional variable using `dplyr::group_by()`, and the bias this introduces when that variable is related to resistance (#345)
* The manual index on the website was rewritten to improve findability, with sections ordered by task and a filter field
* Fixed the link to the copyright section and added MycoBank as a taxonomy source in the *Welcome to AMR* vignette (#325)
# AMR 3.1.0 # AMR 3.1.0
This substantial release makes most core functions considerably faster, adds EUCAST 2026 and CLSI 2026 breakpoints, adopts the new prokaryotic kingdoms of Göker and Oren (2024), introduces WT/NWT interpretation for ECOFFs, unifies the EUCAST and CLSI interpretive rules, and supports `tidymodels` pipelines. As it contains several breaking changes, please read the *Breaking* section below before updating. This substantial release makes most core functions considerably faster, adds EUCAST 2026 and CLSI 2026 breakpoints, adopts the new prokaryotic kingdoms of Göker and Oren (2024), introduces WT/NWT interpretation for ECOFFs, unifies the EUCAST and CLSI interpretive rules, and supports `tidymodels` pipelines. As it contains several breaking changes, please read the *Breaking* section below before updating.
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Options for the AMR package #' Options for the AMR Package
#' #'
#' @description #' @description
#' This is an overview of all the package-specific options you can set in the `AMR` package. Set them using the [options()] function, e.g.: #' This is an overview of all the package-specific options you can set in the `AMR` package. Set them using the [options()] function, e.g.:
+1
View File
@@ -131,6 +131,7 @@ reset_mo_cache <- function() {
AMR_env$mo_accepted <- NULL AMR_env$mo_accepted <- NULL
AMR_env$mo_previously_coerced <- new_coercion_cache() AMR_env$mo_previously_coerced <- new_coercion_cache()
AMR_env$mo_previously_uncertain <- NULL AMR_env$mo_previously_uncertain <- NULL
AMR_env$taxonomy_names <- NULL
} }
get_ab_index <- function() { get_ab_index <- function() {
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Transform Input to an Antibiotic ID #' Transform Input to an Antimicrobial Drug ID
#' #'
#' Use this function to determine the antimicrobial drug code of one or more antimicrobials. The data set [antimicrobials] will be searched for abbreviations, official names and synonyms (brand names). #' Use this function to determine the antimicrobial drug code of one or more antimicrobials. The data set [antimicrobials] will be searched for abbreviations, official names and synonyms (brand names).
#' @param x A [character] vector to determine to antibiotic ID. #' @param x A [character] vector to determine to antibiotic ID.
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Get Properties of an Antibiotic #' Get Properties of an Antimicrobial Drug
#' #'
#' Use these functions to return a specific property of an antibiotic from the [antimicrobials] data set. All input values will be evaluated internally with [as.ab()]. #' Use these functions to return a specific property of an antibiotic from the [antimicrobials] data set. All input values will be evaluated internally with [as.ab()].
#' @param x Any (vector of) text that can be coerced to a valid antibiotic drug code with [as.ab()]. #' @param x Any (vector of) text that can be coerced to a valid antibiotic drug code with [as.ab()].
+32 -9
View File
@@ -156,6 +156,10 @@
#' #'
#' **Prior distributions:** Pathogen incidence uses a non-informative \eqn{Dirichlet(1, 1, \ldots, 1)} prior. Susceptibility proportions use the Jeffreys prior, \eqn{\beta(0.5, 0.5)}, except for pathogen-drug combinations with known intrinsic resistance, which use a strongly informative \eqn{\beta(1, 9999)} prior that forces near-zero susceptibility regardless of observed data. Intrinsic resistance is determined using the [intrinsic_resistant] data set, which is based on `r format_eucast_version_nr(names(EUCAST_VERSION_EXPECTED_PHENOTYPES[1]))`. #' **Prior distributions:** Pathogen incidence uses a non-informative \eqn{Dirichlet(1, 1, \ldots, 1)} prior. Susceptibility proportions use the Jeffreys prior, \eqn{\beta(0.5, 0.5)}, except for pathogen-drug combinations with known intrinsic resistance, which use a strongly informative \eqn{\beta(1, 9999)} prior that forces near-zero susceptibility regardless of observed data. Intrinsic resistance is determined using the [intrinsic_resistant] data set, which is based on `r format_eucast_version_nr(names(EUCAST_VERSION_EXPECTED_PHENOTYPES[1]))`.
#' #'
#' **Pathogens included in the model:** For each regimen (and group), only pathogens that occur in the data and have at least one test result for the regimen are included. Pathogens without any test result for a regimen do not contribute to its coverage, nor to its `n_total` in `long_numeric`. Use [retrieve_wisca_parameters()] to get the parameters that were used.
#'
#' **Rare pathogens and small groups:** Since every pathogen receives a prior weight of 1 in the Dirichlet distribution, the prior weighs heavily when there are few isolates relative to the number of pathogens, i.e., with pathogens found only once or a few times, or in small groups. This pulls the incidence towards a uniform distribution, and the very uncertain susceptibility of rare pathogens can then strongly lower the estimated coverage, without reflecting the clinical expectation. A warning is therefore given when this prior weight accounts for more than 5% of the pathogen incidence in any regimen (or group). Consider limiting the analysis to the most common pathogens using [top_n_microorganisms()], or using fewer and larger groups.
#'
#' **Interpreting the output:** Overlapping credible intervals between regimens indicate no significant difference in coverage; if a narrower-spectrum regimen overlaps with a broader one, the narrower-spectrum option may be preferred on stewardship grounds. Non-overlapping intervals indicate a clinically meaningful difference. For small sample sizes, consider pooling data from multiple sites to improve precision, provided pathogen distributions are sufficiently similar (Bielicki *et al.*, 2016). #' **Interpreting the output:** Overlapping credible intervals between regimens indicate no significant difference in coverage; if a narrower-spectrum regimen overlaps with a broader one, the narrower-spectrum option may be preferred on stewardship grounds. Non-overlapping intervals indicate a clinically meaningful difference. For small sample sizes, consider pooling data from multiple sites to improve precision, provided pathogen distributions are sufficiently similar (Bielicki *et al.*, 2016).
#' #'
#' For the full mathematical derivation and worked examples, see the [WISCA vignette](https://amr-for-r.org/articles/WISCA.html). #' For the full mathematical derivation and worked examples, see the [WISCA vignette](https://amr-for-r.org/articles/WISCA.html).
@@ -182,12 +186,13 @@
#' #'
#' # basic WISCA: empirical coverage per regimen, weighted by pathogen #' # basic WISCA: empirical coverage per regimen, weighted by pathogen
#' # incidence, with 95% credible intervals #' # incidence, with 95% credible intervals
#' wisca(example_isolates, #' # (10 most common species, since rare species can distort coverage estimates)
#' wisca(top_n_microorganisms(example_isolates, n = 10),
#' antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN") #' antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN")
#' ) #' )
#' #'
#' # equivalent using antibiogram(): #' # equivalent using antibiogram():
#' antibiogram(example_isolates, #' antibiogram(top_n_microorganisms(example_isolates, n = 10),
#' antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN"), #' antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN"),
#' wisca = TRUE #' wisca = TRUE
#' ) #' )
@@ -272,7 +277,7 @@
#' #'
#' # Print the output for R Markdown / Quarto ----------------------------- #' # Print the output for R Markdown / Quarto -----------------------------
#' #'
#' ureido <- wisca(example_isolates, #' ureido <- wisca(top_n_microorganisms(example_isolates, n = 10),
#' antimicrobials = ureidopenicillins(), #' antimicrobials = ureidopenicillins(),
#' syndromic_group = "ward" #' syndromic_group = "ward"
#' ) #' )
@@ -290,7 +295,7 @@
#' antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"), #' antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"),
#' mo_transform = "gramstain" #' mo_transform = "gramstain"
#' ) #' )
#' ab2 <- wisca(example_isolates, #' ab2 <- wisca(top_n_microorganisms(example_isolates, n = 10),
#' antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"), #' antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"),
#' syndromic_group = "ward" #' syndromic_group = "ward"
#' ) #' )
@@ -744,7 +749,8 @@ antibiogram.default <- function(x,
coverage = NA_real_, coverage = NA_real_,
lower_ci = NA_real_, lower_ci = NA_real_,
upper_ci = NA_real_, upper_ci = NA_real_,
n_total = sum(n_total, na.rm = TRUE), # only pathogens with test results for the regimen enter the WISCA model, see below
n_total = sum(n_total[which(n_tested > 0)]),
n_tested = sum(n_tested, na.rm = TRUE), n_tested = sum(n_tested, na.rm = TRUE),
n_susceptible = sum(n_susceptible, na.rm = TRUE) n_susceptible = sum(n_susceptible, na.rm = TRUE)
) )
@@ -761,7 +767,24 @@ antibiogram.default <- function(x,
out_wisca$group <- out_wisca$ab out_wisca$group <- out_wisca$ab
} }
wisca_parameters <- out # only pathogens that occur in the group (n_total > 0) and have at least one test result for the regimen (n_tested > 0) may contribute to WISCA; otherwise, absent or untested pathogens would enter with prior-only parameters and bias the coverage downwards (#337)
wisca_parameters <- out[which(out$n_total > 0 & out$n_tested > 0), , drop = FALSE]
# the Dirichlet prior of 1 per pathogen (Bielicki et al., 2016) weighs heavily when many pathogens are rare
prior_share <- vapply(
split(wisca_parameters$n_total, as.character(wisca_parameters$group)),
function(n) length(n) / (length(n) + sum(n)),
FUN.VALUE = numeric(1)
)
if (any(prior_share > 0.05)) {
warning_(
"In ", sum(prior_share > 0.05), " of ", length(prior_share), " WISCA regimen(s) or group(s), the prior weight of 1 per pathogen accounts for more than 5% of the pathogen incidence (up to ", round(max(prior_share) * 100, 1), "%), since there are few isolates relative to the number of pathogens, i.e., rare pathogens or a small group. ",
"Because WISCA gives every pathogen a prior weight of 1 (Bielicki et al., 2016), this can strongly lower the estimated coverage without reflecting the clinical expectation. ",
"Consider limiting the analysis to the most common pathogens with {.help [{.fun top_n_microorganisms}](AMR::top_n_microorganisms)}, or using fewer and larger groups.",
call = FALSE
)
}
wisca_draws <- list() wisca_draws <- list()
wisca_components <- list() wisca_components <- list()
@@ -1381,7 +1404,7 @@ plot.antibiogram <- function(x, ...) {
names.arg = df_sub$ab, names.arg = df_sub$ab,
col = "#aaaaaa", col = "#aaaaaa",
beside = TRUE, beside = TRUE,
main = mo, main = labels_taxonomy(mo),
legend = NULL legend = NULL
) )
@@ -1545,7 +1568,7 @@ autoplot.antibiogram <- function(object,
} }
if (has_facet) { if (has_facet) {
out <- out + ggplot2::facet_wrap("mo") out <- out + ggplot2::facet_wrap("mo", labeller = labeller_taxonomy)
} }
out out
@@ -1710,6 +1733,7 @@ plot_wisca_susceptibility_incidence <- function(wisca_model, sep) {
colour = translate_AMR("Pathogen", language = get_AMR_locale()), colour = translate_AMR("Pathogen", language = get_AMR_locale()),
caption = paste(attributes(wisca_model)$simulations, "Monte Carlo simulations") caption = paste(attributes(wisca_model)$simulations, "Monte Carlo simulations")
) + ) +
ggplot2::scale_colour_discrete(labels = labels_taxonomy) +
ggplot2::guides( ggplot2::guides(
colour = ggplot2::guide_legend( colour = ggplot2::guide_legend(
override.aes = list(alpha = 1, size = 3) override.aes = list(alpha = 1, size = 3)
@@ -1718,7 +1742,6 @@ plot_wisca_susceptibility_incidence <- function(wisca_model, sep) {
ggplot2::theme( ggplot2::theme(
legend.position = "right", legend.position = "right",
legend.key.spacing.y = ggplot2::unit(0.25, "lines"), legend.key.spacing.y = ggplot2::unit(0.25, "lines"),
legend.text = ggplot2::element_text(face = "italic"),
plot.title = ggplot2::element_text(size = 12, face = "bold") plot.title = ggplot2::element_text(size = 12, face = "bold")
) )
} }
+12
View File
@@ -130,6 +130,18 @@
#' #'
#' #'
#' The default method is phenotype-based (using `type = "points"`) and episode-based (using `episode_days = 365`). This makes sure that every genus-species combination is selected per patient once per year, while taking into account all antimicrobial test results. If no antimicrobial test results are available in the data set, only the episode-based method is applied at default. #' The default method is phenotype-based (using `type = "points"`) and episode-based (using `episode_days = 365`). This makes sure that every genus-species combination is selected per patient once per year, while taking into account all antimicrobial test results. If no antimicrobial test results are available in the data set, only the episode-based method is applied at default.
#'
#' ## First isolates per additional variable
#'
#' To determine first isolates separately for the values of any additional variable, such as a ward, a specimen group, a molecular marker or a locally defined classification, group the data with [dplyr::group_by()] before calling [first_isolate()]. Every group is then handled as a separate data set, so the selection, including the start of every episode, is determined per patient, genus-species combination and group value. This works with all methods described above:
#'
#' ```r
#' your_data %>%
#' group_by(ward) %>%
#' mutate(first = first_isolate())
#' ```
#'
#' Be aware that grouping on a variable that is related to antimicrobial resistance, such as a resistance mechanism or the output of [mdro()], retains more isolates of patients whose isolates change category. This deviates from CLSI M39 and generally leads to an overestimation of resistance, as the selection then depends on the outcome that is being estimated.
#' @rdname first_isolate #' @rdname first_isolate
#' @seealso [key_antimicrobials()] #' @seealso [key_antimicrobials()]
#' @export #' @export
+24 -4
View File
@@ -151,6 +151,7 @@ ggplot_pca <- function(x,
pca_model = x, pca_model = x,
groups = groups, groups = groups,
groups_missing = missing(groups), groups_missing = missing(groups),
group_name = if (missing(groups)) NULL else paste(deparse(substitute(groups)), collapse = ""),
labels = labels, labels = labels,
labels_missing = missing(labels), labels_missing = missing(labels),
choices = choices, choices = choices,
@@ -183,8 +184,19 @@ ggplot_pca <- function(x,
) )
# Score Labels # Score Labels
labels_parse <- FALSE
if (!is.null(labels)) { if (!is.null(labels)) {
df.u$labels <- labels df.u$labels <- labels
if (contains_taxonomy(labels)) {
# italicise taxonomic names, as plotmath text to be parsed by geom_text()
labels_parse <- TRUE
df.u$labels <- vapply(
FUN.VALUE = character(1),
as.list(labels_taxonomy(labels)),
function(e) if (is.character(e) && is.na(e)) NA_character_ else paste(deparse(e, width.cutoff = 500L), collapse = ""),
USE.NAMES = FALSE
)
}
} }
# Grouping variable # Grouping variable
@@ -213,7 +225,8 @@ ggplot_pca <- function(x,
) + ) +
ggplot2::geom_text(ggplot2::aes(label = labels, colour = groups), ggplot2::geom_text(ggplot2::aes(label = labels, colour = groups),
nudge_y = -0.05, nudge_y = -0.05,
size = labels_textsize size = labels_textsize,
parse = labels_parse
) + ) +
ggplot2::labs(colour = group_name) ggplot2::labs(colour = group_name)
} else { } else {
@@ -223,7 +236,8 @@ ggplot_pca <- function(x,
) + ) +
ggplot2::geom_text(ggplot2::aes(label = labels), ggplot2::geom_text(ggplot2::aes(label = labels),
nudge_y = -0.05, nudge_y = -0.05,
size = labels_textsize size = labels_textsize,
parse = labels_parse
) )
} }
} else { } else {
@@ -241,6 +255,11 @@ ggplot_pca <- function(x,
} }
} }
# italicise taxonomic names in the legend
if (!is.null(df.u$groups) && contains_taxonomy(df.u$groups)) {
g <- g + ggplot2::scale_colour_discrete(labels = labels_taxonomy)
}
# Overlay a concentration ellipse if there are groups # Overlay a concentration ellipse if there are groups
if (!is.null(df.u$groups) && !is.null(ell) && isTRUE(ellipse)) { if (!is.null(df.u$groups) && !is.null(ell) && isTRUE(ellipse)) {
g <- g + ggplot2::geom_path( g <- g + ggplot2::geom_path(
@@ -308,6 +327,7 @@ ggplot_pca <- function(x,
pca_calculations <- function(pca_model, pca_calculations <- function(pca_model,
groups = NULL, groups = NULL,
groups_missing = TRUE, groups_missing = TRUE,
group_name = NULL,
labels = NULL, labels = NULL,
labels_missing = TRUE, labels_missing = TRUE,
choices = 1:2, choices = 1:2,
@@ -329,8 +349,8 @@ pca_calculations <- function(pca_model,
error = function(e) NULL error = function(e) NULL
) )
} }
if (!is.null(groups) && is.null(labels)) { if (groups_missing && !is.null(groups) && is.null(labels)) {
# turn them around # only one non-numeric column: use it for labels instead of groups
labels <- groups labels <- groups
groups <- NULL groups <- NULL
group_name <- NULL group_name <- NULL
+98 -28
View File
@@ -33,10 +33,10 @@
#' @param data A [data.frame] with column(s) of class [`sir`] (see [as.sir()]). #' @param data A [data.frame] with column(s) of class [`sir`] (see [as.sir()]).
#' @param position Position adjustment of bars, either `"fill"`, `"stack"` or `"dodge"`. #' @param position Position adjustment of bars, either `"fill"`, `"stack"` or `"dodge"`.
#' @param x Variable to show on x axis, either `"antibiotic"` (default) or `"interpretation"` or a grouping variable. #' @param x Variable to show on x axis, either `"antibiotic"` (default) or `"interpretation"` or a grouping variable.
#' @param fill Variable to categorise using the plots legend, either `"antibiotic"` (default) or `"interpretation"` or a grouping variable. #' @param fill Variable to categorise using the plots legend, either `"interpretation"` (default) or `"antibiotic"` or a grouping variable. For [labels_sir_count()], this must be the same as the `fill` of [geom_sir()], so that the labels are placed on the right bars. If the interpretation is not shown in the plot (i.e., neither in `x`, `fill`, nor `facet`), only the susceptible proportion is plotted, which follows the guideline (see `guideline`): S+SDD+I for EUCAST, S+SDD for CLSI, and S if `guideline = NULL`.
#' @param breaks A [numeric] vector of positions. #' @param breaks A [numeric] vector of positions.
#' @param limits A [numeric] vector of length two providing limits of the scale, use `NA` to refer to the existing minimum or maximum. #' @param limits A [numeric] vector of length two providing limits of the scale, use `NA` to refer to the existing minimum or maximum.
#' @param facet Variable to split plots by, either `"interpretation"` (default) or `"antibiotic"` or a grouping variable. #' @param facet Variable to split plots by, either `"interpretation"` or `"antibiotic"` or a grouping variable. If there is more than one antimicrobial and the antimicrobial is not shown in the plot (i.e., neither in `x`, `fill`, nor `facet`), [ggplot_sir()] splits the plot by antimicrobial, as their bars would otherwise be stacked: using `facet = "antibiotic"` if `facet` is not set, and otherwise using a grid of `facet` by antimicrobial. Note that [geom_sir()] cannot do this, so add [facet_sir()] in that case.
#' @inheritParams proportion #' @inheritParams proportion
#' @param nrow (when using `facet`) number of rows. #' @param nrow (when using `facet`) number of rows.
#' @param colours A named vactor with colour to be used for filling. The default colours are colour-blind friendly. #' @param colours A named vactor with colour to be used for filling. The default colours are colour-blind friendly.
@@ -240,9 +240,24 @@ ggplot_sir <- function(data,
position <- "fill" position <- "fill"
} }
plot_df <- tryCatch(
sir_df(data = data, translate_ab = translate_ab, language = language, minimum = minimum, guideline = guideline),
error = function(e) NULL
)
shown <- c(normalise_sir_plot_var(x), normalise_sir_plot_var(fill), normalise_sir_plot_var(facet))
facet_grid_ab <- FALSE
if (!is.null(plot_df) && length(unique(plot_df$antibiotic)) > 1 && !"antibiotic" %in% shown) {
# the antibiotic is not shown, so the bars of all antibiotics would be stacked; split the plot by antibiotic
if (is.null(facet)) {
facet <- "antibiotic"
} else {
facet_grid_ab <- TRUE
}
}
p <- ggplot2::ggplot(data = data) + p <- ggplot2::ggplot(data = data) +
geom_sir( create_geom_sir(
position = position, x = x, fill = fill, translate_ab = translate_ab, position = position, x = x, fill = fill, facet = facet, translate_ab = translate_ab,
minimum = minimum, language = language, minimum = minimum, language = language,
guideline = guideline, ... guideline = guideline, ...
) + ) +
@@ -258,9 +273,11 @@ ggplot_sir <- function(data,
} }
if (datalabels == TRUE) { if (datalabels == TRUE) {
p <- p + labels_sir_count( p <- p + create_labels_sir_count(
position = position, position = position,
x = x, x = x,
fill = fill,
facet = facet,
translate_ab = translate_ab, translate_ab = translate_ab,
minimum = minimum, minimum = minimum,
language = language, language = language,
@@ -270,11 +287,34 @@ ggplot_sir <- function(data,
) )
} }
if (!is.null(facet)) { # italicise taxonomic names on the x axis and in the legend, e.g. with x = "mo" or fill = "mo"
if (x %in% colnames(data) && contains_taxonomy(data[[x]])) {
p <- p + ggplot2::scale_x_discrete(labels = labels_taxonomy)
}
if (fill != "interpretation" && fill %in% colnames(data) && contains_taxonomy(data[[fill]])) {
p <- p + ggplot2::scale_fill_discrete(labels = labels_taxonomy)
}
if (isTRUE(facet_grid_ab)) {
# a grid of the set facet by antibiotic
p <- p + ggplot2::facet_grid(
stats::as.formula(paste0("`", normalise_sir_plot_var(facet), "` ~ antibiotic")),
scales = "free_x",
labeller = labeller_taxonomy
)
} else if (!is.null(facet)) {
# pass the value, as facet_sir() takes an unquoted name as column name # pass the value, as facet_sir() takes an unquoted name as column name
p <- p + do.call(facet_sir, list(facet = facet, nrow = nrow)) p <- p + do.call(facet_sir, list(facet = facet, nrow = nrow))
} }
if (missing(y.title) && !"interpretation" %in% c(normalise_sir_plot_var(x), normalise_sir_plot_var(fill), normalise_sir_plot_var(facet))) {
# only the susceptible proportion is shown, e.g. "Proportion SI" for EUCAST
susceptible <- if (is.null(plot_df)) NULL else levels(plot_df$interpretation)[1]
if (!is.null(susceptible)) {
y.title <- paste(y.title, susceptible)
}
}
p <- p + ggplot2::labs( p <- p + ggplot2::labs(
title = title, title = title,
subtitle = subtitle, subtitle = subtitle,
@@ -308,15 +348,6 @@ geom_sir <- function(position = NULL,
language <- validate_language(language) language <- validate_language(language)
guideline <- get_aggregation_guideline(guideline, missing(guideline), combine_SI, missing(combine_SI), fn = "geom_sir") guideline <- get_aggregation_guideline(guideline, missing(guideline), combine_SI, missing(combine_SI), fn = "geom_sir")
y <- "value"
if (missing(position) || is.null(position)) {
position <- "fill"
}
if (identical(position, "fill")) {
position <- ggplot2::position_fill(vjust = 0.5, reverse = TRUE)
}
x_deparse <- deparse(substitute(x)) x_deparse <- deparse(substitute(x))
if (x_deparse != "x") { if (x_deparse != "x") {
x <- x_deparse x <- x_deparse
@@ -325,24 +356,63 @@ geom_sir <- function(position = NULL,
x <- substr(x, 2, nchar(x) - 1) x <- substr(x, 2, nchar(x) - 1)
} }
if (tolower(x) %in% tolower(c("ab", "abx", "antimicrobials"))) { create_geom_sir(
x <- "antibiotic" position = position, x = x, fill = fill, facet = NULL, translate_ab = translate_ab,
} else if (tolower(x) %in% tolower(c("SIR", "sir", "interpretations", "result"))) { minimum = minimum, language = language, guideline = guideline, ...
x <- "interpretation" )
} }
normalise_sir_plot_var <- function(v) {
# aliases for the columns of sir_df(), as also used by facet_sir()
if (is.null(v)) {
return(NULL)
}
if (tolower(v) %in% tolower(c("ab", "abx", "antimicrobials"))) {
v <- "antibiotic"
} else if (tolower(v) %in% tolower(c("SIR", "sir", "interpretations", "result"))) {
v <- "interpretation"
}
v
}
create_geom_sir <- function(position, x, fill, facet, translate_ab, minimum, language, guideline, ...) {
if (is.null(position)) {
position <- "fill"
}
if (identical(position, "fill")) {
position <- ggplot2::position_fill(vjust = 0.5, reverse = TRUE)
}
x <- normalise_sir_plot_var(x)
ggplot2::geom_col( ggplot2::geom_col(
data = function(x) { data = function(data) {
sir_df( sir_plot_data(
data = x, data = data, x = x, fill = fill, facet = facet, translate_ab = translate_ab,
translate_ab = translate_ab, minimum = minimum, language = language, guideline = guideline
language = language,
minimum = minimum,
guideline = guideline
) )
}, },
mapping = utils::modifyList(ggplot2::aes(), list(x = str2lang(x), y = str2lang(y), fill = str2lang(fill))), mapping = utils::modifyList(ggplot2::aes(), list(x = str2lang(x), y = str2lang("value"), fill = str2lang(fill))),
position = position, position = position,
... ...
) )
} }
sir_plot_data <- function(data, x, fill, facet, translate_ab, minimum, language, guideline) {
out <- sir_df(
data = data,
translate_ab = translate_ab,
language = language,
minimum = minimum,
guideline = guideline
)
if (!"interpretation" %in% c(normalise_sir_plot_var(x), normalise_sir_plot_var(fill), normalise_sir_plot_var(facet))) {
# the interpretation is not shown, so the bars of all interpretations would overlap;
# keep only the susceptible proportion, which is the first level and follows the guideline:
# S+SDD+I ("SI") for EUCAST, S+SDD ("S") for CLSI, and S for guideline = NULL
# the number of isolates of the data labels then becomes the number of tested isolates
keys <- do.call(paste, c(out[, setdiff(colnames(out), c("interpretation", "value", "ci_min", "ci_max", "isolates")), drop = FALSE], sep = "\r"))
totals <- tapply(out$isolates, keys, sum)
out$isolates <- as.integer(totals[keys])
out <- out[which(as.character(out$interpretation) == levels(out$interpretation)[1]), , drop = FALSE]
}
out
}
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Guess Antibiotic Column #' Guess Antimicrobial Column
#' #'
#' This tries to find a column name in a data set based on information from the [antimicrobials] data set. Also supports WHONET abbreviations. #' This tries to find a column name in a data set based on information from the [antimicrobials] data set. Also supports WHONET abbreviations.
#' @param x A [data.frame]. #' @param x A [data.frame].
+164 -69
View File
@@ -27,31 +27,57 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Italicise Taxonomic Families, Genera, Species, Subspecies #' Italicise Taxonomic Families, Genera, Species, and Subspecies
#' #'
#' According to the binomial nomenclature, the lowest four taxonomic levels (family, genus, species, subspecies) should be printed in italics. This function finds taxonomic names within strings and makes them italic. #' According to the binomial nomenclature, the lowest four taxonomic levels (family, genus, species, subspecies) should be printed in italics. This function finds taxonomic names within strings and makes them italic.
#' @param string A [character] (vector). #' @param string A [character] (vector).
#' @param type Type of conversion of the taxonomic names, either "markdown", "html" or "ansi", see *Details*. #' @param type Type of conversion of the taxonomic names, either "markdown", "html", "ansi" or "plotmath", see *Details*.
#' @details #' @details
#' This function finds the taxonomic names and makes them italic based on the [microorganisms] data set. #' This function finds the taxonomic names and makes them italic based on the [microorganisms] data set. Family and genus names are italicised anywhere in the text. Species and subspecies names are only italicised when they follow a genus, an abbreviated genus (such as "E." in "E. coli"), or another species or subspecies name, so that ordinary words that are also species names (such as "coli" or "major") stay upright. Subspecies may also follow "subsp.", "ssp." or "var.", which stay upright themselves, e.g., "*K. pneumoniae* subsp. *ozaenae*". *Salmonella* serovars (such as Typhimurium) and groups are not italicised, as is the convention. Punctuation around the names, such as brackets and commas, stays outside the italics.
#' #'
#' The taxonomic names can be italicised using markdown (the default) by adding `*` before and after the taxonomic names, or `<i>` and `</i>` when using html. When using 'ansi', ANSI colours will be added using `\033[3m` before and `\033[23m` after the taxonomic names. If multiple ANSI colours are not available, no conversion will occur. #' The taxonomic names can be italicised using markdown (the default) by adding `*` before and after the taxonomic names, or `<i>` and `</i>` when using html. When using 'ansi', ANSI colours will be added using `\033[3m` before and `\033[23m` after the taxonomic names. If multiple ANSI colours are not available, no conversion will occur.
#' #'
#' This function also supports abbreviation of the genus if it is followed by a species, such as "E. coli" and "K. pneumoniae ozaenae". #' Use `type = "plotmath"` to get an [expression] vector for plots (see [plotmath][grDevices::plotmath]), which works for titles, axes and legends in both base \R and `ggplot2`. For axis and legend labels, the function [labels_taxonomy()] does exactly this and can directly be used as labeller, e.g., `ggplot2::scale_x_discrete(labels = labels_taxonomy)`.
#' @return For "markdown", "html" and "ansi", a [character] vector. For "plotmath", an [expression] vector. Both have the same length as `string` and keep `NA` values.
#' @seealso [labels_taxonomy()] to italicise taxonomic names in plot labels.
#' @export #' @export
#' @examples #' @examples
#' italicise_taxonomy("An overview of Staphylococcus aureus isolates") #' italicise_taxonomy("An overview of Staphylococcus aureus isolates")
#' italicise_taxonomy("An overview of S. aureus isolates") #' italicise_taxonomy("An overview of S. aureus isolates")
#' italicise_taxonomy("Infections with E. coli (and coli bacteria in general)")
#' #'
#' cat(italicise_taxonomy("An overview of S. aureus isolates", type = "ansi")) #' cat(italicise_taxonomy("An overview of S. aureus isolates", type = "ansi"))
italicise_taxonomy <- function(string, type = c("markdown", "ansi", "html")) { #'
#' # for plots:
#' italicise_taxonomy("An overview of S. aureus isolates", type = "plotmath")
#' plot(1:10, main = italicise_taxonomy("Growth of S. aureus", type = "plotmath"))
italicise_taxonomy <- function(string, type = c("markdown", "ansi", "html", "plotmath")) {
if (missing(type)) { if (missing(type)) {
type <- "markdown" type <- "markdown"
} }
meet_criteria(string, allow_class = "character") meet_criteria(string, allow_class = "character")
meet_criteria(type, allow_class = "character", has_length = 1, is_in = c("markdown", "ansi", "html")) meet_criteria(type, allow_class = "character", has_length = 1, is_in = c("markdown", "ansi", "html", "plotmath"))
add_MO_lookup_to_AMR_env() pieces <- taxonomy_pieces(string)
if (type == "plotmath") {
return(as.expression(lapply(pieces, function(p) {
if (is.null(p)) {
return(NA_character_)
}
if (!any(p$italic)) {
return(paste0(p$text, collapse = ""))
}
parts <- lapply(seq_along(p$text), function(i) {
if (p$italic[i]) call("italic", p$text[i]) else p$text[i]
})
if (length(parts) == 1) {
parts[[1]]
} else {
as.call(c(as.name("paste"), parts))
}
})))
}
if (type == "markdown") { if (type == "markdown") {
before <- "*" before <- "*"
@@ -69,75 +95,144 @@ italicise_taxonomy <- function(string, type = c("markdown", "ansi", "html")) {
vapply( vapply(
FUN.VALUE = character(1), FUN.VALUE = character(1),
string, pieces,
function(s) { function(p) {
s_split <- unlist(strsplit(s, " ", fixed = TRUE)) if (is.null(p)) {
return(NA_character_)
search_strings <- gsub("[^a-zA-Z-]", "", s_split) }
p$text[p$italic] <- paste0(before, p$text[p$italic], after)
ind_species <- search_strings != "" & paste0(p$text, collapse = "")
search_strings %in% AMR_env$MO_lookup[
which(AMR_env$MO_lookup$rank %in% c(
"family",
"genus",
"species",
"subspecies",
"infraspecies",
"subsp."
)),
"species",
drop = TRUE
]
ind_fullname <- search_strings != "" &
search_strings %in% c(
AMR_env$MO_lookup[
which(AMR_env$MO_lookup$rank %in% c(
"family",
"genus",
"species",
"subspecies",
"infraspecies",
"subsp."
)),
"fullname",
drop = TRUE
],
AMR_env$MO_lookup[
which(AMR_env$MO_lookup$rank %in% c(
"family",
"genus",
"species",
"subspecies",
"infraspecies",
"subsp."
)),
"subspecies",
drop = TRUE
]
)
# also support E. coli, add "E." to indices
has_previous_genera_abbr <- s_split[which(ind_species) - 1] %like_case% "^[A-Z][.]?$"
ind_species <- c(which(ind_species), which(ind_species)[has_previous_genera_abbr] - 1)
ind <- c(ind_species, which(ind_fullname))
s_split[ind] <- paste0(before, s_split[ind], after)
s_paste <- paste(s_split, collapse = " ")
# clean up a bit
s_paste <- gsub(paste0(after, " ", before), " ", s_paste, fixed = TRUE)
s_paste
}, },
USE.NAMES = FALSE USE.NAMES = FALSE
) )
} }
contains_taxonomy <- function(x) {
# whether any value holds a taxonomic name that would be italicised
x <- unique(as.character(x))
any(vapply(taxonomy_pieces(x[!is.na(x)]), function(p) any(p$italic), logical(1)))
}
get_taxonomy_names <- function() {
# cached for the session, reset by reset_mo_cache() when the lookup table changes
if (is.null(AMR_env$taxonomy_names)) {
add_MO_lookup_to_AMR_env()
lookup <- AMR_env$MO_lookup
families <- unique(lookup$family)
genera <- unique(lookup$genus)
epithets <- unique(c(
lookup$species[lookup$rank %in% c("species", "subspecies", "species group")],
lookup$subspecies[lookup$rank == "subspecies"]
))
AMR_env$taxonomy_names <- list(
# only real names, so no "(unknown genus)" or similar
families = families[families %like_case% "^[A-Z][a-z-]+$"],
genera = genera[genera %like_case% "^[A-Z][a-z-]+$"],
# only lowercase epithets, so no Salmonella serovars ("Typhimurium") or groups ("Group A")
epithets = epithets[epithets %like_case% "^[a-z][a-z-]*$"]
)
}
AMR_env$taxonomy_names
}
taxonomy_pieces <- function(string) {
# returns a list with for every string NULL (if NA) or a list with `text` and `italic`
nms <- get_taxonomy_names()
out <- vector("list", length(string))
if (length(string) == 0) {
return(out)
}
# split all strings at once, so the lookups below run only once
words <- strsplit(string, " ", fixed = TRUE)
# strsplit() drops a trailing empty string
trailing <- which(!is.na(string) & string %like_case% " $")
words[trailing] <- lapply(words[trailing], c, "")
n_words <- lengths(words)
all_words <- unlist(words, use.names = FALSE)
all_words[is.na(all_words)] <- ""
# leading punctuation, the word itself, trailing punctuation
prefix <- gsub("^([^A-Za-z]*).*$", "\\1", all_words, perl = TRUE)
rest <- substr(all_words, nchar(prefix) + 1, nchar(all_words))
core <- gsub("^([A-Za-z]([A-Za-z-]*[A-Za-z])?).*$", "\\1", rest, perl = TRUE)
core[rest %unlike_case% "^[A-Za-z]"] <- ""
suffix <- substr(rest, nchar(core) + 1, nchar(rest))
# abbreviated genus, e.g. "E." in "E. coli"
is_abbr <- core %like_case% "^[A-Z]$" & suffix %like_case% "^[.]"
core[is_abbr] <- paste0(core[is_abbr], ".")
suffix[is_abbr] <- substr(suffix[is_abbr], 2, nchar(suffix[is_abbr]))
is_genus <- core != "" & core %in% nms$genera
is_higher <- is_genus | (core != "" & core %in% nms$families)
is_epithet <- core != "" & core %in% nms$epithets
is_connector <- all_words %in% c("subsp.", "ssp.", "var.")
ends <- cumsum(n_words)
starts <- ends - n_words + 1
for (s in which(!is.na(string))) {
ind <- seq.int(starts[s], length.out = n_words[s])
pre <- prefix[ind]
cr <- core[ind]
suf <- suffix[ind]
higher <- is_higher[ind]
genus <- is_genus[ind]
abbr <- is_abbr[ind]
epithet <- is_epithet[ind]
connector <- is_connector[ind]
n <- length(ind)
italic <- higher
# whether the next word can be a species or subspecies name
can_follow <- logical(n)
for (i in seq_len(n)) {
if (i > 1 && epithet[i] && pre[i] == "" && can_follow[i - 1]) {
italic[i] <- TRUE
if (abbr[i - 1]) {
italic[i - 1] <- TRUE
}
}
if (suf[i] == "" || connector[i]) {
can_follow[i] <- genus[i] ||
abbr[i] ||
(italic[i] && epithet[i]) ||
(connector[i] && i > 1 && italic[i - 1] && epithet[i - 1])
}
}
# connectors such as "subsp." are not part of the core, so keep them upright
cr[connector] <- all_words[ind][connector]
suf[connector] <- ""
italic[connector] <- FALSE
text <- character(0)
ital <- logical(0)
for (i in seq_len(n)) {
text <- c(text, pre[i], cr[i], suf[i])
ital <- c(ital, FALSE, italic[i], FALSE)
if (i < n) {
# the space between two italic words belongs to the italics
text <- c(text, " ")
ital <- c(ital, italic[i] && italic[i + 1] && suf[i] == "" && pre[i + 1] == "")
}
}
keep <- text != ""
text <- text[keep]
ital <- ital[keep]
if (length(text) == 0) {
out[[s]] <- list(text = "", italic = FALSE)
next
}
# merge adjacent pieces of the same style
grp <- cumsum(c(TRUE, ital[-1] != ital[-length(ital)]))
out[s] <- list(list(
text = vapply(split(text, grp), paste0, character(1), collapse = "", USE.NAMES = FALSE),
italic = vapply(split(ital, grp), function(x) x[1], logical(1), USE.NAMES = FALSE)
))
}
out
}
#' @rdname italicise_taxonomy #' @rdname italicise_taxonomy
#' @export #' @export
italicize_taxonomy <- function(string, type = c("markdown", "ansi", "html")) { italicize_taxonomy <- function(string, type = c("markdown", "ansi", "html", "plotmath")) {
if (missing(type)) { if (missing(type)) {
type <- "markdown" type <- "markdown"
} }
+3 -3
View File
@@ -1156,7 +1156,7 @@ mdro <- function(x = NULL,
) )
trans_tbl( trans_tbl(
3, 3,
which(x$genus == "Salmonella" & x$species == "Typhi"), which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems, fluoroquinolones), c(carbapenems, fluoroquinolones),
"any" "any"
) )
@@ -1251,7 +1251,7 @@ mdro <- function(x = NULL,
) )
trans_tbl( trans_tbl(
3, 3,
which(x$genus == "Salmonella" & x$species == "Typhi"), which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems), c(carbapenems),
"any" "any"
) )
@@ -1353,7 +1353,7 @@ mdro <- function(x = NULL,
) )
trans_tbl( trans_tbl(
3, 3,
which(x$genus == "Salmonella" & x$species == "Typhi"), which(x$genus == "Salmonella" & x$subspecies == "Typhi"),
c(carbapenems), c(carbapenems),
"any" "any"
) )
+4 -1
View File
@@ -1144,7 +1144,10 @@ print.mo_renamed <- function(x, extra_txt = "", n = 25, ...) {
rows <- seq_len(min(NROW(x), n)) rows <- seq_len(min(NROW(x), n))
message_("The following microorganism", ifelse(NROW(x) > 1, "s were", " was"), " taxonomically renamed", extra_txt, ":") message_("The following microorganism", ifelse(NROW(x) > 1, "s were", " was"), " taxonomically renamed", extra_txt, ":")
old_format <- format(paste0(font_italic(x$old[rows], collapse = NULL), x$ref_old[rows])) # format() will set trailing spaces for textual alignment old_format <- paste0(font_italic(x$old[rows], collapse = NULL), x$ref_old[rows])
# pad based on the visible width, since format() counts ANSI codes as characters
visible_width <- nchar(font_stripstyle(old_format), type = "width")
old_format <- paste0(old_format, strrep(" ", max(visible_width) - visible_width))
old_format <- gsub(" ", "\u00a0", old_format, fixed = TRUE) old_format <- gsub(" ", "\u00a0", old_format, fixed = TRUE)
for (old_tax in rows) { for (old_tax in rows) {
message_("\u00a0\u00a0", AMR_env$bullet_icon, " ", old_format[old_tax], " -> ", font_italic(x$new[old_tax]), x$ref_new[old_tax], as_note = FALSE) message_("\u00a0\u00a0", AMR_env$bullet_icon, " ", old_format[old_tax], " -> ", font_italic(x$new[old_tax]), x$ref_new[old_tax], as_note = FALSE)
+8 -2
View File
@@ -44,7 +44,7 @@
#' #'
#' [mo_ref()] returns the abbreviated authority of the nomenclatural act that created the queried name combination. When `keep_synonyms = FALSE` (default), this is the authority of the currently accepted name. When `keep_synonyms = TRUE`, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned. #' [mo_ref()] returns the abbreviated authority of the nomenclatural act that created the queried name combination. When `keep_synonyms = FALSE` (default), this is the authority of the currently accepted name. When `keep_synonyms = TRUE`, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned.
#' #'
#' The short name ([mo_shortname()]) returns the first character of the genus and the full species, such as `"E. coli"`, for species and subspecies. Exceptions are abbreviations of staphylococci (such as *"CoNS"*, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as *"GBS"*, Group B Streptococci). Please bear in mind that e.g. *E. coli* could mean *Escherichia coli* (domain of Bacteria) as well as *Entamoeba coli* (domain of Protozoa). Returning to the full name will be done using [as.mo()] internally, giving priority to bacteria and human pathogens, i.e. `"E. coli"` will always be considered *Escherichia coli*. As a result, `mo_fullname(mo_shortname("Entamoeba coli"))` returns `"Escherichia coli"`. #' The short name ([mo_shortname()]) returns the first character of the genus and the full species, such as `"E. coli"`, for species and subspecies. Exceptions are *Salmonella* serovars, which return the serovar (such as `"S. Typhi"`), and abbreviations of staphylococci (such as *"CoNS"*, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as *"GBS"*, Group B Streptococci). Please bear in mind that e.g. *E. coli* could mean *Escherichia coli* (domain of Bacteria) as well as *Entamoeba coli* (domain of Protozoa). Returning to the full name will be done using [as.mo()] internally, giving priority to bacteria and human pathogens, i.e. `"E. coli"` will always be considered *Escherichia coli*. As a result, `mo_fullname(mo_shortname("Entamoeba coli"))` returns `"Escherichia coli"`.
#' #'
#' Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), [mo_kingdom()] and [mo_domain()] return different results for bacteria and archaea: [mo_kingdom()] returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while [mo_domain()] returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results. #' Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), [mo_kingdom()] and [mo_domain()] return different results for bacteria and archaea: [mo_kingdom()] returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while [mo_domain()] returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results.
#' #'
@@ -262,13 +262,19 @@ mo_shortname <- function(x, language = get_AMR_locale(), keep_synonyms = getOpti
genera <- mo_genus(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...) genera <- mo_genus(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)
shortnames <- paste0(substr(genera, 1, 1), ". ", replace_empty(mo_species(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...))) shortnames <- paste0(substr(genera, 1, 1), ". ", replace_empty(mo_species(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)))
# exceptions for Salmonella serovars, that are stored as subspecies of S. enterica: return e.g. "S. Typhi"
subspecies <- mo_subspecies(x.mo, language = NULL, keep_synonyms = keep_synonyms, ...)
serovars <- which(genera == "Salmonella" & subspecies %like_case% "^[A-Z]")
shortnames[serovars] <- paste0("S. ", subspecies[serovars])
# exceptions for where no species is known # exceptions for where no species is known
shortnames[shortnames %like% ".[.] spp[.]"] <- genera[shortnames %like% ".[.] spp[.]"] shortnames[shortnames %like% ".[.] spp[.]"] <- genera[shortnames %like% ".[.] spp[.]"]
# exceptions for staphylococci # exceptions for staphylococci
shortnames[shortnames == "S. coagulase-negative"] <- "CoNS" shortnames[shortnames == "S. coagulase-negative"] <- "CoNS"
shortnames[shortnames == "S. coagulase-positive"] <- "CoPS" shortnames[shortnames == "S. coagulase-positive"] <- "CoPS"
# exceptions for streptococci: Group A Streptococcus -> GAS # exceptions for streptococci: Group A Streptococcus -> GAS
shortnames[shortnames %like_case% "S. Group [ABCDFGHK]"] <- paste0("G", gsub("S. Group ([ABCDFGHK])", "\\1", shortnames[shortnames %like_case% "S. Group [ABCDFGHK]"], perl = TRUE), "S") streptococci_groups <- which(genera == "Streptococcus" & shortnames %like_case% "^S[.] Group [A-L]$")
shortnames[streptococci_groups] <- paste0("G", gsub("S. Group ([A-L])", "\\1", shortnames[streptococci_groups], perl = TRUE), "S")
# unknown species etc. # unknown species etc.
shortnames[shortnames %like% "unknown"] <- paste0("(", trimws2(gsub("[^a-zA-Z -]", "", shortnames[shortnames %like% "unknown"], perl = TRUE)), ")") shortnames[shortnames %like% "unknown"] <- paste0("(", trimws2(gsub("[^a-zA-Z -]", "", shortnames[shortnames %like% "unknown"], perl = TRUE)), ")")
+71 -17
View File
@@ -33,7 +33,7 @@
#' Functions to plot classes `sir`, `mic` and `disk`, with support for base \R and `ggplot2`. #' Functions to plot classes `sir`, `mic` and `disk`, with support for base \R and `ggplot2`.
#' #'
#' Especially the `scale_*_mic()` functions are relevant wrappers to plot MIC values for `ggplot2`. They allow custom MIC ranges and to plot intermediate log2 levels for missing MIC values. #' Especially the `scale_*_mic()` functions are relevant wrappers to plot MIC values for `ggplot2`. They allow custom MIC ranges and to plot intermediate log2 levels for missing MIC values.
#' @param x,object Values created with [as.mic()], [as.disk()] or [as.sir()] (or their `random_*` variants, such as [random_mic()]). #' @param x,object Values created with [as.mic()], [as.disk()] or [as.sir()] (or their `random_*` variants, such as [random_mic()]). For [labels_taxonomy()], any values to use as labels, such as the breaks of an axis.
#' @param mo Any (vector of) text that can be coerced to a valid microorganism code with [as.mo()]. #' @param mo Any (vector of) text that can be coerced to a valid microorganism code with [as.mo()].
#' @param ab Any (vector of) text that can be coerced to a valid antimicrobial drug code with [as.ab()]. #' @param ab Any (vector of) text that can be coerced to a valid antimicrobial drug code with [as.ab()].
#' @param guideline Interpretation guideline to use - the default is the latest included EUCAST guideline, see *Details*. #' @param guideline Interpretation guideline to use - the default is the latest included EUCAST guideline, see *Details*.
@@ -76,6 +76,12 @@
#' * [theme_sir()] is a [ggplot2 theme][ggplot2::theme()] with minimal distraction. #' * [theme_sir()] is a [ggplot2 theme][ggplot2::theme()] with minimal distraction.
#' * [labels_sir_count()] prints data labels on the bars with percentage and number of isolates, using [ggplot2::geom_text()]. #' * [labels_sir_count()] prints data labels on the bars with percentage and number of isolates, using [ggplot2::geom_text()].
#' #'
#' ### Taxonomic Names in Labels
#'
#' The function [labels_taxonomy()] prints taxonomic names in axis and legend labels in italics, as is the convention in microbiology, e.g., *E. coli* and *Klebsiella pneumoniae*. It can directly be used as labeller in `ggplot2`, e.g., `scale_x_discrete(labels = labels_taxonomy)`, and in base \R, e.g., `axis(1, at = 1:3, labels = labels_taxonomy(names))`. Other words, such as "Other" or "CoNS", stay upright. It uses [italicise_taxonomy()] with `type = "plotmath"`, which also works for titles. It returns an [expression] vector of the same length as `x`; other arguments in `...` are ignored.
#'
#' The plotting functions of this package apply this automatically wherever they show taxonomic names, e.g., in the facets and titles of [antibiogram()] plots, in the legend of [wisca_plot()], on the axes, legends and facets of [ggplot_sir()] and [facet_sir()], in [ggplot_pca()], and in the subtitles of MIC and disk diffusion plots.
#'
#' The interpretation of "I" will be named "Susceptible, incr. exp." (susceptible, increased exposure) for all EUCAST guidelines since 2019, and will be named "Intermediate" in all other cases. #' The interpretation of "I" will be named "Susceptible, incr. exp." (susceptible, increased exposure) for all EUCAST guidelines since 2019, and will be named "Intermediate" in all other cases.
#' #'
#' For interpreting MIC values as well as disk diffusion diameters, the default guideline is `r AMR::clinical_breakpoints$guideline[1]`, unless the package option [`AMR_guideline`][AMR-options] is set. See [as.sir()] for more information. #' For interpreting MIC values as well as disk diffusion diameters, the default guideline is `r AMR::clinical_breakpoints$guideline[1]`, unless the package option [`AMR_guideline`][AMR-options] is set. See [as.sir()] for more information.
@@ -225,6 +231,18 @@
#' plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") #' plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl")
#' #'
#' plot(some_sir_values) #' plot(some_sir_values)
#'
#' # Taxonomic names in italics using labels_taxonomy() ------------------
#' bacteria <- data.frame(
#' name = c("E. coli", "Klebsiella pneumoniae", "CoNS", "Other"),
#' n = c(25, 18, 9, 6)
#' )
#' barplot(bacteria$n, names.arg = labels_taxonomy(bacteria$name))
#' if (require("ggplot2")) {
#' ggplot(bacteria, aes(name, n)) +
#' geom_col() +
#' scale_x_discrete(labels = labels_taxonomy)
#' }
NULL NULL
#' @rawNamespace if(getRversion() >= "3.0.0") S3method(ggplot2::scale_type, mic) #' @rawNamespace if(getRversion() >= "3.0.0") S3method(ggplot2::scale_type, mic)
@@ -1272,7 +1290,7 @@ plot_colours_subtitle_guideline <- function(x, mo, ab, guideline, colours_SIR, f
sir <- as.character(sir) sir <- as.character(sir)
cols <- unname(plot_sir_colour_values(colours_SIR)[sir]) cols <- unname(plot_sir_colour_values(colours_SIR)[sir])
cols[is.na(cols)] <- "#BEBEBE" cols[is.na(cols)] <- "#BEBEBE"
sub <- bquote(.(abname) ~ "-" ~ italic(.(moname)) ~ .(guideline_txt)) sub <- bquote(.(abname) ~ "-" ~ .(italicise_taxonomy(moname, type = "plotmath")[[1]]) ~ .(guideline_txt))
} else { } else {
sir <- rep(NA_character_, length(x)) sir <- rep(NA_character_, length(x))
cols <- "#BEBEBE" cols <- "#BEBEBE"
@@ -1303,7 +1321,7 @@ facet_sir <- function(facet = c("interpretation", "antibiotic"), nrow = NULL) {
facet <- "antibiotic" facet <- "antibiotic"
} }
ggplot2::facet_wrap(facets = facet, scales = "free_x", nrow = nrow) ggplot2::facet_wrap(facets = facet, scales = "free_x", nrow = nrow, labeller = labeller_taxonomy)
} }
#' @rdname plot #' @rdname plot
@@ -1412,10 +1430,31 @@ theme_sir <- function() {
) )
} }
#' @rdname plot
#' @export
labels_taxonomy <- function(x, ...) {
if (length(x) == 0) {
return(expression())
}
italicise_taxonomy(as.character(x), type = "plotmath")
}
# ggplot2 facet labeller that italicises taxonomic names in the strips
labeller_taxonomy <- structure(
function(labels) {
lapply(labels, function(values) {
expr <- labels_taxonomy(values)
lapply(seq_along(expr), function(i) expr[i])
})
},
class = c("function", "labeller")
)
#' @rdname ggplot_sir #' @rdname ggplot_sir
#' @export #' @export
labels_sir_count <- function(position = NULL, labels_sir_count <- function(position = NULL,
x = "antibiotic", x = "antibiotic",
fill = "interpretation",
translate_ab = "name", translate_ab = "name",
minimum = 30, minimum = 30,
language = get_AMR_locale(), language = get_AMR_locale(),
@@ -1426,6 +1465,7 @@ labels_sir_count <- function(position = NULL,
stop_ifnot_installed("ggplot2") stop_ifnot_installed("ggplot2")
meet_criteria(position, allow_class = "character", has_length = 1, is_in = c("fill", "stack", "dodge"), allow_NULL = TRUE) meet_criteria(position, allow_class = "character", has_length = 1, is_in = c("fill", "stack", "dodge"), allow_NULL = TRUE)
meet_criteria(x, allow_class = "character", has_length = 1) meet_criteria(x, allow_class = "character", has_length = 1)
meet_criteria(fill, allow_class = "character", has_length = 1)
meet_criteria(translate_ab, allow_class = c("character", "logical"), has_length = 1, allow_NA = TRUE) meet_criteria(translate_ab, allow_class = c("character", "logical"), has_length = 1, allow_NA = TRUE)
meet_criteria(minimum, allow_class = c("numeric", "integer"), has_length = 1, is_positive_or_zero = TRUE, is_finite = TRUE) meet_criteria(minimum, allow_class = c("numeric", "integer"), has_length = 1, is_positive_or_zero = TRUE, is_finite = TRUE)
language <- validate_language(language) language <- validate_language(language)
@@ -1433,35 +1473,49 @@ labels_sir_count <- function(position = NULL,
meet_criteria(datalabels.size, allow_class = c("numeric", "integer"), has_length = 1, is_positive = TRUE, is_finite = TRUE) meet_criteria(datalabels.size, allow_class = c("numeric", "integer"), has_length = 1, is_positive = TRUE, is_finite = TRUE)
meet_criteria(datalabels.colour, allow_class = "character", has_length = 1) meet_criteria(datalabels.colour, allow_class = "character", has_length = 1)
create_labels_sir_count(
position = position, x = x, fill = fill, facet = NULL, translate_ab = translate_ab,
minimum = minimum, language = language, guideline = guideline,
datalabels.size = datalabels.size, datalabels.colour = datalabels.colour
)
}
create_labels_sir_count <- function(position, x, fill, facet, translate_ab, minimum, language, guideline, datalabels.size, datalabels.colour) {
if (is.null(position)) { if (is.null(position)) {
position <- "fill" position <- "fill"
} }
x <- normalise_sir_plot_var(x)
# labels are grouped like the bars of geom_sir(), so that they are placed on the right bar
vjust <- 0.5
if (identical(position, "fill")) { if (identical(position, "fill")) {
# centred in the bar segments
position <- ggplot2::position_fill(vjust = 0.5, reverse = TRUE) position <- ggplot2::position_fill(vjust = 0.5, reverse = TRUE)
} else if (identical(position, "stack")) {
# centred in the bar segments
position <- ggplot2::position_stack(vjust = 0.5)
} else if (identical(position, "dodge")) {
# on top of the bars, with the default width of geom_col()
position <- ggplot2::position_dodge(width = 0.9)
vjust <- -0.5
} }
x_name <- x x_name <- x
ggplot2::geom_text( ggplot2::geom_text(
mapping = utils::modifyList(ggplot2::aes(), list(label = str2lang("lbl"), x = str2lang(x), y = str2lang("value"))), mapping = utils::modifyList(ggplot2::aes(), list(label = str2lang("lbl"), x = str2lang(x), y = str2lang("value"), group = str2lang(fill))),
position = position, position = position,
inherit.aes = FALSE, inherit.aes = FALSE,
size = datalabels.size, size = datalabels.size,
colour = datalabels.colour, colour = datalabels.colour,
lineheight = 0.75, lineheight = 0.75,
data = function(x) { vjust = vjust,
transformed <- sir_df( data = function(data) {
data = x, # the same rows as the bars of geom_sir()
translate_ab = translate_ab, transformed <- sir_plot_data(
minimum = minimum, data = data, x = x_name, fill = fill, facet = facet, translate_ab = translate_ab,
language = language, minimum = minimum, language = language, guideline = guideline
guideline = guideline
) )
transformed$gr <- transformed[, x_name, drop = TRUE] transformed$lbl <- paste0("n=", transformed$isolates)
transformed %pm>% transformed
pm_group_by(gr) %pm>%
pm_mutate(lbl = paste0("n=", isolates)) %pm>%
pm_ungroup() %pm>%
pm_select(-gr)
} }
) )
} }
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Random MIC Values/Disk Zones/SIR Generation #' Generate Random MIC Values, Disk Zones, or SIR Results
#' #'
#' These functions can be used for generating random MIC values and disk diffusion diameters, for AMR data analysis practice. By providing a microorganism and antimicrobial drug, the generated results will reflect reality as much as possible. #' These functions can be used for generating random MIC values and disk diffusion diameters, for AMR data analysis practice. By providing a microorganism and antimicrobial drug, the generated results will reflect reality as much as possible.
#' @param size Desired size of the returned vector. If used in a [data.frame] call or `dplyr` verb, will get the current (group) size if left blank. #' @param size Desired size of the returned vector. If used in a [data.frame] call or `dplyr` verb, will get the current (group) size if left blank.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -2,6 +2,7 @@
#' #'
#' This family of functions allows using AMR-specific data types such as `<sir>` and `<mic>` inside `tidymodels` pipelines. #' This family of functions allows using AMR-specific data types such as `<sir>` and `<mic>` inside `tidymodels` pipelines.
#' @inheritParams recipes::step_center #' @inheritParams recipes::step_center
#' @param columns A character string of the selected variable names. This is `NULL` until the step is trained by [recipes::prep()], and should not be set manually.
#' @details #' @details
#' You can read more in our online [AMR with tidymodels introduction](https://amr-for-r.org/articles/AMR_with_tidymodels.html). #' You can read more in our online [AMR with tidymodels introduction](https://amr-for-r.org/articles/AMR_with_tidymodels.html).
#' #'
@@ -17,7 +18,6 @@
#' These steps integrate with `recipes::recipe()` and work like standard preprocessing steps. They are useful for preparing data for modelling, especially with classification models. #' These steps integrate with `recipes::recipe()` and work like standard preprocessing steps. They are useful for preparing data for modelling, especially with classification models.
#' @seealso [recipes::recipe()], [as.sir()], [as.mic()], [as.disk()] #' @seealso [recipes::recipe()], [as.sir()], [as.mic()], [as.disk()]
#' @name amr-tidymodels #' @name amr-tidymodels
#' @keywords internal
#' @export #' @export
#' @examples #' @examples
#' if (require("tidymodels")) { #' if (require("tidymodels")) {
+1 -1
View File
@@ -27,7 +27,7 @@
# how to conduct AMR data analysis: https://amr-for-r.org # # how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== # # ==================================================================== #
#' Deprecated Functions, Arguments, or Datasets #' Deprecated Functions, Arguments, or Data Sets
#' #'
#' These objects are so-called '[Deprecated]'. **They will be removed in a future version of this package.** Using these will give a warning with the name of the alternative object it has been replaced by (if there is one). #' These objects are so-called '[Deprecated]'. **They will be removed in a future version of this package.** Using these will give a warning with the name of the alternative object it has been replaced by (if there is one).
#' @keywords internal #' @keywords internal
+80 -97
View File
@@ -38,6 +38,8 @@ template:
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
bootswatch: "flatly" bootswatch: "flatly"
# dark mode: the CSS in pkgdown/extra.css is prepared for it, but light mode is enforced for now
light-switch: false
bslib: bslib:
base_font: {google: "Lato"} base_font: {google: "Lato"}
heading_font: {google: "Lato"} heading_font: {google: "Lato"}
@@ -127,155 +129,136 @@ navbar:
href: "https://github.com/msberends/AMR" href: "https://github.com/msberends/AMR"
reference: reference:
- title: "Introduction to the package" - title: "Getting started"
desc: > desc: >
Please find the introduction to (and some general information about) our package here. An overview of the package, its options for users and teams, and its 28 supported languages.
contents: contents:
- "`AMR`" - "`AMR`"
- "`AMR-options`"
- title: "Preparing data: microorganisms" - "`translate`"
- "`amr_course`"
- title: "Cleaning data"
desc: > desc: >
These functions are meant to get taxonomically valid properties of microorganisms from any input, but Transform raw input of any kind into valid microorganisms with `as.mo()`, antimicrobials with `as.ab()`, and MIC values and disk diffusion zones with `as.mic()` and `as.disk()`, and retrieve their properties.
also properties derived from taxonomy, such as the Gram stain (`mo_gramstain()`) , or `mo_is_yeast()`. - subtitle: "Microorganisms"
Use `mo_source()` to teach this package how to translate your own codes to valid microorganisms, and
use `add_custom_microorganisms()` to add your own custom microorganisms to this package.
contents: contents:
- "`as.mo`" - "`as.mo`"
- "`mo_property`" - "`mo_property`"
- "`add_custom_microorganisms`"
- "`mo_source`" - "`mo_source`"
- "`add_custom_microorganisms`"
- title: "Preparing data: antimicrobials" - "`mo_matching_score`"
desc: > - subtitle: "Antimicrobials"
Use these functions to get valid properties of antimicrobials from any input or to clean your input.
You can even retrieve drug names and doses from clinical text records, using `ab_from_text()`.
contents: contents:
- "`as.ab`" - "`as.ab`"
- "`ab_property`" - "`ab_property`"
- "`ab_from_text`" - "`ab_from_text`"
- "`atc_online_property`"
- "`add_custom_antimicrobials`" - "`add_custom_antimicrobials`"
- "`atc_online_property`"
- title: "Interpreting data: antimicrobial results" - subtitle: "Antivirals"
desc: > contents:
With `as.mic()` and `as.disk()` you can transform your raw input to valid MIC or disk diffusion values. - "`as.av`"
Use `as.sir()` for cleaning raw data to let it only contain "R", "I" and "S", or to interpret MIC or disk diffusion values as SIR based on the lastest EUCAST and CLSI guidelines. - "`av_property`"
Afterwards, you can extend antibiotic interpretations by applying interpretive rules, for example [from EUCAST](https://www.eucast.org/expert_rules_and_intrinsic_resistance/) with `interpretive_rules()`. - "`av_from_text`"
- subtitle: "Test results"
contents: contents:
- "`as.sir`"
- "`as.mic`" - "`as.mic`"
- "`as.disk`" - "`as.disk`"
- title: "Interpreting results as SIR"
desc: >
Interpret MIC values and disk diffusion zones as SIR with `as.sir()`, according to EUCAST or CLSI, for every guideline year from 2011 to 2026, and apply interpretive rules. `as.sir()` also cleans existing SIR data.
contents:
- "`as.sir`"
- "`interpretive_rules`" - "`interpretive_rules`"
- "`custom_interpretive_rules`" - "`custom_interpretive_rules`"
- title: "Analysing data" - title: "Analysing data"
desc: > desc: >
Use these function for the analysis part. You can use `susceptibility()` or `resistance()` on any antibiotic column. Select first isolates with `first_isolate()`, generate traditional, combination, syndromic, and WISCA antibiograms with `antibiogram()` and `wisca()`, calculate resistance, and determine multidrug-resistant organisms.
With `antibiogram()`, you can generate a traditional, combined, syndromic, or weighted-incidence syndromic combination - subtitle: "Selecting isolates and antimicrobials"
antibiogram (WISCA). This function also comes with support for R Markdown and Quarto. contents:
Be sure to first select the isolates that are appropiate for analysis, by using `first_isolate()` or `is_new_episode()`. - "`first_isolate`"
You can also filter your data on certain resistance in certain antibiotic classes (`carbapenems()`, `aminoglycosides()`), - "`get_episode`"
or determine multi-drug resistant microorganisms (MDRO, `mdro()`). - "`key_antimicrobials`"
- "`top_n_microorganisms`"
- "`antimicrobial_selectors`"
- "`guess_ab_col`"
- "`availability`"
- subtitle: "Generating antibiograms and WISCA"
contents: contents:
- "`antibiogram`" - "`antibiogram`"
- subtitle: "Calculating resistance and susceptibility"
contents:
- "`proportion`" - "`proportion`"
- "`count`" - "`count`"
- "`sir_combine`" - "`sir_combine`"
- "`is_new_episode`" - "`bug_drug_combinations`"
- "`first_isolate`" - subtitle: "Determining multidrug resistance"
- "`key_antimicrobials`" contents:
- "`mdro`" - "`mdro`"
- "`custom_mdro_guideline`" - "`custom_mdro_guideline`"
- "`bug_drug_combinations`"
- "`antimicrobial_selectors`" - title: "Visualising data"
- "`top_n_microorganisms`"
- "`mean_amr_distance`"
- "`resistance_predict`"
- "`guess_ab_col`"
- title: "Plotting data"
desc: > desc: >
Use these functions for the plotting part. The `scale_*_mic()` functions extend the ggplot2 package to allow plotting of Plot MIC values, disk zones, and SIR results in base R or `ggplot2`, with scales and colours tailored to AMR data, and taxonomic names in italics.
MIC values, even within a manually set range.
If using `plot()` (base R) or `autoplot()` (ggplot2) on MIC values or disk diffusion values, the user can
set the interpretation guideline to give the bars the right SIR colours.
The `ggplot_sir()` function is a short wrapper for users not much accustomed to ggplot2 yet.
The `ggplot_pca()` function is a specific function to plot so-called biplots for PCA (principal component analysis).
contents: contents:
- "`plot`" - "`plot`"
- "`ggplot_sir`" - "`ggplot_sir`"
- "`ggplot_pca`" - "`ggplot_pca`"
- title: "AMR-specific options"
desc: >
The AMR package is customisable, by providing settings that can be set per user or per team. For
example, the default interpretation guideline can be changed from EUCAST to CLSI, or a supported
language can be set for the whole team (system-language independent) for antibiotic names in a
foreign language.
contents:
- "`AMR-options`"
- title: "Other: antiviral drugs"
desc: >
This package also provides extensive support for antiviral agents, even though it is not the primary
scope of this package. Working with data containing information about antiviral drugs was never easier.
Use these functions to get valid properties of antiviral drugs from any input or to clean your input.
You can even retrieve drug names and doses from clinical text records, using `av_from_text()`.
contents:
- "`as.av`"
- "`av_property`"
- "`av_from_text`"
- title: "Other: background information on included data" - title: "Modelling"
desc: > desc: >
Some pages about our package and its external sources. Be sure to read our [How To's](./../articles/index.html) Use AMR data types in `tidymodels` pipelines, and perform principal component analysis or distance calculations on resistance profiles.
for more information about how to work with functions in this package. contents:
- "`amr-tidymodels`"
- "`pca`"
- "`mean_amr_distance`"
- title: "Reference data"
desc: >
The data sets underlying this package, all of which can also be [downloaded in other formats](../articles/datasets.html).
- subtitle: "Microorganisms"
contents: contents:
- "`microorganisms`" - "`microorganisms`"
- "`antimicrobials`"
- "`clinical_breakpoints`"
- "`example_isolates`"
- "`esbl_isolates`"
- "`microorganisms.codes`" - "`microorganisms.codes`"
- "`microorganisms.groups`" - "`microorganisms.groups`"
- "`intrinsic_resistant`" - subtitle: "Antimicrobials"
- "`dosage`" contents:
- "`antimicrobials`"
- "`WHOCC`" - "`WHOCC`"
- "`dosage`"
- subtitle: "Breakpoints and intrinsic resistance"
contents:
- "`clinical_breakpoints`"
- "`intrinsic_resistant`"
- subtitle: "Example data"
contents:
- "`example_isolates`"
- "`example_isolates_unclean`" - "`example_isolates_unclean`"
- "`esbl_isolates`"
- "`WHONET`" - "`WHONET`"
- title: "Other: miscellaneous functions" - title: "Utilities"
desc: > desc: >
Miscellaneous functions that support various parts of an AMR analysis, Helper functions for pattern matching with `%like%`, ages, joining and italicising taxonomy, generating random test results, and data export.
such as working with ages, joining tables, principal component analysis,
and other utilities. Especially the 'like' function can be useful:
`if (x %like% y) {...}`.
contents: contents:
- "`age_groups`"
- "`age`"
- "`amr_course`"
- "`export_ncbi_biosample`"
- "`availability`"
- "`get_AMR_locale`"
- "`italicise_taxonomy`"
- "`join`"
- "`like`" - "`like`"
- "`mo_matching_score`" - "`age`"
- "`pca`" - "`age_groups`"
- "`join`"
- "`italicise_taxonomy`"
- "`random`" - "`random`"
- "`export_ncbi_biosample`"
- title: "Other: statistical tests" - subtitle: "Statistical tests"
desc: >
Some statistical tests or methods usable for AMR analyses are not part of base R and were added to this package for convenience.
contents: contents:
- "`g.test`" - "`g.test`"
- "`kurtosis`" - "`kurtosis`"
- "`skewness`" - "`skewness`"
- title: "Other: deprecated functions/arguments/datasets" - title: "Deprecated functions"
desc: > desc: >
These objects are deprecated, meaning that they will still These still work, but show a warning and will be removed in a future version.
work but show a warning that they will be removed
in a future version.
contents: contents:
- "`AMR-deprecated`" - "`AMR-deprecated`"
- "`resistance_predict`"
@@ -58,6 +58,23 @@ int_resis3 <- int_resis2[which(!int_resis2$ab %in% untreatable), ]
int_resis3$ab <- as.ab(int_resis3$ab) int_resis3$ab <- as.ab(int_resis3$ab)
int_resis3 int_resis3
# add species groups (e.g., complexes) for every antimicrobial to which all their species are intrinsically resistant (#338)
groups <- microorganisms.groups[which(microorganisms$rank[match(microorganisms.groups$mo, microorganisms$mo)] == "species"), , drop = FALSE]
int_resis_keys <- paste(int_resis3$mo, int_resis3$ab)
groups_species <- split(as.character(groups$mo), as.character(groups$mo_group))
int_resis_groups <- do.call(rbind, Map(function(group, species) {
abx <- unique(as.character(int_resis3$ab[int_resis3$mo %in% species]))
abx <- abx[vapply(abx, function(ab) all(paste(species, ab) %in% int_resis_keys), logical(1))]
data.frame(mo = rep(group, length(abx)), ab = abx, stringsAsFactors = FALSE)
}, names(groups_species), groups_species))
int_resis_groups <- int_resis_groups[which(!paste(int_resis_groups$mo, int_resis_groups$ab) %in% int_resis_keys), , drop = FALSE]
int_resis_groups$mo <- as.mo(int_resis_groups$mo, keep_synonyms = TRUE)
int_resis_groups$ab <- as.ab(int_resis_groups$ab)
int_resis3 <- bind_rows(int_resis3, int_resis_groups)
# keep the order of the microorganisms and antimicrobials data sets
int_resis3 <- int_resis3[order(match(int_resis3$mo, microorganisms$mo), match(int_resis3$ab, antimicrobials$ab)), , drop = FALSE]
rownames(int_resis3) <- NULL
all(int_resis3$mo %in% microorganisms$mo) all(int_resis3$mo %in% microorganisms$mo)
all(int_resis3$ab %in% antimicrobials$ab) all(int_resis3$ab %in% antimicrobials$ab)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+62
View File
@@ -31323,8 +31323,19 @@
"Burkholderia cepacia" "Zorbamycin" "Burkholderia cepacia" "Zorbamycin"
"Burkholderia cepacia complex" "Acetylmidecamycin" "Burkholderia cepacia complex" "Acetylmidecamycin"
"Burkholderia cepacia complex" "Acetylspiramycin" "Burkholderia cepacia complex" "Acetylspiramycin"
"Burkholderia cepacia complex" "Amikacin"
"Burkholderia cepacia complex" "Amikacin/fosfomycin"
"Burkholderia cepacia complex" "Amoxicillin"
"Burkholderia cepacia complex" "Amoxicillin/clavulanic acid"
"Burkholderia cepacia complex" "Ampicillin"
"Burkholderia cepacia complex" "Ampicillin/sulbactam"
"Burkholderia cepacia complex" "Apramycin"
"Burkholderia cepacia complex" "Arbekacin"
"Burkholderia cepacia complex" "Astromicin"
"Burkholderia cepacia complex" "Avoparcin" "Burkholderia cepacia complex" "Avoparcin"
"Burkholderia cepacia complex" "Azithromycin" "Burkholderia cepacia complex" "Azithromycin"
"Burkholderia cepacia complex" "Aztreonam"
"Burkholderia cepacia complex" "Bekanamycin"
"Burkholderia cepacia complex" "Benzylpenicillin" "Burkholderia cepacia complex" "Benzylpenicillin"
"Burkholderia cepacia complex" "Bleomycin" "Burkholderia cepacia complex" "Bleomycin"
"Burkholderia cepacia complex" "Cadazolid" "Burkholderia cepacia complex" "Cadazolid"
@@ -31342,54 +31353,87 @@
"Burkholderia cepacia complex" "Cefmetazole" "Burkholderia cepacia complex" "Cefmetazole"
"Burkholderia cepacia complex" "Cefonicid" "Burkholderia cepacia complex" "Cefonicid"
"Burkholderia cepacia complex" "Ceforanide" "Burkholderia cepacia complex" "Ceforanide"
"Burkholderia cepacia complex" "Cefotaxime"
"Burkholderia cepacia complex" "Cefotetan" "Burkholderia cepacia complex" "Cefotetan"
"Burkholderia cepacia complex" "Cefotiam" "Burkholderia cepacia complex" "Cefotiam"
"Burkholderia cepacia complex" "Cefoxitin" "Burkholderia cepacia complex" "Cefoxitin"
"Burkholderia cepacia complex" "Cefprozil" "Burkholderia cepacia complex" "Cefprozil"
"Burkholderia cepacia complex" "Cefroxadine" "Burkholderia cepacia complex" "Cefroxadine"
"Burkholderia cepacia complex" "Ceftezole" "Burkholderia cepacia complex" "Ceftezole"
"Burkholderia cepacia complex" "Ceftriaxone"
"Burkholderia cepacia complex" "Cefuroxime" "Burkholderia cepacia complex" "Cefuroxime"
"Burkholderia cepacia complex" "Cefuroxime axetil" "Burkholderia cepacia complex" "Cefuroxime axetil"
"Burkholderia cepacia complex" "Cephradine" "Burkholderia cepacia complex" "Cephradine"
"Burkholderia cepacia complex" "Chloramphenicol"
"Burkholderia cepacia complex" "Ciprofloxacin"
"Burkholderia cepacia complex" "Clarithromycin" "Burkholderia cepacia complex" "Clarithromycin"
"Burkholderia cepacia complex" "Clindamycin" "Burkholderia cepacia complex" "Clindamycin"
"Burkholderia cepacia complex" "Colistin"
"Burkholderia cepacia complex" "Cycloserine" "Burkholderia cepacia complex" "Cycloserine"
"Burkholderia cepacia complex" "Dalbavancin" "Burkholderia cepacia complex" "Dalbavancin"
"Burkholderia cepacia complex" "Dibekacin"
"Burkholderia cepacia complex" "Dirithromycin" "Burkholderia cepacia complex" "Dirithromycin"
"Burkholderia cepacia complex" "Ertapenem"
"Burkholderia cepacia complex" "Erythromycin" "Burkholderia cepacia complex" "Erythromycin"
"Burkholderia cepacia complex" "Flurithromycin" "Burkholderia cepacia complex" "Flurithromycin"
"Burkholderia cepacia complex" "Fosfomycin"
"Burkholderia cepacia complex" "Framycetin"
"Burkholderia cepacia complex" "Fusidic acid" "Burkholderia cepacia complex" "Fusidic acid"
"Burkholderia cepacia complex" "Gamithromycin" "Burkholderia cepacia complex" "Gamithromycin"
"Burkholderia cepacia complex" "Gentamicin"
"Burkholderia cepacia complex" "Habekacin"
"Burkholderia cepacia complex" "Hygromycin"
"Burkholderia cepacia complex" "Isepamicin"
"Burkholderia cepacia complex" "Josamycin" "Burkholderia cepacia complex" "Josamycin"
"Burkholderia cepacia complex" "Kanamycin"
"Burkholderia cepacia complex" "Kanamycin/cephalexin"
"Burkholderia cepacia complex" "Kasugamycin"
"Burkholderia cepacia complex" "Kitasamycin" "Burkholderia cepacia complex" "Kitasamycin"
"Burkholderia cepacia complex" "Lincomycin" "Burkholderia cepacia complex" "Lincomycin"
"Burkholderia cepacia complex" "Linezolid" "Burkholderia cepacia complex" "Linezolid"
"Burkholderia cepacia complex" "Loracarbef" "Burkholderia cepacia complex" "Loracarbef"
"Burkholderia cepacia complex" "Meleumycin" "Burkholderia cepacia complex" "Meleumycin"
"Burkholderia cepacia complex" "Micronomicin"
"Burkholderia cepacia complex" "Midecamycin" "Burkholderia cepacia complex" "Midecamycin"
"Burkholderia cepacia complex" "Miocamycin" "Burkholderia cepacia complex" "Miocamycin"
"Burkholderia cepacia complex" "Nafithromycin" "Burkholderia cepacia complex" "Nafithromycin"
"Burkholderia cepacia complex" "Neomycin"
"Burkholderia cepacia complex" "Netilmicin"
"Burkholderia cepacia complex" "Norvancomycin" "Burkholderia cepacia complex" "Norvancomycin"
"Burkholderia cepacia complex" "Oleandomycin" "Burkholderia cepacia complex" "Oleandomycin"
"Burkholderia cepacia complex" "Oritavancin" "Burkholderia cepacia complex" "Oritavancin"
"Burkholderia cepacia complex" "Ostreogrycin" "Burkholderia cepacia complex" "Ostreogrycin"
"Burkholderia cepacia complex" "Pentisomicin"
"Burkholderia cepacia complex" "Piperacillin"
"Burkholderia cepacia complex" "Piperacillin/tazobactam"
"Burkholderia cepacia complex" "Pirlimycin" "Burkholderia cepacia complex" "Pirlimycin"
"Burkholderia cepacia complex" "Plazomicin"
"Burkholderia cepacia complex" "Polymyxin B"
"Burkholderia cepacia complex" "Primycin" "Burkholderia cepacia complex" "Primycin"
"Burkholderia cepacia complex" "Pristinamycin" "Burkholderia cepacia complex" "Pristinamycin"
"Burkholderia cepacia complex" "Propikacin"
"Burkholderia cepacia complex" "Quinupristin/dalfopristin" "Burkholderia cepacia complex" "Quinupristin/dalfopristin"
"Burkholderia cepacia complex" "Ramoplanin" "Burkholderia cepacia complex" "Ramoplanin"
"Burkholderia cepacia complex" "Ribostamycin"
"Burkholderia cepacia complex" "Rifampicin" "Burkholderia cepacia complex" "Rifampicin"
"Burkholderia cepacia complex" "Rokitamycin" "Burkholderia cepacia complex" "Rokitamycin"
"Burkholderia cepacia complex" "Roxithromycin" "Burkholderia cepacia complex" "Roxithromycin"
"Burkholderia cepacia complex" "Sisomicin"
"Burkholderia cepacia complex" "Solithromycin" "Burkholderia cepacia complex" "Solithromycin"
"Burkholderia cepacia complex" "Spiramycin" "Burkholderia cepacia complex" "Spiramycin"
"Burkholderia cepacia complex" "Streptoduocin"
"Burkholderia cepacia complex" "Streptomycin"
"Burkholderia cepacia complex" "Tedizolid" "Burkholderia cepacia complex" "Tedizolid"
"Burkholderia cepacia complex" "Teicoplanin" "Burkholderia cepacia complex" "Teicoplanin"
"Burkholderia cepacia complex" "Telavancin" "Burkholderia cepacia complex" "Telavancin"
"Burkholderia cepacia complex" "Telithromycin" "Burkholderia cepacia complex" "Telithromycin"
"Burkholderia cepacia complex" "Thiacetazone" "Burkholderia cepacia complex" "Thiacetazone"
"Burkholderia cepacia complex" "Ticarcillin"
"Burkholderia cepacia complex" "Ticarcillin/clavulanic acid"
"Burkholderia cepacia complex" "Tildipirosin" "Burkholderia cepacia complex" "Tildipirosin"
"Burkholderia cepacia complex" "Tilmicosin" "Burkholderia cepacia complex" "Tilmicosin"
"Burkholderia cepacia complex" "Tobramycin"
"Burkholderia cepacia complex" "Trimethoprim"
"Burkholderia cepacia complex" "Troleandomycin" "Burkholderia cepacia complex" "Troleandomycin"
"Burkholderia cepacia complex" "Tulathromycin" "Burkholderia cepacia complex" "Tulathromycin"
"Burkholderia cepacia complex" "Tylosin" "Burkholderia cepacia complex" "Tylosin"
@@ -65522,6 +65566,7 @@
"Cnuibacter physcomitrellae" "Polymyxin B" "Cnuibacter physcomitrellae" "Polymyxin B"
"Cnuibacter physcomitrellae" "Temocillin" "Cnuibacter physcomitrellae" "Temocillin"
"Coagulase-negative Staphylococcus (CoNS)" "Aztreonam" "Coagulase-negative Staphylococcus (CoNS)" "Aztreonam"
"Coagulase-negative Staphylococcus (CoNS)" "Ceftazidime"
"Coagulase-negative Staphylococcus (CoNS)" "Colistin" "Coagulase-negative Staphylococcus (CoNS)" "Colistin"
"Coagulase-negative Staphylococcus (CoNS)" "Nalidixic acid" "Coagulase-negative Staphylococcus (CoNS)" "Nalidixic acid"
"Coagulase-negative Staphylococcus (CoNS)" "Polymyxin B" "Coagulase-negative Staphylococcus (CoNS)" "Polymyxin B"
@@ -67762,6 +67807,11 @@
"Corynebacterium zhongnanshanii" "Nalidixic acid" "Corynebacterium zhongnanshanii" "Nalidixic acid"
"Corynebacterium zhongnanshanii" "Polymyxin B" "Corynebacterium zhongnanshanii" "Polymyxin B"
"Corynebacterium zhongnanshanii" "Temocillin" "Corynebacterium zhongnanshanii" "Temocillin"
"Coryneform bacterium" "Aztreonam"
"Coryneform bacterium" "Colistin"
"Coryneform bacterium" "Nalidixic acid"
"Coryneform bacterium" "Polymyxin B"
"Coryneform bacterium" "Temocillin"
"Cosenzaea" "Acetylmidecamycin" "Cosenzaea" "Acetylmidecamycin"
"Cosenzaea" "Acetylspiramycin" "Cosenzaea" "Acetylspiramycin"
"Cosenzaea" "Avoparcin" "Cosenzaea" "Avoparcin"
@@ -76494,11 +76544,20 @@
"Enterobacter cloacae cloacae" "Zorbamycin" "Enterobacter cloacae cloacae" "Zorbamycin"
"Enterobacter cloacae complex" "Acetylmidecamycin" "Enterobacter cloacae complex" "Acetylmidecamycin"
"Enterobacter cloacae complex" "Acetylspiramycin" "Enterobacter cloacae complex" "Acetylspiramycin"
"Enterobacter cloacae complex" "Amoxicillin"
"Enterobacter cloacae complex" "Amoxicillin/clavulanic acid"
"Enterobacter cloacae complex" "Ampicillin"
"Enterobacter cloacae complex" "Ampicillin/sulbactam"
"Enterobacter cloacae complex" "Avoparcin" "Enterobacter cloacae complex" "Avoparcin"
"Enterobacter cloacae complex" "Azithromycin" "Enterobacter cloacae complex" "Azithromycin"
"Enterobacter cloacae complex" "Benzylpenicillin" "Enterobacter cloacae complex" "Benzylpenicillin"
"Enterobacter cloacae complex" "Bleomycin" "Enterobacter cloacae complex" "Bleomycin"
"Enterobacter cloacae complex" "Cadazolid" "Enterobacter cloacae complex" "Cadazolid"
"Enterobacter cloacae complex" "Cefadroxil"
"Enterobacter cloacae complex" "Cefalexin"
"Enterobacter cloacae complex" "Cefalotin"
"Enterobacter cloacae complex" "Cefazolin"
"Enterobacter cloacae complex" "Cefoxitin"
"Enterobacter cloacae complex" "Clarithromycin" "Enterobacter cloacae complex" "Clarithromycin"
"Enterobacter cloacae complex" "Clindamycin" "Enterobacter cloacae complex" "Clindamycin"
"Enterobacter cloacae complex" "Cycloserine" "Enterobacter cloacae complex" "Cycloserine"
@@ -102515,6 +102574,8 @@
"Klebsiella pneumoniae" "Zorbamycin" "Klebsiella pneumoniae" "Zorbamycin"
"Klebsiella pneumoniae complex" "Acetylmidecamycin" "Klebsiella pneumoniae complex" "Acetylmidecamycin"
"Klebsiella pneumoniae complex" "Acetylspiramycin" "Klebsiella pneumoniae complex" "Acetylspiramycin"
"Klebsiella pneumoniae complex" "Amoxicillin"
"Klebsiella pneumoniae complex" "Ampicillin"
"Klebsiella pneumoniae complex" "Avoparcin" "Klebsiella pneumoniae complex" "Avoparcin"
"Klebsiella pneumoniae complex" "Azithromycin" "Klebsiella pneumoniae complex" "Azithromycin"
"Klebsiella pneumoniae complex" "Benzylpenicillin" "Klebsiella pneumoniae complex" "Benzylpenicillin"
@@ -102556,6 +102617,7 @@
"Klebsiella pneumoniae complex" "Telavancin" "Klebsiella pneumoniae complex" "Telavancin"
"Klebsiella pneumoniae complex" "Telithromycin" "Klebsiella pneumoniae complex" "Telithromycin"
"Klebsiella pneumoniae complex" "Thiacetazone" "Klebsiella pneumoniae complex" "Thiacetazone"
"Klebsiella pneumoniae complex" "Ticarcillin"
"Klebsiella pneumoniae complex" "Tildipirosin" "Klebsiella pneumoniae complex" "Tildipirosin"
"Klebsiella pneumoniae complex" "Tilmicosin" "Klebsiella pneumoniae complex" "Tilmicosin"
"Klebsiella pneumoniae complex" "Troleandomycin" "Klebsiella pneumoniae complex" "Troleandomycin"
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
b32da210529081a8e0ab8874956739d1 e17c4cf3ac9495a9ed832b64da96770a
File diff suppressed because it is too large. Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
+30 -28
View File
@@ -70,15 +70,15 @@ With the help of contributors from all corners of the world, the `AMR` package i
One of the most powerful functions of this package, aside from calculating and plotting AMR, is selecting and filtering based on antimicrobial columns. This can be done using the so-called [antimicrobial selectors](https://amr-for-r.org/reference/antimicrobial_selectors.html), which work in base R, `dplyr` and `data.table`. One of the most powerful functions of this package, aside from calculating and plotting AMR, is selecting and filtering based on antimicrobial columns. This can be done using the so-called [antimicrobial selectors](https://amr-for-r.org/reference/antimicrobial_selectors.html), which work in base R, `dplyr` and `data.table`.
```{r} ```{r}
# AMR works great with dplyr, but it's not required or neccesary # AMR works great with dplyr, but it's not required or necessary
library(AMR) library(AMR)
library(dplyr, warn.conflicts = FALSE) library(dplyr, warn.conflicts = FALSE)
example_isolates %>% example_isolates |>
mutate(bacteria = mo_fullname()) %>% mutate(bacteria = mo_fullname()) |>
# filtering functions for microorganisms: # filtering functions for microorganisms:
filter(mo_is_gram_negative(), filter(mo_is_gram_negative(),
mo_is_intrinsic_resistant(ab = "cefotax")) %>% mo_is_intrinsic_resistant(ab = "cefotax")) |>
# antimicrobial selectors: # antimicrobial selectors:
select(bacteria, select(bacteria,
aminoglycosides(), aminoglycosides(),
@@ -94,44 +94,46 @@ The `AMR` package supports four types of antibiograms, with support for `r lengt
**For empirical therapy guidance (i.e., coverage estimates), use WISCA** (Weighted-Incidence Syndromic Combination Antibiogram). When a clinician starts empirical treatment, the causative pathogen is unknown. The relevant question is not *"what percentage of E. coli is susceptible?"* but *"what is the probability that this regimen will cover whatever pathogen is causing the infection?"*. WISCA answers that question directly, weighting susceptibility by pathogen incidence and providing credible intervals via Bayesian simulation. See `vignette("WISCA")` for the full explanation. **For empirical therapy guidance (i.e., coverage estimates), use WISCA** (Weighted-Incidence Syndromic Combination Antibiogram). When a clinician starts empirical treatment, the causative pathogen is unknown. The relevant question is not *"what percentage of E. coli is susceptible?"* but *"what is the probability that this regimen will cover whatever pathogen is causing the infection?"*. WISCA answers that question directly, weighting susceptibility by pathogen incidence and providing credible intervals via Bayesian simulation. See `vignette("WISCA")` for the full explanation.
```{r} ```{r}
wisca(example_isolates, example_isolates |>
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), top_n_microorganisms(n = 10) |> # 10 most common species
minimum = 10) # Recommended threshold: >=30 wisca(antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"),
minimum = 10) # Recommended threshold: >=30
``` ```
WISCA supports stratification by any clinical variable, so you can generate syndrome-specific or ward-specific coverage estimates: WISCA supports stratification by any clinical variable, so you can generate syndrome-specific or ward-specific coverage estimates:
```{r} ```{r}
wisca(example_isolates, example_isolates |>
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), top_n_microorganisms(n = 10) |>
syndromic_group = "ward", wisca(antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"),
minimum = 10) # Recommended threshold: >=30 syndromic_group = "ward",
minimum = 10) # Recommended threshold: >=30
``` ```
**For AMR surveillance**, traditional antibiograms remain the right tool for tracking resistance per species over time: **For AMR surveillance**, traditional antibiograms remain the right tool for tracking resistance per species over time:
```{r} ```{r}
antibiogram(example_isolates, example_isolates |>
mo_transform = "gramstain", antibiogram(mo_transform = "gramstain",
antimicrobials = c("AMC", carbapenems(), "TZP")) antimicrobials = c("AMC", carbapenems(), "TZP"))
``` ```
Combination antibiograms show the additional coverage gained by adding a second agent, stratified by species: Combination antibiograms show the additional coverage gained by adding a second agent, stratified by species:
```{r} ```{r}
antibiogram(example_isolates, example_isolates |>
mo_transform = "gramstain", antibiogram(mo_transform = "gramstain",
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN")) antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"))
``` ```
Like many other functions in this package, `antibiogram()` and `wisca()` come with support for `r length(AMR:::LANGUAGES_SUPPORTED)` languages that are often detected automatically based on system language: Like many other functions in this package, `antibiogram()` and `wisca()` come with support for `r length(AMR:::LANGUAGES_SUPPORTED)` languages that are often detected automatically based on system language:
```{r} ```{r}
antibiogram(example_isolates, example_isolates |>
antimicrobials = c("cipro", "tobra", "genta"), # any arbitrary name or code will work antibiogram(antimicrobials = c("cipro", "tobra", "genta"), # any arbitrary name or code will work
mo_transform = "gramstain", mo_transform = "gramstain",
ab_transform = "name", ab_transform = "name",
language = "uk") # Ukrainian language = "uk") # Ukrainian
``` ```
### Interpreting and plotting MIC and SIR values ### Interpreting and plotting MIC and SIR values
@@ -176,9 +178,9 @@ ggplot(data.frame(mic = some_mic_values,
For a manual approach, you can use the `resistance` or `susceptibility()` function: For a manual approach, you can use the `resistance` or `susceptibility()` function:
```{r} ```{r}
example_isolates %>% example_isolates |>
# group by ward: # group by ward:
group_by(ward) %>% group_by(ward) |>
# calculate AMR using resistance() for gentamicin and tobramycin # calculate AMR using resistance() for gentamicin and tobramycin
# and get their 95% confidence intervals using sir_confidence_interval(): # and get their 95% confidence intervals using sir_confidence_interval():
summarise(across(c(GEN, TOB), summarise(across(c(GEN, TOB),
@@ -192,9 +194,9 @@ Or use [antimicrobial selectors](https://amr-for-r.org/reference/antimicrobial_s
library(AMR) library(AMR)
library(dplyr) library(dplyr)
out <- example_isolates %>% out <- example_isolates |>
# group by ward: # group by ward:
group_by(ward) %>% group_by(ward) |>
# calculate AMR using resistance(), over all aminoglycosides and polymyxins: # calculate AMR using resistance(), over all aminoglycosides and polymyxins:
summarise(across(c(aminoglycosides(), polymyxins()), summarise(across(c(aminoglycosides(), polymyxins()),
resistance)) resistance))
@@ -203,12 +205,12 @@ out
```{r} ```{r}
# transform the antibiotic columns to names: # transform the antibiotic columns to names:
out %>% set_ab_names() out |> set_ab_names()
``` ```
```{r} ```{r}
# transform the antibiotic column to ATC codes: # transform the antibiotic column to ATC codes:
out %>% set_ab_names(property = "atc") out |> set_ab_names(property = "atc")
``` ```
## What else can you do with this package? ## What else can you do with this package?
+45 -36
View File
@@ -59,8 +59,8 @@ times](https://scholar.google.com/citations?view_op=view_citation&hl=en&citation
in scientific research. in scientific research.
After installing this package, R knows [**~103 000 distinct microbial After installing this package, R knows [**~103 000 distinct microbial
species**](./reference/microorganisms.html) (updated April 2026) and all species**](./reference/microorganisms.html) (updated October 2026) and
[**~630 antimicrobial and antiviral all [**~630 antimicrobial and antiviral
drugs**](./reference/antimicrobials.html) by name and code (including drugs**](./reference/antimicrobials.html) by name and code (including
ATC, EARS-Net, ASIARS-Net, PubChem, LOINC and SNOMED CT), and knows all ATC, EARS-Net, ASIARS-Net, PubChem, LOINC and SNOMED CT), and knows all
about valid SIR and MIC values. The integral clinical breakpoint about valid SIR and MIC values. The integral clinical breakpoint
@@ -154,15 +154,15 @@ selectors](https://amr-for-r.org/reference/antimicrobial_selectors.html),
which work in base R, `dplyr` and `data.table`. which work in base R, `dplyr` and `data.table`.
``` r ``` r
# AMR works great with dplyr, but it's not required or neccesary # AMR works great with dplyr, but it's not required or necessary
library(AMR) library(AMR)
library(dplyr, warn.conflicts = FALSE) library(dplyr, warn.conflicts = FALSE)
example_isolates %>% example_isolates |>
mutate(bacteria = mo_fullname()) %>% mutate(bacteria = mo_fullname()) |>
# filtering functions for microorganisms: # filtering functions for microorganisms:
filter(mo_is_gram_negative(), filter(mo_is_gram_negative(),
mo_is_intrinsic_resistant(ab = "cefotax")) %>% mo_is_intrinsic_resistant(ab = "cefotax")) |>
# antimicrobial selectors: # antimicrobial selectors:
select(bacteria, select(bacteria,
aminoglycosides(), aminoglycosides(),
@@ -217,38 +217,46 @@ credible intervals via Bayesian simulation. See `vignette("WISCA")` for
the full explanation. the full explanation.
``` r ``` r
wisca(example_isolates, example_isolates |>
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), top_n_microorganisms(n = 10) |> # 10 most common species
minimum = 10) # Recommended threshold: >=30 wisca(antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"),
minimum = 10) # Recommended threshold: >=30
``` ```
| Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin | | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin |
|:------------------------|:-------------------------------------|:-------------------------------------| |:------------------------|:-------------------------------------|:-------------------------------------|
| 70.2% (65-75.4%) | 93.6% (92-95.1%) | 89.9% (87-92.5%) | | 73% (67.7-79%) | 98.7% (98-99.2%) | 93.8% (90.5-96.6%) |
WISCA supports stratification by any clinical variable, so you can WISCA supports stratification by any clinical variable, so you can
generate syndrome-specific or ward-specific coverage estimates: generate syndrome-specific or ward-specific coverage estimates:
``` r ``` r
wisca(example_isolates, example_isolates |>
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), top_n_microorganisms(n = 10) |>
syndromic_group = "ward", wisca(antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"),
minimum = 10) # Recommended threshold: >=30 syndromic_group = "ward",
minimum = 10) # Recommended threshold: >=30
#> Warning: In 3 of 9 WISCA regimen(s) or group(s), the prior weight of 1 per pathogen accounts for more than
#> 5% of the pathogen incidence (up to 6.4%), since there are few isolates relative to the number of
#> pathogens, i.e., rare pathogens or a small group. Because WISCA gives every pathogen a prior weight
#> of 1 (Bielicki et al., 2016), this can strongly lower the estimated coverage without reflecting the
#> clinical expectation. Consider limiting the analysis to the most common pathogens with
#> `top_n_microorganisms()`, or using fewer and larger groups.
``` ```
| Syndromic Group | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin | | Syndromic Group | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin |
|:----------------|:------------------------|:-------------------------------------|:-------------------------------------| |:----------------|:------------------------|:-------------------------------------|:-------------------------------------|
| Clinical | 74.8% (69-80.3%) | 93.7% (92.1-95.1%) | 90.4% (86.8-93.3%) | | Clinical | 78.9% (72.8-85.6%) | 98.7% (97.8-99.3%) | 94.5% (90.3-97.6%) |
| ICU | 56.7% (48.2-65.4%) | 86.6% (83.3-89.6%) | 83.1% (78.2-87.5%) | | ICU | 57.3% (48.9-69.5%) | 97.5% (95.7-98.8%) | 91.7% (85.8-96.3%) |
| Outpatient | 57.6% (46-69%) | 76.4% (69.8-81.7%) | 68% (57.7-77.5%) | | Outpatient | 68% (49.4-88.5%) | 95.7% (90.7-98.7%) | 79.3% (62.7-93.8%) |
**For AMR surveillance**, traditional antibiograms remain the right tool **For AMR surveillance**, traditional antibiograms remain the right tool
for tracking resistance per species over time: for tracking resistance per species over time:
``` r ``` r
antibiogram(example_isolates, example_isolates |>
mo_transform = "gramstain", antibiogram(mo_transform = "gramstain",
antimicrobials = c("AMC", carbapenems(), "TZP")) antimicrobials = c("AMC", carbapenems(), "TZP"))
#> ℹ For `carbapenems()` using columns IPM (imipenem) and MEM (meropenem) #> ℹ For `carbapenems()` using columns IPM (imipenem) and MEM (meropenem)
``` ```
@@ -261,9 +269,9 @@ Combination antibiograms show the additional coverage gained by adding a
second agent, stratified by species: second agent, stratified by species:
``` r ``` r
antibiogram(example_isolates, example_isolates |>
mo_transform = "gramstain", antibiogram(mo_transform = "gramstain",
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN")) antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"))
``` ```
| Pathogen | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin | | Pathogen | Piperacillin/tazobactam | Piperacillin/tazobactam + Gentamicin | Piperacillin/tazobactam + Tobramycin |
@@ -276,11 +284,11 @@ come with support for 28 languages that are often detected automatically
based on system language: based on system language:
``` r ``` r
antibiogram(example_isolates, example_isolates |>
antimicrobials = c("cipro", "tobra", "genta"), # any arbitrary name or code will work antibiogram(antimicrobials = c("cipro", "tobra", "genta"), # any arbitrary name or code will work
mo_transform = "gramstain", mo_transform = "gramstain",
ab_transform = "name", ab_transform = "name",
language = "uk") # Ukrainian language = "uk") # Ukrainian
``` ```
| Збудник | Гентаміцин | Тобраміцин | Ципрофлоксацин | | Збудник | Гентаміцин | Тобраміцин | Ципрофлоксацин |
@@ -334,9 +342,9 @@ For a manual approach, you can use the `resistance` or
`susceptibility()` function: `susceptibility()` function:
``` r ``` r
example_isolates %>% example_isolates |>
# group by ward: # group by ward:
group_by(ward) %>% group_by(ward) |>
# calculate AMR using resistance() for gentamicin and tobramycin # calculate AMR using resistance() for gentamicin and tobramycin
# and get their 95% confidence intervals using sir_confidence_interval(): # and get their 95% confidence intervals using sir_confidence_interval():
summarise(across(c(GEN, TOB), summarise(across(c(GEN, TOB),
@@ -358,9 +366,9 @@ to select a series of antibiotic columns:
library(AMR) library(AMR)
library(dplyr) library(dplyr)
out <- example_isolates %>% out <- example_isolates |>
# group by ward: # group by ward:
group_by(ward) %>% group_by(ward) |>
# calculate AMR using resistance(), over all aminoglycosides and polymyxins: # calculate AMR using resistance(), over all aminoglycosides and polymyxins:
summarise(across(c(aminoglycosides(), polymyxins()), summarise(across(c(aminoglycosides(), polymyxins()),
resistance)) resistance))
@@ -384,7 +392,7 @@ out
``` r ``` r
# transform the antibiotic columns to names: # transform the antibiotic columns to names:
out %>% set_ab_names() out |> set_ab_names()
#> # A tibble: 3 × 6 #> # A tibble: 3 × 6
#> ward gentamicin tobramycin amikacin kanamycin colistin #> ward gentamicin tobramycin amikacin kanamycin colistin
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
@@ -395,7 +403,7 @@ out %>% set_ab_names()
``` r ``` r
# transform the antibiotic column to ATC codes: # transform the antibiotic column to ATC codes:
out %>% set_ab_names(property = "atc") out |> set_ab_names(property = "atc")
#> # A tibble: 3 × 6 #> # A tibble: 3 × 6
#> ward J01GB03 J01GB01 J01GB06 J01GB04 J01XB01 #> ward J01GB03 J01GB01 J01GB06 J01GB04 J01XB01
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
@@ -427,7 +435,7 @@ data analysis. This package can be used for:
organisms (MDRO) ([tutorial](./reference/mdro.html)) organisms (MDRO) ([tutorial](./reference/mdro.html))
- Calculating (empirical) susceptibility of both mono therapy and - Calculating (empirical) susceptibility of both mono therapy and
combination therapies ([tutorial](./articles/AMR.html)) combination therapies ([tutorial](./articles/AMR.html))
- Apply AMR functions in predictive modelling - Applying AMR functions in predictive modelling
([tutorial](./articles/AMR_with_tidymodels.html)) ([tutorial](./articles/AMR_with_tidymodels.html))
- Getting properties for any microorganism (like Gram stain, species, - Getting properties for any microorganism (like Gram stain, species,
genus or family) ([manual](./reference/mo_property.html)) genus or family) ([manual](./reference/mo_property.html))
@@ -435,7 +443,8 @@ data analysis. This package can be used for:
EARS-Net/ATC/LOINC/PubChem, defined daily dose or trade name) EARS-Net/ATC/LOINC/PubChem, defined daily dose or trade name)
([manual](./reference/ab_property.html)) ([manual](./reference/ab_property.html))
- Plotting antimicrobial resistance ([tutorial](./articles/AMR.html)) - Plotting antimicrobial resistance ([tutorial](./articles/AMR.html))
- Applying EUCAST expert rules ([manual](./reference/eucast_rules.html)) - Applying EUCAST and CLSI interpretive rules
([manual](./reference/interpretive_rules.html))
- Getting SNOMED codes of a microorganism, or getting properties of a - Getting SNOMED codes of a microorganism, or getting properties of a
microorganism based on a SNOMED code microorganism based on a SNOMED code
([manual](./reference/mo_property.html)) ([manual](./reference/mo_property.html))
+1 -1
View File
@@ -5,7 +5,7 @@
\alias{custom_eucast_rules} \alias{custom_eucast_rules}
\alias{ab_class} \alias{ab_class}
\alias{ab_selector} \alias{ab_selector}
\title{Deprecated Functions, Arguments, or Datasets} \title{Deprecated Functions, Arguments, or Data Sets}
\usage{ \usage{
custom_eucast_rules(...) custom_eucast_rules(...)
+1 -1
View File
@@ -2,7 +2,7 @@
% Please edit documentation in R/aa_options.R % Please edit documentation in R/aa_options.R
\name{AMR-options} \name{AMR-options}
\alias{AMR-options} \alias{AMR-options}
\title{Options for the AMR package} \title{Options for the AMR Package}
\description{ \description{
This is an overview of all the package-specific options you can set in the \code{AMR} package. Set them using the \code{\link[=options]{options()}} function, e.g.: This is an overview of all the package-specific options you can set in the \code{AMR} package. Set them using the \code{\link[=options]{options()}} function, e.g.:
+1 -1
View File
@@ -17,7 +17,7 @@
\alias{ab_info} \alias{ab_info}
\alias{ab_url} \alias{ab_url}
\alias{set_ab_names} \alias{set_ab_names}
\title{Get Properties of an Antibiotic} \title{Get Properties of an Antimicrobial Drug}
\usage{ \usage{
ab_name(x, language = get_AMR_locale(), tolower = FALSE, ...) ab_name(x, language = get_AMR_locale(), tolower = FALSE, ...)
+2 -1
View File
@@ -56,6 +56,8 @@ See \code{\link[recipes:selections]{selections()}} for more details.}
\item{trained}{A logical to indicate if the quantities for preprocessing have \item{trained}{A logical to indicate if the quantities for preprocessing have
been estimated.} been estimated.}
\item{columns}{A character string of the selected variable names. This is \code{NULL} until the step is trained by \code{\link[recipes:prep]{recipes::prep()}}, and should not be set manually.}
\item{skip}{A logical. Should the step be skipped when the recipe is baked by \item{skip}{A logical. Should the step be skipped when the recipe is baked by
\code{\link[recipes:bake]{bake()}}? While all operations are baked when \code{\link[recipes:prep]{prep()}} is run, some \code{\link[recipes:bake]{bake()}}? While all operations are baked when \code{\link[recipes:prep]{prep()}} is run, some
operations may not be able to be conducted on new data (e.g. processing the operations may not be able to be conducted on new data (e.g. processing the
@@ -147,4 +149,3 @@ if (require("tidymodels")) {
\seealso{ \seealso{
\code{\link[recipes:recipe]{recipes::recipe()}}, \code{\link[=as.sir]{as.sir()}}, \code{\link[=as.mic]{as.mic()}}, \code{\link[=as.disk]{as.disk()}} \code{\link[recipes:recipe]{recipes::recipe()}}, \code{\link[=as.sir]{as.sir()}}, \code{\link[=as.mic]{as.mic()}}, \code{\link[=as.disk]{as.disk()}}
} }
\keyword{internal}
+9 -4
View File
@@ -268,6 +268,10 @@ It does so by combining two quantities: the relative incidence of each pathogen
\strong{Prior distributions:} Pathogen incidence uses a non-informative \eqn{Dirichlet(1, 1, \ldots, 1)} prior. Susceptibility proportions use the Jeffreys prior, \eqn{\beta(0.5, 0.5)}, except for pathogen-drug combinations with known intrinsic resistance, which use a strongly informative \eqn{\beta(1, 9999)} prior that forces near-zero susceptibility regardless of observed data. Intrinsic resistance is determined using the \link{intrinsic_resistant} data set, which is based on \href{https://www.eucast.org/bacteria/important-additional-information/expert-rules/}{'EUCAST Expected Resistant Phenotypes' v1.2} (2023). \strong{Prior distributions:} Pathogen incidence uses a non-informative \eqn{Dirichlet(1, 1, \ldots, 1)} prior. Susceptibility proportions use the Jeffreys prior, \eqn{\beta(0.5, 0.5)}, except for pathogen-drug combinations with known intrinsic resistance, which use a strongly informative \eqn{\beta(1, 9999)} prior that forces near-zero susceptibility regardless of observed data. Intrinsic resistance is determined using the \link{intrinsic_resistant} data set, which is based on \href{https://www.eucast.org/bacteria/important-additional-information/expert-rules/}{'EUCAST Expected Resistant Phenotypes' v1.2} (2023).
\strong{Pathogens included in the model:} For each regimen (and group), only pathogens that occur in the data and have at least one test result for the regimen are included. Pathogens without any test result for a regimen do not contribute to its coverage, nor to its \code{n_total} in \code{long_numeric}. Use \code{\link[=retrieve_wisca_parameters]{retrieve_wisca_parameters()}} to get the parameters that were used.
\strong{Rare pathogens and small groups:} Since every pathogen receives a prior weight of 1 in the Dirichlet distribution, the prior weighs heavily when there are few isolates relative to the number of pathogens, i.e., with pathogens found only once or a few times, or in small groups. This pulls the incidence towards a uniform distribution, and the very uncertain susceptibility of rare pathogens can then strongly lower the estimated coverage, without reflecting the clinical expectation. A warning is therefore given when this prior weight accounts for more than 5\% of the pathogen incidence in any regimen (or group). Consider limiting the analysis to the most common pathogens using \code{\link[=top_n_microorganisms]{top_n_microorganisms()}}, or using fewer and larger groups.
\strong{Interpreting the output:} Overlapping credible intervals between regimens indicate no significant difference in coverage; if a narrower-spectrum regimen overlaps with a broader one, the narrower-spectrum option may be preferred on stewardship grounds. Non-overlapping intervals indicate a clinically meaningful difference. For small sample sizes, consider pooling data from multiple sites to improve precision, provided pathogen distributions are sufficiently similar (Bielicki \emph{et al.}, 2016). \strong{Interpreting the output:} Overlapping credible intervals between regimens indicate no significant difference in coverage; if a narrower-spectrum regimen overlaps with a broader one, the narrower-spectrum option may be preferred on stewardship grounds. Non-overlapping intervals indicate a clinically meaningful difference. For small sample sizes, consider pooling data from multiple sites to improve precision, provided pathogen distributions are sufficiently similar (Bielicki \emph{et al.}, 2016).
For the full mathematical derivation and worked examples, see the \href{https://amr-for-r.org/articles/WISCA.html}{WISCA vignette}. For the full mathematical derivation and worked examples, see the \href{https://amr-for-r.org/articles/WISCA.html}{WISCA vignette}.
@@ -353,12 +357,13 @@ example_isolates
# basic WISCA: empirical coverage per regimen, weighted by pathogen # basic WISCA: empirical coverage per regimen, weighted by pathogen
# incidence, with 95\% credible intervals # incidence, with 95\% credible intervals
wisca(example_isolates, # (10 most common species, since rare species can distort coverage estimates)
wisca(top_n_microorganisms(example_isolates, n = 10),
antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN") antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN")
) )
# equivalent using antibiogram(): # equivalent using antibiogram():
antibiogram(example_isolates, antibiogram(top_n_microorganisms(example_isolates, n = 10),
antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN"), antimicrobials = c("AMC", "AMC+CIP", "AMC+GEN"),
wisca = TRUE wisca = TRUE
) )
@@ -443,7 +448,7 @@ antibiogram(ex1,
# Print the output for R Markdown / Quarto ----------------------------- # Print the output for R Markdown / Quarto -----------------------------
ureido <- wisca(example_isolates, ureido <- wisca(top_n_microorganisms(example_isolates, n = 10),
antimicrobials = ureidopenicillins(), antimicrobials = ureidopenicillins(),
syndromic_group = "ward" syndromic_group = "ward"
) )
@@ -461,7 +466,7 @@ ab1 <- antibiogram(example_isolates,
antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"), antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"),
mo_transform = "gramstain" mo_transform = "gramstain"
) )
ab2 <- wisca(example_isolates, ab2 <- wisca(top_n_microorganisms(example_isolates, n = 10),
antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"), antimicrobials = c("AMC", "CIP", "TZP", "TZP+TOB"),
syndromic_group = "ward" syndromic_group = "ward"
) )
+1 -1
View File
@@ -6,7 +6,7 @@
\alias{is.ab} \alias{is.ab}
\alias{ab_reset_session} \alias{ab_reset_session}
\alias{NA_ab_} \alias{NA_ab_}
\title{Transform Input to an Antibiotic ID} \title{Transform Input to an Antimicrobial Drug ID}
\usage{ \usage{
as.ab( as.ab(
x, x,
+12
View File
@@ -162,6 +162,18 @@ Key antimicrobials are internally selected using the \code{\link[=key_antimicrob
The default method is phenotype-based (using \code{type = "points"}) and episode-based (using \code{episode_days = 365}). This makes sure that every genus-species combination is selected per patient once per year, while taking into account all antimicrobial test results. If no antimicrobial test results are available in the data set, only the episode-based method is applied at default. The default method is phenotype-based (using \code{type = "points"}) and episode-based (using \code{episode_days = 365}). This makes sure that every genus-species combination is selected per patient once per year, while taking into account all antimicrobial test results. If no antimicrobial test results are available in the data set, only the episode-based method is applied at default.
} }
\subsection{First isolates per additional variable}{
To determine first isolates separately for the values of any additional variable, such as a ward, a specimen group, a molecular marker or a locally defined classification, group the data with \code{\link[dplyr:group_by]{dplyr::group_by()}} before calling \code{\link[=first_isolate]{first_isolate()}}. Every group is then handled as a separate data set, so the selection, including the start of every episode, is determined per patient, genus-species combination and group value. This works with all methods described above:
\if{html}{\out{<div class="sourceCode r">}}\preformatted{your_data \%>\%
group_by(ward) \%>\%
mutate(first = first_isolate())
}\if{html}{\out{</div>}}
Be aware that grouping on a variable that is related to antimicrobial resistance, such as a resistance mechanism or the output of \code{\link[=mdro]{mdro()}}, retains more isolates of patients whose isolates change category. This deviates from CLSI M39 and generally leads to an overestimation of resistance, as the selection then depends on the outcome that is being estimated.
}
} }
\examples{ \examples{
# `example_isolates` is a data set available in the AMR package. # `example_isolates` is a data set available in the AMR package.
+3 -2
View File
@@ -48,6 +48,7 @@ geom_sir(
labels_sir_count( labels_sir_count(
position = NULL, position = NULL,
x = "antibiotic", x = "antibiotic",
fill = "interpretation",
translate_ab = "name", translate_ab = "name",
minimum = 30, minimum = 30,
language = get_AMR_locale(), language = get_AMR_locale(),
@@ -64,9 +65,9 @@ labels_sir_count(
\item{x}{Variable to show on x axis, either \code{"antibiotic"} (default) or \code{"interpretation"} or a grouping variable.} \item{x}{Variable to show on x axis, either \code{"antibiotic"} (default) or \code{"interpretation"} or a grouping variable.}
\item{fill}{Variable to categorise using the plots legend, either \code{"antibiotic"} (default) or \code{"interpretation"} or a grouping variable.} \item{fill}{Variable to categorise using the plots legend, either \code{"interpretation"} (default) or \code{"antibiotic"} or a grouping variable. For \code{\link[=labels_sir_count]{labels_sir_count()}}, this must be the same as the \code{fill} of \code{\link[=geom_sir]{geom_sir()}}, so that the labels are placed on the right bars. If the interpretation is not shown in the plot (i.e., neither in \code{x}, \code{fill}, nor \code{facet}), only the susceptible proportion is plotted, which follows the guideline (see \code{guideline}): S+SDD+I for EUCAST, S+SDD for CLSI, and S if \code{guideline = NULL}.}
\item{facet}{Variable to split plots by, either \code{"interpretation"} (default) or \code{"antibiotic"} or a grouping variable.} \item{facet}{Variable to split plots by, either \code{"interpretation"} or \code{"antibiotic"} or a grouping variable. If there is more than one antimicrobial and the antimicrobial is not shown in the plot (i.e., neither in \code{x}, \code{fill}, nor \code{facet}), \code{\link[=ggplot_sir]{ggplot_sir()}} splits the plot by antimicrobial, as their bars would otherwise be stacked: using \code{facet = "antibiotic"} if \code{facet} is not set, and otherwise using a grid of \code{facet} by antimicrobial. Note that \code{\link[=geom_sir]{geom_sir()}} cannot do this, so add \code{\link[=facet_sir]{facet_sir()}} in that case.}
\item{breaks}{A \link{numeric} vector of positions.} \item{breaks}{A \link{numeric} vector of positions.}
+1 -1
View File
@@ -2,7 +2,7 @@
% Please edit documentation in R/guess_ab_col.R % Please edit documentation in R/guess_ab_col.R
\name{guess_ab_col} \name{guess_ab_col}
\alias{guess_ab_col} \alias{guess_ab_col}
\title{Guess Antibiotic Column} \title{Guess Antimicrobial Column}
\usage{ \usage{
guess_ab_col( guess_ab_col(
x = NULL, x = NULL,
+1 -1
View File
@@ -5,7 +5,7 @@
\alias{intrinsic_resistant} \alias{intrinsic_resistant}
\title{Data Set Denoting Bacterial Intrinsic Resistance} \title{Data Set Denoting Bacterial Intrinsic Resistance}
\format{ \format{
A \link[tibble:tibble]{tibble} with 307 684 observations and 3 variables: A \link[tibble:tibble]{tibble} with 307 746 observations and 3 variables:
\itemize{ \itemize{
\item \code{mo}\cr Microorganism ID which occurs in \code{\link[=microorganisms]{microorganisms$mo}}. Names can be retrieved using \code{\link[=mo_name]{mo_name()}}. \item \code{mo}\cr Microorganism ID which occurs in \code{\link[=microorganisms]{microorganisms$mo}}. Names can be retrieved using \code{\link[=mo_name]{mo_name()}}.
\item \code{ab}\cr Antimicrobial ID which occurs in \code{\link[=antimicrobials]{antimicrobials$ab}}. Names can be retrieved using \code{\link[=ab_name]{ab_name()}}. \item \code{ab}\cr Antimicrobial ID which occurs in \code{\link[=antimicrobials]{antimicrobials$ab}}. Names can be retrieved using \code{\link[=ab_name]{ab_name()}}.
+17 -6
View File
@@ -3,30 +3,41 @@
\name{italicise_taxonomy} \name{italicise_taxonomy}
\alias{italicise_taxonomy} \alias{italicise_taxonomy}
\alias{italicize_taxonomy} \alias{italicize_taxonomy}
\title{Italicise Taxonomic Families, Genera, Species, Subspecies} \title{Italicise Taxonomic Families, Genera, Species, and Subspecies}
\usage{ \usage{
italicise_taxonomy(string, type = c("markdown", "ansi", "html")) italicise_taxonomy(string, type = c("markdown", "ansi", "html", "plotmath"))
italicize_taxonomy(string, type = c("markdown", "ansi", "html")) italicize_taxonomy(string, type = c("markdown", "ansi", "html", "plotmath"))
} }
\arguments{ \arguments{
\item{string}{A \link{character} (vector).} \item{string}{A \link{character} (vector).}
\item{type}{Type of conversion of the taxonomic names, either "markdown", "html" or "ansi", see \emph{Details}.} \item{type}{Type of conversion of the taxonomic names, either "markdown", "html", "ansi" or "plotmath", see \emph{Details}.}
}
\value{
For "markdown", "html" and "ansi", a \link{character} vector. For "plotmath", an \link{expression} vector. Both have the same length as \code{string} and keep \code{NA} values.
} }
\description{ \description{
According to the binomial nomenclature, the lowest four taxonomic levels (family, genus, species, subspecies) should be printed in italics. This function finds taxonomic names within strings and makes them italic. According to the binomial nomenclature, the lowest four taxonomic levels (family, genus, species, subspecies) should be printed in italics. This function finds taxonomic names within strings and makes them italic.
} }
\details{ \details{
This function finds the taxonomic names and makes them italic based on the \link{microorganisms} data set. This function finds the taxonomic names and makes them italic based on the \link{microorganisms} data set. Family and genus names are italicised anywhere in the text. Species and subspecies names are only italicised when they follow a genus, an abbreviated genus (such as "E." in "E. coli"), or another species or subspecies name, so that ordinary words that are also species names (such as "coli" or "major") stay upright. Subspecies may also follow "subsp.", "ssp." or "var.", which stay upright themselves, e.g., "\emph{K. pneumoniae} subsp. \emph{ozaenae}". \emph{Salmonella} serovars (such as Typhimurium) and groups are not italicised, as is the convention. Punctuation around the names, such as brackets and commas, stays outside the italics.
The taxonomic names can be italicised using markdown (the default) by adding \code{*} before and after the taxonomic names, or \verb{<i>} and \verb{</i>} when using html. When using 'ansi', ANSI colours will be added using \verb{\\033[3m} before and \verb{\\033[23m} after the taxonomic names. If multiple ANSI colours are not available, no conversion will occur. The taxonomic names can be italicised using markdown (the default) by adding \code{*} before and after the taxonomic names, or \verb{<i>} and \verb{</i>} when using html. When using 'ansi', ANSI colours will be added using \verb{\\033[3m} before and \verb{\\033[23m} after the taxonomic names. If multiple ANSI colours are not available, no conversion will occur.
This function also supports abbreviation of the genus if it is followed by a species, such as "E. coli" and "K. pneumoniae ozaenae". Use \code{type = "plotmath"} to get an \link{expression} vector for plots (see \link[grDevices:plotmath]{plotmath}), which works for titles, axes and legends in both base \R and \code{ggplot2}. For axis and legend labels, the function \code{\link[=labels_taxonomy]{labels_taxonomy()}} does exactly this and can directly be used as labeller, e.g., \code{ggplot2::scale_x_discrete(labels = labels_taxonomy)}.
} }
\examples{ \examples{
italicise_taxonomy("An overview of Staphylococcus aureus isolates") italicise_taxonomy("An overview of Staphylococcus aureus isolates")
italicise_taxonomy("An overview of S. aureus isolates") italicise_taxonomy("An overview of S. aureus isolates")
italicise_taxonomy("Infections with E. coli (and coli bacteria in general)")
cat(italicise_taxonomy("An overview of S. aureus isolates", type = "ansi")) cat(italicise_taxonomy("An overview of S. aureus isolates", type = "ansi"))
# for plots:
italicise_taxonomy("An overview of S. aureus isolates", type = "plotmath")
plot(1:10, main = italicise_taxonomy("Growth of S. aureus", type = "plotmath"))
}
\seealso{
\code{\link[=labels_taxonomy]{labels_taxonomy()}} to italicise taxonomic names in plot labels.
} }
+1 -1
View File
@@ -349,7 +349,7 @@ All functions will, at default, \strong{not} keep old taxonomic properties, as s
\code{\link[=mo_ref]{mo_ref()}} returns the abbreviated authority of the nomenclatural act that created the queried name combination. When \code{keep_synonyms = FALSE} (default), this is the authority of the currently accepted name. When \code{keep_synonyms = TRUE}, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned. \code{\link[=mo_ref]{mo_ref()}} returns the abbreviated authority of the nomenclatural act that created the queried name combination. When \code{keep_synonyms = FALSE} (default), this is the authority of the currently accepted name. When \code{keep_synonyms = TRUE}, this is the authority under which the queried (possibly outdated) name was published. Emendations (changes to the species description without a name change) are not reflected; only the combination or original description authority is returned.
The short name (\code{\link[=mo_shortname]{mo_shortname()}}) returns the first character of the genus and the full species, such as \code{"E. coli"}, for species and subspecies. Exceptions are abbreviations of staphylococci (such as \emph{"CoNS"}, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as \emph{"GBS"}, Group B Streptococci). Please bear in mind that e.g. \emph{E. coli} could mean \emph{Escherichia coli} (domain of Bacteria) as well as \emph{Entamoeba coli} (domain of Protozoa). Returning to the full name will be done using \code{\link[=as.mo]{as.mo()}} internally, giving priority to bacteria and human pathogens, i.e. \code{"E. coli"} will always be considered \emph{Escherichia coli}. As a result, \code{mo_fullname(mo_shortname("Entamoeba coli"))} returns \code{"Escherichia coli"}. The short name (\code{\link[=mo_shortname]{mo_shortname()}}) returns the first character of the genus and the full species, such as \code{"E. coli"}, for species and subspecies. Exceptions are \emph{Salmonella} serovars, which return the serovar (such as \code{"S. Typhi"}), and abbreviations of staphylococci (such as \emph{"CoNS"}, Coagulase-Negative Staphylococci) and beta-haemolytic streptococci (such as \emph{"GBS"}, Group B Streptococci). Please bear in mind that e.g. \emph{E. coli} could mean \emph{Escherichia coli} (domain of Bacteria) as well as \emph{Entamoeba coli} (domain of Protozoa). Returning to the full name will be done using \code{\link[=as.mo]{as.mo()}} internally, giving priority to bacteria and human pathogens, i.e. \code{"E. coli"} will always be considered \emph{Escherichia coli}. As a result, \code{mo_fullname(mo_shortname("Entamoeba coli"))} returns \code{"Escherichia coli"}.
Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), \code{\link[=mo_kingdom]{mo_kingdom()}} and \code{\link[=mo_domain]{mo_domain()}} return different results for bacteria and archaea: \code{\link[=mo_kingdom]{mo_kingdom()}} returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while \code{\link[=mo_domain]{mo_domain()}} returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results. Following the formal introduction of the new kingdom rank into prokaryotic nomenclature in 2024 (\doi{10.1099/ijsem.0.006242}), \code{\link[=mo_kingdom]{mo_kingdom()}} and \code{\link[=mo_domain]{mo_domain()}} return different results for bacteria and archaea: \code{\link[=mo_kingdom]{mo_kingdom()}} returns the new formal kingdom (e.g. "Pseudomonadati", "Bacillati"), while \code{\link[=mo_domain]{mo_domain()}} returns the new domain (e.g. "Bacteria", "Archaea"). For non-prokaryotic organisms, both functions return identical results.
+24 -2
View File
@@ -22,6 +22,7 @@
\alias{scale_sir_colours} \alias{scale_sir_colours}
\alias{scale_sir_colors} \alias{scale_sir_colors}
\alias{theme_sir} \alias{theme_sir}
\alias{labels_taxonomy}
\title{Plotting Helpers for AMR Data Analysis} \title{Plotting Helpers for AMR Data Analysis}
\usage{ \usage{
scale_x_mic(keep_operators = "edges", mic_range = NULL, ...) scale_x_mic(keep_operators = "edges", mic_range = NULL, ...)
@@ -150,6 +151,8 @@ scale_sir_colours(
) )
theme_sir() theme_sir()
labels_taxonomy(x, ...)
} }
\arguments{ \arguments{
\item{keep_operators}{A \link{character} specifying how to handle operators (such as \code{>} and \code{<=}) in the input. Accepts one of three values: \code{"all"} (or \code{TRUE}) to keep all operators, \code{"none"} (or \code{FALSE}) to remove all operators, or \code{"edges"} to keep operators only at both ends of the range.} \item{keep_operators}{A \link{character} specifying how to handle operators (such as \code{>} and \code{<=}) in the input. Accepts one of three values: \code{"all"} (or \code{TRUE}) to keep all operators, \code{"none"} (or \code{FALSE}) to remove all operators, or \code{"edges"} to keep operators only at both ends of the range.}
@@ -164,7 +167,7 @@ theme_sir()
\item{eucast_I}{A \link{logical} to indicate whether the 'I' must be interpreted as "Susceptible, under increased exposure". Will be \code{TRUE} if the default \link[=as.sir]{AMR interpretation guideline} is set to EUCAST 2019 or later (which is the default). With \code{FALSE}, it will be interpreted as "Intermediate".} \item{eucast_I}{A \link{logical} to indicate whether the 'I' must be interpreted as "Susceptible, under increased exposure". Will be \code{TRUE} if the default \link[=as.sir]{AMR interpretation guideline} is set to EUCAST 2019 or later (which is the default). With \code{FALSE}, it will be interpreted as "Intermediate".}
\item{x, object}{Values created with \code{\link[=as.mic]{as.mic()}}, \code{\link[=as.disk]{as.disk()}} or \code{\link[=as.sir]{as.sir()}} (or their \verb{random_*} variants, such as \code{\link[=random_mic]{random_mic()}}).} \item{x, object}{Values created with \code{\link[=as.mic]{as.mic()}}, \code{\link[=as.disk]{as.disk()}} or \code{\link[=as.sir]{as.sir()}} (or their \verb{random_*} variants, such as \code{\link[=random_mic]{random_mic()}}). For \code{\link[=labels_taxonomy]{labels_taxonomy()}}, any values to use as labels, such as the breaks of an axis.}
\item{mo}{Any (vector of) text that can be coerced to a valid microorganism code with \code{\link[=as.mo]{as.mo()}}.} \item{mo}{Any (vector of) text that can be coerced to a valid microorganism code with \code{\link[=as.mo]{as.mo()}}.}
@@ -182,7 +185,7 @@ theme_sir()
\item{breakpoint_type}{The type of breakpoints to use, either \code{"ECOFF"}, \code{"animal"}, or \code{"human"}. ECOFF stands for Epidemiological Cut-Off values. The default is \code{"human"}, which can also be set with the package option \code{\link[=AMR-options]{AMR_breakpoint_type}}. If \code{host} is set to values of veterinary species, this will automatically be set to \code{"animal"}.} \item{breakpoint_type}{The type of breakpoints to use, either \code{"ECOFF"}, \code{"animal"}, or \code{"human"}. ECOFF stands for Epidemiological Cut-Off values. The default is \code{"human"}, which can also be set with the package option \code{\link[=AMR-options]{AMR_breakpoint_type}}. If \code{host} is set to values of veterinary species, this will automatically be set to \code{"animal"}.}
\item{facet}{Variable to split plots by, either \code{"interpretation"} (default) or \code{"antibiotic"} or a grouping variable.} \item{facet}{Variable to split plots by, either \code{"interpretation"} or \code{"antibiotic"} or a grouping variable. If there is more than one antimicrobial and the antimicrobial is not shown in the plot (i.e., neither in \code{x}, \code{fill}, nor \code{facet}), \code{\link[=ggplot_sir]{ggplot_sir()}} splits the plot by antimicrobial, as their bars would otherwise be stacked: using \code{facet = "antibiotic"} if \code{facet} is not set, and otherwise using a grid of \code{facet} by antimicrobial. Note that \code{\link[=geom_sir]{geom_sir()}} cannot do this, so add \code{\link[=facet_sir]{facet_sir()}} in that case.}
\item{nrow}{(when using \code{facet}) number of rows.} \item{nrow}{(when using \code{facet}) number of rows.}
@@ -229,6 +232,13 @@ This package contains more functions that extend the \code{ggplot2} package, to
\item \code{\link[=theme_sir]{theme_sir()}} is a \link[ggplot2:theme]{ggplot2 theme} with minimal distraction. \item \code{\link[=theme_sir]{theme_sir()}} is a \link[ggplot2:theme]{ggplot2 theme} with minimal distraction.
\item \code{\link[=labels_sir_count]{labels_sir_count()}} prints data labels on the bars with percentage and number of isolates, using \code{\link[ggplot2:geom_text]{ggplot2::geom_text()}}. \item \code{\link[=labels_sir_count]{labels_sir_count()}} prints data labels on the bars with percentage and number of isolates, using \code{\link[ggplot2:geom_text]{ggplot2::geom_text()}}.
} }
}
\subsection{Taxonomic Names in Labels}{
The function \code{\link[=labels_taxonomy]{labels_taxonomy()}} prints taxonomic names in axis and legend labels in italics, as is the convention in microbiology, e.g., \emph{E. coli} and \emph{Klebsiella pneumoniae}. It can directly be used as labeller in \code{ggplot2}, e.g., \code{scale_x_discrete(labels = labels_taxonomy)}, and in base \R, e.g., \code{axis(1, at = 1:3, labels = labels_taxonomy(names))}. Other words, such as "Other" or "CoNS", stay upright. It uses \code{\link[=italicise_taxonomy]{italicise_taxonomy()}} with \code{type = "plotmath"}, which also works for titles. It returns an \link{expression} vector of the same length as \code{x}; other arguments in \code{...} are ignored.
The plotting functions of this package apply this automatically wherever they show taxonomic names, e.g., in the facets and titles of \code{\link[=antibiogram]{antibiogram()}} plots, in the legend of \code{\link[=wisca_plot]{wisca_plot()}}, on the axes, legends and facets of \code{\link[=ggplot_sir]{ggplot_sir()}} and \code{\link[=facet_sir]{facet_sir()}}, in \code{\link[=ggplot_pca]{ggplot_pca()}}, and in the subtitles of MIC and disk diffusion plots.
The interpretation of "I" will be named "Susceptible, incr. exp." (susceptible, increased exposure) for all EUCAST guidelines since 2019, and will be named "Intermediate" in all other cases. The interpretation of "I" will be named "Susceptible, incr. exp." (susceptible, increased exposure) for all EUCAST guidelines since 2019, and will be named "Intermediate" in all other cases.
@@ -377,4 +387,16 @@ plot(some_disk_values, mo = "Escherichia coli", ab = "cipro")
plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl")
plot(some_sir_values) plot(some_sir_values)
# Taxonomic names in italics using labels_taxonomy() ------------------
bacteria <- data.frame(
name = c("E. coli", "Klebsiella pneumoniae", "CoNS", "Other"),
n = c(25, 18, 9, 6)
)
barplot(bacteria$n, names.arg = labels_taxonomy(bacteria$name))
if (require("ggplot2")) {
ggplot(bacteria, aes(name, n)) +
geom_col() +
scale_x_discrete(labels = labels_taxonomy)
}
} }
+1 -1
View File
@@ -5,7 +5,7 @@
\alias{random_mic} \alias{random_mic}
\alias{random_disk} \alias{random_disk}
\alias{random_sir} \alias{random_sir}
\title{Random MIC Values/Disk Zones/SIR Generation} \title{Generate Random MIC Values, Disk Zones, or SIR Results}
\usage{ \usage{
random_mic( random_mic(
size = NULL, size = NULL,
+203 -12
View File
@@ -68,12 +68,56 @@
--bs-dropdown-link-active-color: white !important; --bs-dropdown-link-active-color: white !important;
} }
/* mode-dependent colours; light mode is enforced in _pkgdown.yml (light-switch: false), the dark values below are prepared for when it is enabled; they are set on :root only, since pkgdown also marks the navbar as dark */
:root {
color-scheme: light;
--amr-code-literal: var(--bs-dark);
--amr-code-muted: var(--bs-gray-600);
--amr-search-text: #0b3d33;
--amr-search-placeholder: rgba(11, 61, 51, 0.65);
--amr-accent: var(--amr-green-dark); /* green for text; --amr-green-dark is the brand colour for backgrounds such as the navbar */
--amr-accent-rgb: var(--amr-green-dark-rgb);
--amr-text-muted: #5b6770;
--amr-ref-border: #e3e8e6;
--amr-filter-border: #c4cdca;
--amr-filter-placeholder: #7b8783;
--amr-filter-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237b8783' stroke-width='1.75' stroke-linecap='round'%3E%3Ccircle cx='6.75' cy='6.75' r='5'/%3E%3Cpath d='M10.5 10.5l4 4'/%3E%3C/svg%3E");
--amr-filter-clear: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%237b8783' stroke-width='1.75' stroke-linecap='round'%3E%3Cpath d='M4 4l8 8M12 4l-8 8'/%3E%3C/svg%3E");
--amr-chip-bg: #f4f6f5;
--amr-chip-border: #dde3e1;
--amr-chip-match-bg: rgba(var(--amr-green-light-rgb), 0.5);
--amr-badge-bg: var(--amr-green-dark);
--amr-badge-text: white;
}
:root[data-bs-theme="dark"] {
color-scheme: dark;
--amr-accent: #4fc3a1;
--amr-accent-rgb: 79, 195, 161;
--bs-link-color: var(--amr-accent) !important;
--bs-link-color-rgb: var(--amr-accent-rgb) !important;
--amr-green-middle: #8fd1c1;
--amr-green-middle-rgb: 143, 209, 193;
--amr-code-literal: var(--bs-gray-300);
--amr-code-muted: var(--bs-gray-500);
--amr-text-muted: var(--bs-gray-400);
--amr-ref-border: rgba(255, 255, 255, 0.1);
--amr-filter-border: rgba(255, 255, 255, 0.25);
--amr-filter-placeholder: var(--bs-gray-500);
--amr-filter-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23adb5bd' stroke-width='1.75' stroke-linecap='round'%3E%3Ccircle cx='6.75' cy='6.75' r='5'/%3E%3Cpath d='M10.5 10.5l4 4'/%3E%3C/svg%3E");
--amr-filter-clear: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23adb5bd' stroke-width='1.75' stroke-linecap='round'%3E%3Cpath d='M4 4l8 8M12 4l-8 8'/%3E%3C/svg%3E");
--amr-chip-bg: rgba(255, 255, 255, 0.04);
--amr-chip-border: rgba(255, 255, 255, 0.14);
--amr-chip-match-bg: rgba(var(--amr-accent-rgb), 0.25);
}
/* Python page */ /* Python page */
body.amr-for-python * { body.amr-for-python * {
--amr-green-light: #FFD43B; /* Python yellow */ --amr-green-light: #FFD43B; /* Python yellow */
--amr-green-light-rgb: 255, 212, 59; --amr-green-light-rgb: 255, 212, 59;
--amr-green-dark: #306998; /* Python blue */ --amr-green-dark: #306998; /* Python blue */
--amr-green-dark-rgb: 48, 105, 152; --amr-green-dark-rgb: 48, 105, 152;
--amr-accent: var(--amr-green-dark);
--amr-accent-rgb: var(--amr-green-dark-rgb);
--amr-green-middle: #b1bc5e; /* Adjusted middle yellow */ --amr-green-middle: #b1bc5e; /* Adjusted middle yellow */
--amr-green-middle-rgb: 177, 188, 94; --amr-green-middle-rgb: 177, 188, 94;
--bs-success: var(--amr-green-dark) !important; --bs-success: var(--amr-green-dark) !important;
@@ -114,13 +158,16 @@ body.amr-for-python * {
opacity: 0.75; opacity: 0.75;
font-size: 0.85em; font-size: 0.85em;
} }
input[type="search"] { .navbar input[type="search"] {
color: var(--bs-tertiary-bg) !important; color: var(--amr-search-text) !important;
background-color: var(--amr-green-light) !important; background-color: var(--amr-green-light) !important;
max-width: 160px; max-width: 220px;
border-color: var(--amr-green-dark) !important; border-color: var(--amr-green-dark) !important;
border-radius: 2px !important; border-radius: 2px !important;
} }
.navbar input[type="search"]::placeholder {
color: var(--amr-search-placeholder) !important;
}
.dropdown-item:hover, .dropdown-item:hover,
.dropdown-item:focus { .dropdown-item:focus {
background-color: var(--bs-tertiary-bg) !important; background-color: var(--bs-tertiary-bg) !important;
@@ -149,6 +196,8 @@ blockquote {
} }
p { p {
text-align: justify; text-align: justify;
/* pkgdown only hyphenates on small screens; justified text needs it on all screens */
hyphens: auto;
} }
/* /*
@@ -167,10 +216,152 @@ this shows on top of every sidebar to the right
padding-bottom: 10px; padding-bottom: 10px;
} }
/* REFERENCE INDEX ('Manual'), see pkgdown/templates/content-reference-index.html */
.template-reference-index {
--amr-navbar-height: 72px; /* set exactly by extra.js */
}
.template-reference-index .section-desc { .template-reference-index .section-desc {
font-style: italic;
text-align: justify; text-align: justify;
font-size: 16px; hyphens: auto;
color: var(--amr-text-muted);
margin-bottom: 0.5rem;
}
.template-reference-index h2 {
margin-top: 2.5rem;
padding-bottom: 0.3rem;
border-bottom: 2px solid var(--amr-green-dark);
}
.amr-ref-filter + .section h2 {
margin-top: 1.5rem;
}
.template-reference-index h3 {
margin-top: 1.25rem;
font-size: 1.1rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--amr-accent);
}
/* while filtering, sub-headings show the title of their section, as the section heading may be far above */
.amr-ref-filtering main h3[data-amr-parent]::before {
content: attr(data-amr-parent) " \203A";
display: block;
font-size: 0.8rem;
font-weight: normal;
text-transform: none;
letter-spacing: normal;
color: var(--amr-text-muted);
}
/* filter field, sticky below the navbar */
.amr-ref-filter {
clear: right; /* start below the package logo, which pkgdown lets hang below the page header */
position: sticky;
top: var(--amr-navbar-height);
z-index: 10;
padding: 0.75rem 0;
background: var(--bs-body-bg);
}
.amr-ref-filter.amr-ref-stuck {
box-shadow: 0 1px 0 var(--amr-ref-border);
}
#amr-ref-filter-input {
width: 100%;
max-width: none;
padding-left: 2.5rem;
color: var(--bs-body-color);
background: var(--bs-body-bg) var(--amr-filter-icon) no-repeat left 0.85rem center / 1rem 1rem;
border: 1px solid var(--amr-filter-border);
border-radius: 2px;
}
#amr-ref-filter-input::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
width: 1rem;
height: 1rem;
background: var(--amr-filter-clear) no-repeat center / 1rem 1rem;
cursor: pointer;
}
#amr-ref-filter-input::placeholder {
color: var(--amr-filter-placeholder);
}
#amr-ref-filter-input:focus {
border-color: var(--amr-accent);
box-shadow: 0 0 0 1px var(--amr-accent) !important;
}
.amr-ref-filter-status {
font-size: 0.85rem;
padding-top: 0.35rem;
color: var(--amr-text-muted);
}
.amr-ref-filter-status:empty {
display: none;
}
/* topics: title first, function names as chips below */
.amr-ref-list {
margin-bottom: 0;
}
.amr-ref-item {
padding: 0.6rem 0 0.35rem;
border-bottom: 1px solid var(--amr-ref-border);
}
.amr-ref-item dt {
display: flex;
flex-wrap: wrap;
align-items: baseline;
column-gap: 0.5rem;
font-weight: bold !important;
margin-bottom: 0.3rem;
}
.amr-ref-item dt a {
color: var(--bs-body-color);
}
.amr-ref-item dt a:hover {
color: var(--amr-accent);
}
.template-reference-index .amr-ref-item dd {
margin: 0;
padding: 0;
font-size: 0.85rem;
line-height: 1.6;
}
.amr-ref-item dd code {
display: inline-block;
margin: 0 0.25rem 0.3rem 0;
padding: 0 0.4rem;
background: var(--amr-chip-bg);
border: 1px solid var(--amr-chip-border);
border-radius: 2px;
}
.amr-ref-item dd code.amr-ref-match {
background: var(--amr-chip-match-bg);
border-color: var(--amr-accent);
}
.amr-ref-item:not(.amr-ref-expanded) dd code.amr-ref-extra:not(.amr-ref-match) {
display: none;
}
.amr-ref-more {
border: none;
background: none;
padding: 0;
font-size: 0.85rem;
color: var(--amr-accent);
cursor: pointer;
}
.amr-ref-more:hover {
text-decoration: underline;
}
.amr-ref-hidden {
display: none !important;
}
.amr-ref-textmatch dt::after {
content: "mentioned on this page";
padding: 0 0.45rem;
font-weight: bold;
font-size: 0.75rem;
line-height: 1.6;
color: var(--amr-badge-text);
background: var(--amr-badge-bg);
border-radius: 2px;
} }
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
@@ -192,7 +383,7 @@ this shows on top of every sidebar to the right
.template-reference-topic h3, .template-reference-topic h3,
.template-reference-topic h3 code { .template-reference-topic h3 code {
color: var(--amr-green-dark) !important; color: var(--amr-accent) !important;
} }
.template-reference-topic h3 { .template-reference-topic h3 {
font-weight: normal; font-weight: normal;
@@ -232,11 +423,11 @@ code, .code {
.ot, .dv, .fl, .cn, .at, .ot, .dv, .fl, .cn, .at,
.op a { .op a {
/* numbers, TRUE/FALSE and operators */ /* numbers, TRUE/FALSE and operators */
color: var(--bs-dark) !important; color: var(--amr-code-literal) !important;
} }
pre .fu, .fu { pre .fu, .fu {
/* functions */ /* functions */
color: var(--amr-green-dark) !important; color: var(--amr-accent) !important;
} }
pre .fu a:hover, .fu a:hover { pre .fu a:hover, .fu a:hover {
/* functions */ /* functions */
@@ -249,18 +440,18 @@ pre .st, .st {
} }
pre .co, .co { pre .co, .co {
/* comments */ /* comments */
color: var(--amr-green-dark) !important; color: var(--amr-accent) !important;
font-style: italic !important; font-style: italic !important;
} }
div.sourceCode  pre .co, div.sourceCode  pre .co,
div.sourceCode .co { div.sourceCode .co {
/* comments in example sections, since functions are already green too */ /* comments in example sections, since functions are already green too */
color: var(--bs-gray-600) !important; color: var(--amr-code-muted) !important;
} }
pre code .r-out, pre code .r-out,
pre code .r-msg { pre code .r-msg {
/* output of functions */ /* output of functions */
color: var(--bs-gray-600) !important; color: var(--amr-code-muted) !important;
font-style: normal !important; font-style: normal !important;
} }
code a[href], code a[href],
@@ -269,7 +460,7 @@ pre a[href],
a code[href], a code[href],
a pre[href] { a pre[href] {
/* adjusted colour for all real links; having href attribute */ /* adjusted colour for all real links; having href attribute */
color: var(--amr-green-dark); color: var(--amr-accent);
text-decoration: none; text-decoration: none;
} }
a[href] { a[href] {
+188
View File
@@ -64,6 +64,177 @@ $(function () {
document.title = document.title.replace("Package index", "Manual"); document.title = document.title.replace("Package index", "Manual");
} }
// reference index ('Manual'): collapse long function lists and filter topics, see pkgdown/templates/content-reference-index.html
if ($(".amr-ref-item").length > 0) {
const maxAliases = 8;
const $body = $(".template-reference-index");
const $input = $("#amr-ref-filter-input");
const $status = $("#amr-ref-filter-status");
const $items = $(".amr-ref-item");
const pageText = {}; // full text per reference page, from search.json
let pageTextLoaded = false;
// keep the sticky filter field directly below the navbar, which hides when scrolling down
const navbar = document.querySelector(".navbar.fixed-top");
const filterBar = document.querySelector(".amr-ref-filter");
function placeFilter() {
const navBottom = navbar ? Math.max(0, navbar.getBoundingClientRect().bottom) : 0;
$body.css("--amr-navbar-height", navBottom + "px");
$(filterBar).toggleClass("amr-ref-stuck", filterBar.getBoundingClientRect().top <= navBottom + 1 && window.scrollY > 0);
}
let placing = false;
$(window).on("scroll resize", function () {
if (placing) return;
placing = true;
// follow the navbar during its slide transition
const start = performance.now();
(function step(now) {
placeFilter();
if (now - start < 400) requestAnimationFrame(step); else placing = false;
})(start);
});
placeFilter();
// headings that are jumped to (e.g. from 'On this page') must end up below the filter field, and below the navbar
// when jumping up, since the navbar only shows when scrolling up
$(document).on("click", 'a[href^="#"]', function () {
const target = document.getElementById(decodeURIComponent($(this).attr("href").slice(1)));
if (!target) return;
const up = target.getBoundingClientRect().top < 0;
document.documentElement.style.scrollPaddingTop = ((up && navbar ? navbar.offsetHeight : 0) + filterBar.offsetHeight + 8) + "px";
});
// the same when opening a link to a section, as the browser jumps before this script runs
if (window.location.hash) {
const target = document.getElementById(decodeURIComponent(window.location.hash.slice(1)));
if (target) {
document.documentElement.style.scrollPaddingTop = (filterBar.offsetHeight + 8) + "px";
target.scrollIntoView();
}
}
function updateMoreButton($item) {
const nHidden = $item.find("code.amr-ref-extra:not(.amr-ref-match)").length;
$item.find(".amr-ref-more").text("+ " + nHidden + " more").toggle(nHidden > 0);
}
$items.each(function () {
const $item = $(this);
const $codes = $item.find("dd code");
// pkgdown puts backticks around non-syntactic names, e.g. `%like%`
$codes.find("a").each(function () {
const name = $(this).text();
if (/^`.+`$/.test(name)) $(this).text(name.slice(1, -1));
});
$item.data("page", $item.find("dt a").attr("href"));
$item.data("text", ($item.find("dt").text() + " " + $item.find("dd").text()).toLowerCase());
// do not offer to show only one or two more
if ($codes.length > maxAliases + 2) {
$codes.slice(maxAliases).addClass("amr-ref-extra");
$item.find("dd").append('<button type="button" class="amr-ref-more"></button>');
updateMoreButton($item);
}
});
let h2Title = "";
$(".template-reference-index main").find("h2, h3").each(function () {
if (this.tagName === "H2") h2Title = $(this).text().trim();
else $(this).attr("data-amr-parent", h2Title);
});
$(".amr-ref-more").on("click", function () {
$(this).closest(".amr-ref-item").addClass("amr-ref-expanded");
$(this).remove();
});
function loadPageText() {
if (pageTextLoaded) return;
pageTextLoaded = true;
$.getJSON($("#search-input").data("search-index") || "../search.json", function (data) {
data.forEach(function (x) {
if (x.dir !== "Reference") return;
const page = x.path.split("/").pop();
pageText[page] = (pageText[page] || "") + " " + (x.what + " " + x.text + " " + x.code).toLowerCase();
});
filter();
});
}
function filter() {
const terms = $.trim($input.val()).toLowerCase().split(/\s+/).filter(Boolean);
$body.toggleClass("amr-ref-filtering", terms.length > 0);
let nDirect = 0;
let nText = 0;
$items.each(function () {
const $item = $(this);
const $codes = $item.find("dd code");
const direct = terms.length > 0 && terms.every(t => $item.data("text").includes(t));
const text = terms.length > 0 && !direct && terms.every(t => (pageText[$item.data("page")] || "").includes(t));
$item.toggleClass("amr-ref-hidden", terms.length > 0 && !direct && !text);
$item.toggleClass("amr-ref-textmatch", text);
// highlight the function names that match, which also reveals them if collapsed
$codes.each(function () {
const name = $(this).text().toLowerCase();
$(this).toggleClass("amr-ref-match", direct && terms.some(t => name.includes(t)));
});
updateMoreButton($item);
if (direct) nDirect++;
if (text) nText++;
});
// hide headings (with their descriptions) that have no visible topic before the next heading of the same or a higher level;
// this follows the document order, since pkgdown puts a heading and its topics in separate sibling blocks
const nVisible = new Map();
let h2 = null;
let h3 = null;
$(".template-reference-index main").find("h2, h3, .amr-ref-item").each(function () {
if (this.tagName === "H2") {
h2 = this;
h3 = null;
nVisible.set(this, 0);
} else if (this.tagName === "H3") {
h3 = this;
nVisible.set(this, 0);
} else if (!$(this).hasClass("amr-ref-hidden")) {
if (h2) nVisible.set(h2, nVisible.get(h2) + 1);
if (h3) nVisible.set(h3, nVisible.get(h3) + 1);
}
});
nVisible.forEach((n, heading) => $(heading).closest(".section").toggleClass("amr-ref-hidden", n === 0));
// blocks that only hold topics
$(".template-reference-index main .section").not(":has(h2, h3)").each(function () {
$(this).toggleClass("amr-ref-hidden", $(this).find(".amr-ref-item:not(.amr-ref-hidden)").length === 0);
});
const topics = n => n + (n === 1 ? " topic" : " topics");
if (terms.length === 0) {
$status.text("");
} else if (nDirect + nText === 0) {
$status.text(pageTextLoaded ? "No topics found" : "Searching...");
} else if (nText === 0) {
$status.text(topics(nDirect) + " found by name or title");
} else if (nDirect === 0) {
$status.text("No topics found by name or title, but " + topics(nText) + " mention it");
} else {
$status.text(topics(nDirect) + " found by name or title, and " + nText + " more mention it");
}
placeFilter();
}
$input.on("input", function () {
loadPageText();
filter();
});
$input.on("keydown", function (e) {
if (e.key === "Escape") {
$input.val("");
filter();
}
});
// press '/' anywhere to focus the filter field
$(document).on("keydown", function (e) {
if (e.key === "/" && !$(e.target).is("input, textarea")) {
e.preventDefault();
$input.trigger("focus");
}
});
}
// remove leading newline in code examples on changelog // remove leading newline in code examples on changelog
if ($("body .template-news").length > 0) { if ($("body .template-news").length > 0) {
$("body .template-news").html($("body .template-news").html().replaceAll('sourceCode R">\n<span', 'sourceCode R"><span')); $("body .template-news").html($("body .template-news").html().replaceAll('sourceCode R">\n<span', 'sourceCode R"><span'));
@@ -160,6 +331,23 @@ $(function () {
// add lines between collaborator types // add lines between collaborator types
$('.template-citation-authors li:contains("Contributor")').first().before('<hr>'); $('.template-citation-authors li:contains("Contributor")').first().before('<hr>');
$('.template-citation-authors li:contains("Former doctoral advisor")').first().before('<hr>'); $('.template-citation-authors li:contains("Former doctoral advisor")').first().before('<hr>');
// dark variants of figures: list a file name here and put a file with '_dark' before its extension next to it in pkgdown/assets,
// e.g. "logo_umcg.svg" requires "logo_umcg_dark.svg"; images are swapped when the theme is dark, also after switching theme
const darkVariants = [];
function applyDarkVariants() {
const dark = document.documentElement.getAttribute("data-bs-theme") === "dark";
$("img").each(function () {
const src = $(this).attr("src") || "";
const file = src.split(/[?#]/)[0].split("/").pop();
const base = file.replace(/_dark(\.\w+)$/, "$1");
if (!darkVariants.includes(base)) return;
const target = dark ? base.replace(/(\.\w+)$/, "_dark$1") : base;
if (file !== target) $(this).attr("src", src.replace(file, target));
});
}
applyDarkVariants();
new MutationObserver(applyDarkVariants).observe(document.documentElement, { attributes: true, attributeFilter: ["data-bs-theme"] });
}); });
$('head').append("<!-- Matomo --><script type='text/javascript'> var _paq = _paq || []; /* tracker methods like 'setCustomDimension' should be called before 'trackPageView' */ _paq.push(['setDomains', ['*.amr-for-r.org']]); _paq.push(['enableCrossDomainLinking']); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u='https://analyse.uscloud.nl/'; _paq.push(['setTrackerUrl', u+'piwik.php']); _paq.push(['setSiteId', '3']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); })();</script><!-- End Matomo Code -->"); $('head').append("<!-- Matomo --><script type='text/javascript'> var _paq = _paq || []; /* tracker methods like 'setCustomDimension' should be called before 'trackPageView' */ _paq.push(['setDomains', ['*.amr-for-r.org']]); _paq.push(['enableCrossDomainLinking']); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u='https://analyse.uscloud.nl/'; _paq.push(['setTrackerUrl', u+'piwik.php']); _paq.push(['setSiteId', '3']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); })();</script><!-- End Matomo Code -->");
@@ -0,0 +1,33 @@
<!-- AMR: adapted from pkgdown's BS5 template, with titles first, function names second, and a filter field (see pkgdown/extra.js) -->
<div class="row">
<main id="main" class="col-md-9">
<div class="page-header">
{{#logo}}<img src="{{logo.src}}" class="logo" alt="" >{{/logo}}
<h1>{{{pagetitle}}}</h1>
</div>
<div class="amr-ref-filter">
<input type="search" id="amr-ref-filter-input" class="form-control" placeholder="Filter, e.g. mo_gramstain or WISCA" aria-label="Filter the manual" autocomplete="off" spellcheck="false">
<div id="amr-ref-filter-status" class="amr-ref-filter-status" aria-live="polite"></div>
</div>
{{#rows}}<div {{#slug}}id="{{.}}"{{/slug}} class="section level2">
{{#title}}<h2>{{{.}}}</h2>{{/title}}
{{#subtitle}}<h3>{{{.}}}</h3>{{/subtitle}}
{{#desc}}<div class="section-desc">{{{desc}}}</div>{{/desc}}
<dl class="amr-ref-list">{{#topics}}
<div class="amr-ref-item">
<dt><a href="{{path}}">{{{title}}}</a>{{#lifecycle}} <span class="badge lifecycle lifecycle-{{.}}">{{.}}</span>{{/lifecycle}}</dt>
<dd>{{#aliases}}<code><a href="{{path}}">{{{.}}}</a></code> {{/aliases}}</dd>
</div>
{{/topics}}</dl>
</div>{{/rows}}
</main>
<aside class="col-md-3">
<nav id="toc" aria-label="{{#translate}}{{toc}}{{/translate}}">
<h2>{{#translate}}{{on_this_page}}{{/translate}}</h2>
</nav>
</aside>
</div>
+56
View File
@@ -0,0 +1,56 @@
# Release checklist
The steps of a regular CRAN release of the `AMR` package, as followed for v3.1.0 (October 2026). Replace `X.Y.Z` with the new version number. Copy this list into an issue or a note per release, or tick the boxes in a local copy.
## 1. Preparation (on the development version)
- [ ] All intended issues and pull requests for this release are merged into `main`
- [ ] All GitHub Actions on `main` succeed, including `check-revdeps` (it runs on every push to `main` and can also be started manually from the Actions tab)
- [ ] Reverse dependencies are checked against this version; if a package breaks, its maintainer is informed in advance through an issue in its repository (e.g., https://github.com/agerada/MIC/issues/15 for v3.1.0)
- [ ] `vignettes/welcome_to_AMR.Rmd` is reviewed, since it is the only vignette that goes to CRAN (all other vignettes are in `.Rbuildignore`): check the text, the links, and the outcome of its inline R code (number of species and drugs, guideline years, languages, taxonomy date)
- [ ] `index.Rmd` (website home page) and `README.Rmd` are up to date with the new version
- [ ] `inst/CITATION` is up to date
## 2. Version and documentation
- [ ] `DESCRIPTION`: `Version: X.Y.Z` and `Date:` set to the submission date
- [ ] `NEWS.md`: line 1 replaced by `# AMR X.Y.Z`, the line about the planned release removed, and the intro paragraph written in past tense, summarising the main changes (see *NEWS.md style* in `CLAUDE.md`)
- [ ] `NEWS.md`: the *Breaking* section mentions every change that can alter results or break code, with how to get the old behaviour
- [ ] `cran-comments.md` rewritten for this release: type of release, main changes, reverse dependency results (with links to the issues of affected packages), and the note on the installation size
- [ ] `devtools::document()` is run, so that `man/`, `NAMESPACE` and `R/sysdata.rda` are current (the pre-commit hook does this on commits with the hooks active)
## 3. Checks
- [ ] `R CMD build` and `R CMD check --as-cran` on the tarball: no errors or warnings, and only the known note on the installation size
- [ ] `devtools::check_win_devel()` (win-builder, R-devel); wait for the e-mail
- [ ] Optionally R-hub (`rhub::rhub_check()`) for other platforms
- [ ] `urlchecker::url_check()` reports no broken or redirected URLs
## 4. Submission
- [ ] `devtools::submit_cran()`; this also updates `CRAN-SUBMISSION` (version, date and SHA)
- [ ] Confirm the submission through the link in the e-mail from CRAN
- [ ] Wait for acceptance; answer any questions from the CRAN team and resubmit if needed (keep the version number unless CRAN asks otherwise)
## 5. After acceptance by CRAN
The pre-commit hook in `.github/prehooks/` sets the version to `X.Y.Z.9NNN` and rewrites line 1 of `NEWS.md` on every commit to `main`. The release commit must therefore skip the hooks.
- [ ] `git status`: only the intended files are changed, with no build leftovers (e.g., `AMR_X.Y.Z.tar.gz`, `AMR.Rcheck/`)
- [ ] Commit the release without hooks, with only the version as message: `git add -u && git commit -n -m "vX.Y.Z"`
- [ ] `git push origin main` (this also rebuilds the website and publishes the Python package to PyPI)
- [ ] Create the tag through a GitHub release, with the `NEWS.md` section of this version without its `# AMR X.Y.Z` heading as release notes:
```bash
awk 'NR==1 {next} /^# AMR /{exit} 1' NEWS.md > notes.md
gh release create vX.Y.Z --target main --title "AMR vX.Y.Z" --notes-file notes.md --latest
rm notes.md
```
- [ ] `git fetch --tags` and check that `vX.Y.Z` points at the release commit
- [ ] Run `source("data-raw/microorganisms_files/build_mo_code_registry.R")` from the root of the repository (takes a few minutes; the tag must exist), then review the diff of `mo_code_registry.csv`: only new codes may be added, existing codes may only get a later `last_release`, and renamed codes must have their old name in `previous_names` (see `data-raw/microorganisms_files/README.md`)
- [ ] `.github/ISSUE_TEMPLATE/1-bug-report.yml`: set the versions to `Latest CRAN version (X.Y.Z)` and `One of the latest GitHub versions (X.Y.Z.9xxx)`
- [ ] `NEWS.md`: put a new line 1 `# AMR X.Y.Z.9001`, followed by an empty line, above `# AMR X.Y.Z`; without this, the hook overwrites the heading of the released section
- [ ] Commit with the hooks active (the version becomes `X.Y.Z.9001`) and push to `main`
- [ ] Check that the GitHub Actions succeed, the website (https://amr-for-r.org) shows the new version, and the package is available on PyPI
- [ ] Check the CRAN page (https://cran.r-project.org/package=AMR) and the CRAN check results some days later
+51 -1
View File
@@ -47,6 +47,44 @@ test_that("test-antibiogram.R", {
wisca_known <- suppressMessages(wisca(example_isolates[example_isolates$mo != "UNKNOWN", ], antimicrobials = "GEN", simulations = 100, info = FALSE, guideline = "EUCAST")) wisca_known <- suppressMessages(wisca(example_isolates[example_isolates$mo != "UNKNOWN", ], antimicrobials = "GEN", simulations = 100, info = FALSE, guideline = "EUCAST"))
expect_true("UNKNOWN" %in% attributes(wisca_all)$wisca_parameters$mo) expect_true("UNKNOWN" %in% attributes(wisca_all)$wisca_parameters$mo)
expect_false(identical(as.data.frame(wisca_all), as.data.frame(wisca_known))) expect_false(identical(as.data.frame(wisca_all), as.data.frame(wisca_known)))
# WISCA only includes pathogens that occur in a group and were tested for the regimen (#337)
set.seed(337)
wisca_data <- example_isolates[mo_domain(example_isolates$mo) == "Bacteria", ]
wisca_data$grp <- sample(c("A", "B", "C"), nrow(wisca_data), replace = TRUE)
wisca_strat <- suppressWarnings(wisca(wisca_data, antimicrobials = "MEM", syndromic_group = "grp", simulations = 5000, info = FALSE))
wisca_params <- retrieve_wisca_parameters(wisca_strat)
# no absent or untested pathogens
expect_true(all(wisca_params$n_total > 0 & wisca_params$n_tested > 0))
# each stratum equals the unstratified WISCA of its subset
for (g in c("A", "B", "C")) {
wisca_subset <- suppressWarnings(wisca(wisca_data[wisca_data$grp == g, ], antimicrobials = "MEM", simulations = 5000, info = FALSE))
cov_strat <- attributes(wisca_strat)$long_numeric$coverage[attributes(wisca_strat)$long_numeric$syndromic_group == g]
expect_true(abs(cov_strat - attributes(wisca_subset)$long_numeric$coverage) < 0.01)
expect_identical(attributes(wisca_strat)$long_numeric$n_total[attributes(wisca_strat)$long_numeric$syndromic_group == g], attributes(wisca_subset)$long_numeric$n_total)
}
# untested pathogens have no influence
wisca_untested <- wisca_data[1:20, ]
wisca_untested$mo <- as.mo("Brucella melitensis")
wisca_untested$MEM <- NA_sir_
set.seed(337)
wisca_without <- suppressWarnings(wisca(wisca_data, antimicrobials = "MEM", simulations = 1000, info = FALSE))
set.seed(337)
wisca_with <- suppressWarnings(wisca(rbind(wisca_data, wisca_untested), antimicrobials = "MEM", simulations = 1000, info = FALSE))
expect_identical(attributes(wisca_with)$long_numeric, attributes(wisca_without)$long_numeric)
expect_false("B_BRCLL_MLTN" %in% retrieve_wisca_parameters(wisca_with)$mo)
# warning for a large prior weight of rare pathogens
expect_warning(wisca(wisca_data, antimicrobials = "MEM", syndromic_group = "grp", simulations = 100, info = FALSE), "prior weight")
# (expect_warning(..., NA) is not supported by tinytest)
wisca_warnings <- character(0)
withCallingHandlers(
wisca(example_isolates, antimicrobials = "MEM", simulations = 100, info = FALSE),
warning = function(w) {
wisca_warnings <<- c(wisca_warnings, conditionMessage(w))
invokeRestart("muffleWarning")
}
)
expect_false(any(grepl("prior weight", wisca_warnings)))
if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) { if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) {
ab_grouped <- tryCatch( ab_grouped <- tryCatch(
# pathogen names are translated, so test in English, which must also hold on systems in another language # pathogen names are translated, so test in English, which must also hold on systems in another language
@@ -230,7 +268,19 @@ test_that("test-antibiogram.R", {
expect_equal(nrow(ab_grp_seq), nrow(ab_grp_par)) expect_equal(nrow(ab_grp_seq), nrow(ab_grp_par))
} }
# 6. parallel = TRUE without a plan raises an informative error # 6. parallel and sequential use the same WISCA parameters and give the same coverage (#337)
set.seed(337)
wisca_data <- example_isolates[mo_domain(example_isolates$mo) == "Bacteria", ]
wisca_data$grp <- sample(c("A", "B", "C"), nrow(wisca_data), replace = TRUE)
future::plan(future::sequential)
wisca_strat_seq <- suppressWarnings(wisca(wisca_data, antimicrobials = "MEM", syndromic_group = "grp", simulations = 5000, info = FALSE))
future::plan(future::multicore)
wisca_strat_par <- suppressWarnings(wisca(wisca_data, antimicrobials = "MEM", syndromic_group = "grp", simulations = 5000, parallel = TRUE, info = FALSE))
expect_identical(retrieve_wisca_parameters(wisca_strat_par), retrieve_wisca_parameters(wisca_strat_seq))
expect_true(all(retrieve_wisca_parameters(wisca_strat_par)$n_tested > 0))
expect_true(all(abs(attributes(wisca_strat_par)$long_numeric$coverage - attributes(wisca_strat_seq)$long_numeric$coverage) < 0.01))
# 7. parallel = TRUE without a plan raises an informative error
future::plan(future::sequential) future::plan(future::sequential)
expect_error( expect_error(
suppressWarnings(wisca(example_isolates, antimicrobials = "TZP", parallel = TRUE, info = FALSE)), suppressWarnings(wisca(example_isolates, antimicrobials = "TZP", parallel = TRUE, info = FALSE)),
+19
View File
@@ -547,6 +547,25 @@ test_that("intrinsic_resistant: synonyms have exactly the resistances of their c
expect_true(length(absent) == 0, info = paste("Current names missing while their synonym is listed:", describe_mo(absent))) expect_true(length(absent) == 0, info = paste("Current names missing while their synonym is listed:", describe_mo(absent)))
}) })
test_that("intrinsic_resistant: species groups have the resistances shared by all their species", {
skip_on_cran()
ir_keys <- paste(intrinsic_resistant$mo, intrinsic_resistant$ab)
groups <- microorganisms.groups[which(microorganisms$rank[match(microorganisms.groups$mo, microorganisms$mo)] == "species"), , drop = FALSE]
groups_species <- split(as.character(groups$mo), as.character(groups$mo_group))
missing <- unlist(Map(function(group, species) {
abx <- unique(as.character(intrinsic_resistant$ab[intrinsic_resistant$mo %in% species]))
abx <- abx[vapply(abx, function(ab) all(paste(species, ab) %in% ir_keys), logical(1))]
if (length(abx) == 0) {
return(NULL)
}
abx <- abx[!paste(group, abx) %in% ir_keys]
if (length(abx) > 0) paste0(group, " (", paste(abx, collapse = ", "), ")")
}, names(groups_species), groups_species), use.names = FALSE)
expect_true(length(missing) == 0, info = paste("Species groups missing resistances shared by all their species:", paste(missing, collapse = "; ")))
# Enterobacter cloacae complex (#338)
expect_true(all(mo_is_intrinsic_resistant(rep("B_ENTRBC_CLOC-C", 2), ab = c("AMX", "AMC"))))
})
test_that("interpretive rules name organisms that exist", { test_that("interpretive rules name organisms that exist", {
skip_on_cran() skip_on_cran()
r <- AMR:::INTERPRETIVE_RULES_DF r <- AMR:::INTERPRETIVE_RULES_DF
+48
View File
@@ -160,5 +160,53 @@ test_that("test-ggplot_sir.R", {
scale_sir_colours(aesthetics = "fill", Value4 = "S", Value5 = "I", Value6 = "R"))$data), scale_sir_colours(aesthetics = "fill", Value4 = "S", Value5 = "I", Value6 = "R"))$data),
"data.frame" "data.frame"
) )
# datalabels are placed on their own bar, also when dodged or stacked
for (pos in c("fill", "stack", "dodge")) {
built <- ggplot_build(ggplot_sir(example_isolates[, c("AMX", "CIP")], position = pos, guideline = NULL))
bars <- built$data[[1]]
labels <- built$data[[2]]
bars <- bars[order(bars$x, bars$group), , drop = FALSE]
labels <- labels[order(labels$x, labels$group), , drop = FALSE]
expect_equal(labels$x, bars$x)
if (pos == "dodge") {
expect_equal(labels$y, bars$ymax)
} else {
expect_equal(labels$y, (bars$ymin + bars$ymax) / 2)
}
}
# without the interpretation in the plot, only the susceptible proportion is shown, following the guideline
grouped <- example_isolates[, c("gender", "AMX")]
grouped <- structure(grouped, class = c("grouped_df", "tbl_df", "tbl", "data.frame"), groups = data.frame(gender = c("F", "M"), .rows = I(list(which(grouped$gender == "F"), which(grouped$gender == "M")))))
for (gl in list("EUCAST", "CLSI", NULL)) {
expected <- sir_df(grouped, guideline = gl)
expected <- expected[which(as.character(expected$interpretation) == levels(expected$interpretation)[1]), , drop = FALSE]
p <- ggplot_sir(grouped, x = "antibiotic", fill = "gender", position = "dodge", guideline = gl)
built <- ggplot_build(p)
expect_equal(sort(built$data[[1]]$y), sort(expected$value))
expect_equal(nrow(built$data[[2]]), nrow(expected))
# data labels show the number of tested isolates
expected_n <- tapply(sir_df(grouped, guideline = gl)$isolates, sir_df(grouped, guideline = gl)$gender, sum)
expect_identical(sort(built$data[[2]]$label), sort(paste0("n=", expected_n)))
expect_identical(p$labels$y, paste("Proportion", levels(expected$interpretation)[1]))
}
# all interpretations when they are shown in the facets
built <- ggplot_build(ggplot_sir(grouped, x = "antibiotic", fill = "gender", facet = "interpretation", position = "dodge", guideline = "EUCAST"))
expect_equal(nrow(built$data[[1]]), nrow(sir_df(grouped, guideline = "EUCAST")))
# split by antibiotic if it is not shown in the plot and there are several
grouped2 <- example_isolates[, c("gender", "AMX", "CIP")]
grouped2 <- structure(grouped2, class = c("grouped_df", "tbl_df", "tbl", "data.frame"), groups = data.frame(gender = c("F", "M"), .rows = I(list(which(grouped2$gender == "F"), which(grouped2$gender == "M")))))
p <- ggplot_sir(grouped2, x = "gender", guideline = "EUCAST")
expect_true(inherits(p$facet, "FacetWrap"))
expect_identical(names(p$facet$params$facets), "antibiotic")
p <- ggplot_sir(grouped2, x = "interpretation", fill = "interpretation", facet = "gender", guideline = "EUCAST")
expect_true(inherits(p$facet, "FacetGrid"))
expect_identical(names(p$facet$params$cols), "antibiotic")
expect_identical(names(p$facet$params$rows), "gender")
# not if the antibiotic is shown, or if there is only one
expect_false(inherits(ggplot_sir(grouped2, x = "antibiotic", guideline = "EUCAST")$facet, "FacetWrap"))
expect_false(inherits(ggplot_sir(grouped, x = "gender", guideline = "EUCAST")$facet, "FacetWrap"))
} }
}) })
+33
View File
@@ -48,4 +48,37 @@ test_that("test-italicise_taxonomy.R", {
italicise_taxonomy("test for E. coli", "html"), italicise_taxonomy("test for E. coli", "html"),
"test for <i>E. coli</i>" "test for <i>E. coli</i>"
) )
# species only after a genus or abbreviated genus, punctuation outside the italics
expect_identical(
italicise_taxonomy(c(
"Escherichia coli", "coli bacteria", "(S. aureus)", "E. coli,", "Other",
"Salmonella Typhimurium", "Streptococcus group A", "Staphylococcus spp.",
"Klebsiella pneumoniae subsp. ozaenae", "K. pneumoniae ozaenae", "Enterobacteriaceae", "", NA
)),
c(
"*Escherichia coli*", "coli bacteria", "(*S. aureus*)", "*E. coli*,", "Other",
"*Salmonella* Typhimurium", "*Streptococcus* group A", "*Staphylococcus* spp.",
"*Klebsiella pneumoniae* subsp. *ozaenae*", "*K. pneumoniae ozaenae*", "*Enterobacteriaceae*", "", NA
)
)
# plotmath, also used by labels_taxonomy()
expect_identical(
italicise_taxonomy(c("E. coli", "Other", "test for E. coli", NA), type = "plotmath"),
expression(italic("E. coli"), "Other", paste("test for ", italic("E. coli")), NA_character_)
)
expect_identical(
labels_taxonomy(factor(c("E. coli", "Other"))),
expression(italic("E. coli"), "Other")
)
expect_identical(labels_taxonomy(character(0)), expression())
# used internally by the plotting functions
expect_true(AMR:::contains_taxonomy(c("E. coli", "Other", NA)))
expect_false(AMR:::contains_taxonomy(c("Other", "CoNS", NA)))
expect_identical(
AMR:::labeller_taxonomy(list(mo = c("E. coli", "CoNS"))),
list(mo = list(expression(italic("E. coli")), expression("CoNS")))
)
}) })
+21
View File
@@ -56,6 +56,27 @@ test_that("test-mdro.R", {
mdro(example_isolates, guideline = "BRMO", info = FALSE) mdro(example_isolates, guideline = "BRMO", info = FALSE)
) )
# test EUCAST rule for Salmonella Typhi, which is stored as subspecies of S. enterica
typhi <- data.frame(
mo = as.mo(rep("Salmonella Typhi", 2)),
CIP = as.sir(c("R", "S")),
IPM = as.sir(c("S", "S")),
MEM = as.sir(c("S", "S")),
AMX = as.sir(c("S", "S"))
)
expect_equal(
as.character(suppressWarnings(mdro(typhi, guideline = "EUCAST3.1", col_mo = "mo", info = FALSE))),
c("Positive", "Negative")
)
typhi$CIP <- as.sir(c("S", "S"))
typhi$MEM <- as.sir(c("R", "S"))
for (gl in c("EUCAST3.2", "EUCAST3.3")) {
expect_equal(
as.character(suppressWarnings(mdro(typhi, guideline = gl, col_mo = "mo", info = FALSE))),
c("Positive", "Negative")
)
}
# test Dutch P. aeruginosa MDRO # test Dutch P. aeruginosa MDRO
expect_equal( expect_equal(
as.character(mdro( as.character(mdro(
+19
View File
@@ -45,6 +45,25 @@ test_that("test-mo_property.R", {
expect_equal(mo_shortname("Staphylococcus aureus", Becker = "all", language = "en"), "CoPS") expect_equal(mo_shortname("Staphylococcus aureus", Becker = "all", language = "en"), "CoPS")
expect_equal(mo_shortname("Streptococcus agalactiae"), "S. agalactiae") expect_equal(mo_shortname("Streptococcus agalactiae"), "S. agalactiae")
expect_equal(mo_shortname("Streptococcus agalactiae", Lancefield = TRUE), "GBS") expect_equal(mo_shortname("Streptococcus agalactiae", Lancefield = TRUE), "GBS")
# Salmonella serovars return the serovar, and all stay distinct
expect_equal(
mo_shortname(c("Salmonella Typhi", "Salmonella Goldcoast", "Salmonella Paratyphi A", "Salmonella enterica", "Salmonella enterica enterica", NA), language = NULL),
c("S. Typhi", "S. Goldcoast", "S. Paratyphi A", "S. enterica", "S. enterica", NA)
)
# Salmonella groups must not be abbreviated as streptococci
expect_equal(
mo_shortname(c("Salmonella group B", "Streptococcus group B"), language = NULL),
c("S. Group B", "GBS")
)
# all Lancefield groups are abbreviated, also when they share a letter with a Salmonella group
expect_equal(
mo_shortname(paste0("B_STRPT_GRP", LETTERS[1:12]), language = NULL),
paste0("G", LETTERS[1:12], "S")
)
expect_equal(mo_shortname("B_SLMNL_GRPE", language = NULL), "S. Group E")
expect_equal(mo_shortname("B_STRPT_ABCG", language = NULL), "S. Group A, B, C, G")
serovars <- microorganisms$mo[microorganisms$genus == "Salmonella" & microorganisms$subspecies %like_case% "^[A-Z]" & microorganisms$status == "accepted"]
expect_false(anyDuplicated(mo_shortname(serovars, language = NULL)) > 0)
# check gram stain determination, to prevent we lag after a taxonomic renaming # check gram stain determination, to prevent we lag after a taxonomic renaming
current_grampos_phyla <- c( current_grampos_phyla <- c(
+7
View File
@@ -60,6 +60,13 @@ test_that("test-pca.R", {
if (AMR:::pkg_is_available("ggplot2")) { if (AMR:::pkg_is_available("ggplot2")) {
ggplot_pca(pca_model, ellipse = TRUE) ggplot_pca(pca_model, ellipse = TRUE)
ggplot_pca(pca_model, arrows_textangled = FALSE) ggplot_pca(pca_model, arrows_textangled = FALSE)
# explicit groups, with and without labels
my_groups <- c("Gram-positive", "Gram-negative", "Gram-negative")
p <- ggplot_pca(pca_model, groups = my_groups)
expect_identical(p$data$groups, my_groups)
expect_identical(p$labels$colour, "my_groups")
p <- ggplot_pca(pca_model, labels = resistance_data$genus, groups = my_groups)
expect_identical(p$data$groups, my_groups)
} }
if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) { if (AMR:::pkg_is_available("dplyr", min_version = "1.0.0", also_load = TRUE)) {
+12
View File
@@ -270,6 +270,7 @@ Use the `wisca()` function, or equivalently `antibiogram(..., wisca = TRUE)`. WI
```{r wisca} ```{r wisca}
wisca_result <- example_isolates %>% wisca_result <- example_isolates %>%
top_n_microorganisms(n = 10) %>%
wisca( wisca(
antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"), antimicrobials = c("TZP", "TZP+TOB", "TZP+GEN"),
minimum = 10 minimum = 10
@@ -432,6 +433,17 @@ autoplot(mic_values)
autoplot(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST") autoplot(mic_values, mo = "K. pneumoniae", ab = "cipro", guideline = "EUCAST")
``` ```
Taxonomic names are printed in italics by convention. Use `labels_taxonomy()` for this in axis and legend labels, and `italicise_taxonomy(..., type = "plotmath")` for titles:
```{r labels_taxonomy}
our_data_1st %>%
count(species = mo_name(bacteria)) %>%
ggplot(aes(x = species, y = n)) +
geom_col() +
scale_x_discrete(labels = labels_taxonomy) +
labs(title = italicise_taxonomy("First isolates of E. coli and other species", type = "plotmath"))
```
---- ----
*Author: Dr. Matthijs Berends, 23rd June 2026* *Author: Dr. Matthijs Berends, 23rd June 2026*
+2 -2
View File
@@ -26,7 +26,7 @@ Note: to keep the package size as small as possible, we only include this vignet
---- ----
The `AMR` package is a peer-reviewed, [free and open-source](#copyright) R package with [zero dependencies](https://en.wikipedia.org/wiki/Dependency_hell) to simplify the analysis and prediction of Antimicrobial Resistance (AMR) and to work with microbial and antimicrobial data and properties, by using evidence-based methods. The `AMR` package is a peer-reviewed, [free and open-source](https://amr-for-r.org/#copyright) R package with [zero dependencies](https://en.wikipedia.org/wiki/Dependency_hell) to simplify the analysis and prediction of Antimicrobial Resistance (AMR) and to work with microbial and antimicrobial data and properties, by using evidence-based methods.
**Our aim has always been to provide a standard** for clean and reproducible AMR data analysis, that can therefore empower epidemiological analyses to continuously enable surveillance and treatment evaluation in any setting. We are a team of [many different researchers](https://amr-for-r.org/authors.html) from around the globe to make this a successful and durable project! The `AMR` package was already cited [over 100 times](https://scholar.google.com/citations?view_op=view_citation&hl=en&citation_for_view=sAoHvIgAAAAJ:0EnyYjriUFMC) in scientific research. **Our aim has always been to provide a standard** for clean and reproducible AMR data analysis, that can therefore empower epidemiological analyses to continuously enable surveillance and treatment evaluation in any setting. We are a team of [many different researchers](https://amr-for-r.org/authors.html) from around the globe to make this a successful and durable project! The `AMR` package was already cited [over 100 times](https://scholar.google.com/citations?view_op=view_citation&hl=en&citation_for_view=sAoHvIgAAAAJ:0EnyYjriUFMC) in scientific research.
@@ -36,7 +36,7 @@ The `AMR` package is available in `r AMR:::vector_and(vapply(FUN.VALUE = charact
This package was intended as a comprehensive toolbox for integrated AMR data analysis. This package can be used for: This package was intended as a comprehensive toolbox for integrated AMR data analysis. This package can be used for:
* Reference for the taxonomy of microorganisms, since the package contains all microbial (sub)species from the List of Prokaryotic names with Standing in Nomenclature ([LPSN](https://lpsn.dsmz.de)) and the Global Biodiversity Information Facility ([GBIF](https://www.gbif.org)) ([manual](https://amr-for-r.org/reference/mo_property.html)) * Reference for the taxonomy of microorganisms, since the package contains all microbial (sub)species from the List of Prokaryotic names with Standing in Nomenclature ([LPSN](https://lpsn.dsmz.de)), [MycoBank](https://www.mycobank.org) and the Global Biodiversity Information Facility ([GBIF](https://www.gbif.org)) ([manual](https://amr-for-r.org/reference/mo_property.html))
* Interpreting raw MIC and disk diffusion values, based on any CLSI or EUCAST guideline ([manual](https://amr-for-r.org/reference/as.sir.html)) * Interpreting raw MIC and disk diffusion values, based on any CLSI or EUCAST guideline ([manual](https://amr-for-r.org/reference/as.sir.html))
* Retrieving antimicrobial drug names, doses and forms of administration from clinical health care records ([manual](https://amr-for-r.org/reference/ab_from_text.html)) * Retrieving antimicrobial drug names, doses and forms of administration from clinical health care records ([manual](https://amr-for-r.org/reference/ab_from_text.html))
* Determining first isolates to be used for AMR data analysis ([manual](https://amr-for-r.org/reference/first_isolate.html)) * Determining first isolates to be used for AMR data analysis ([manual](https://amr-for-r.org/reference/first_isolate.html))