diff --git a/DESCRIPTION b/DESCRIPTION index ee622d5b2..2b9e5e3cd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AMR -Version: 3.1.0.9016 -Date: 2026-11-04 +Version: 3.1.0.9018 +Date: 2026-11-09 Title: Antimicrobial Resistance Data Analysis Description: Functions to simplify and standardise antimicrobial resistance (AMR) data analysis and to work with microbial and antimicrobial properties by diff --git a/NAMESPACE b/NAMESPACE index cc84ade57..d14e3e915 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -270,6 +270,7 @@ export(italicize_taxonomy) export(key_antimicrobials) export(kurtosis) export(labels_sir_count) +export(labels_taxonomy) export(left_join_microorganisms) export(like) export(lincosamides) diff --git a/NEWS.md b/NEWS.md index 3108a7126..d47323807 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# AMR 3.1.0.9016 +# AMR 3.1.0.9018 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. @@ -26,6 +26,9 @@ It also adds a `site` argument to `as.sir()`, stores EUCAST MIC breakpoints exac * Oxacillin disk for *S. pseudintermedius* and related species, and oxacillin MIC for coagulase-negative staphylococci, under `OXA-S` * Mupirocin for nasal decolonisation of *S. aureus* * Meropenem in other infections than meningitis for *N. meningitidis* in EUCAST 2021 +* **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 ### Changed * SIR interpretation (`as.sir()`) @@ -35,6 +38,11 @@ It also adds a `site` argument to `as.sir()`, stores EUCAST MIC breakpoints exac * EUCAST MIC breakpoints are stored exactly as published, e.g., 0.03 instead of 0.032, and 0.001 instead of 0.0001 (#331) * Blocking rows of EUCAST 2019-2026 that have no effect on `as.sir()` were removed, 12 243 in total (#326) * The breakpoints of *Bacteroides* now also apply to zone diameters of *Parabacteroides*, *Phocaeicola dorei* and *Phocaeicola vulgatus* (#329) +* `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 * *Morganella* is interpreted as reported, with the breakpoints of Enterobacterales or Morganellaceae and not of *M. morganii*, which is now tested (#330) * 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) diff --git a/R/aa_session_cache.R b/R/aa_session_cache.R index 66d6b33bb..3b2476fe7 100644 --- a/R/aa_session_cache.R +++ b/R/aa_session_cache.R @@ -131,6 +131,7 @@ reset_mo_cache <- function() { AMR_env$mo_accepted <- NULL AMR_env$mo_previously_coerced <- new_coercion_cache() AMR_env$mo_previously_uncertain <- NULL + AMR_env$taxonomy_names <- NULL } get_ab_index <- function() { diff --git a/R/italicise_taxonomy.R b/R/italicise_taxonomy.R index 664675ba6..2abca5693 100755 --- a/R/italicise_taxonomy.R +++ b/R/italicise_taxonomy.R @@ -31,27 +31,53 @@ #' #' 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 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 -#' 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 `` and `` 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 #' @examples #' italicise_taxonomy("An overview of Staphylococcus 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")) -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)) { type <- "markdown" } 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") { before <- "*" @@ -69,75 +95,138 @@ italicise_taxonomy <- function(string, type = c("markdown", "ansi", "html")) { vapply( FUN.VALUE = character(1), - string, - function(s) { - s_split <- unlist(strsplit(s, " ", fixed = TRUE)) - - search_strings <- gsub("[^a-zA-Z-]", "", s_split) - - ind_species <- search_strings != "" & - 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 + pieces, + function(p) { + if (is.null(p)) { + return(NA_character_) + } + p$text[p$italic] <- paste0(before, p$text[p$italic], after) + paste0(p$text, collapse = "") }, USE.NAMES = FALSE ) } +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 #' @export -italicize_taxonomy <- function(string, type = c("markdown", "ansi", "html")) { +italicize_taxonomy <- function(string, type = c("markdown", "ansi", "html", "plotmath")) { if (missing(type)) { type <- "markdown" } diff --git a/R/plotting.R b/R/plotting.R index 05fff12c3..a8e4bb8ce 100755 --- a/R/plotting.R +++ b/R/plotting.R @@ -33,7 +33,7 @@ #' 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. -#' @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 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*. @@ -76,6 +76,10 @@ #' * [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()]. #' +#' ### 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 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. @@ -225,6 +229,18 @@ #' plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") #' #' 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 #' @rawNamespace if(getRversion() >= "3.0.0") S3method(ggplot2::scale_type, mic) @@ -1412,6 +1428,15 @@ theme_sir <- function() { ) } +#' @rdname plot +#' @export +labels_taxonomy <- function(x, ...) { + if (length(x) == 0) { + return(expression()) + } + italicise_taxonomy(as.character(x), type = "plotmath") +} + #' @rdname ggplot_sir #' @export labels_sir_count <- function(position = NULL, diff --git a/_pkgdown.yml b/_pkgdown.yml index 0c692b016..698421c3f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -201,7 +201,7 @@ reference: - title: "Visualising data" desc: > - Plot MIC values, disk zones, and SIR results in base R or `ggplot2`, with scales and colours tailored to AMR data. + 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. contents: - "`plot`" - "`ggplot_sir`" diff --git a/man/italicise_taxonomy.Rd b/man/italicise_taxonomy.Rd index e8a29dc0b..ec8f15d81 100644 --- a/man/italicise_taxonomy.Rd +++ b/man/italicise_taxonomy.Rd @@ -5,28 +5,39 @@ \alias{italicize_taxonomy} \title{Italicise Taxonomic Families, Genera, Species, and Subspecies} \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{ \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{ 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{ -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{} and \verb{} 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{ italicise_taxonomy("An overview of Staphylococcus 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")) + +# 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. } diff --git a/man/plot.Rd b/man/plot.Rd index 527fa725c..bf6067202 100644 --- a/man/plot.Rd +++ b/man/plot.Rd @@ -22,6 +22,7 @@ \alias{scale_sir_colours} \alias{scale_sir_colors} \alias{theme_sir} +\alias{labels_taxonomy} \title{Plotting Helpers for AMR Data Analysis} \usage{ scale_x_mic(keep_operators = "edges", mic_range = NULL, ...) @@ -150,6 +151,8 @@ scale_sir_colours( ) theme_sir() + +labels_taxonomy(x, ...) } \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.} @@ -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{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()}}.} @@ -229,6 +232,11 @@ 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[=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 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 +385,16 @@ plot(some_disk_values, mo = "Escherichia coli", ab = "cipro") plot(some_disk_values, mo = "Escherichia coli", ab = "cipro", language = "nl") 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) +} } diff --git a/tests/testthat/test-italicise_taxonomy.R b/tests/testthat/test-italicise_taxonomy.R index 965043f45..8c44e89b2 100644 --- a/tests/testthat/test-italicise_taxonomy.R +++ b/tests/testthat/test-italicise_taxonomy.R @@ -48,4 +48,29 @@ test_that("test-italicise_taxonomy.R", { italicise_taxonomy("test for E. coli", "html"), "test for E. coli" ) + + # 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()) }) diff --git a/vignettes/AMR.Rmd b/vignettes/AMR.Rmd index ed89b3308..9ca26231e 100755 --- a/vignettes/AMR.Rmd +++ b/vignettes/AMR.Rmd @@ -433,6 +433,17 @@ autoplot(mic_values) 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*