Merge branch 'main' into breakpoints

This commit is contained in:
dr. M.S. (Matthijs) Berends committed 2026-11-09 22:11:08 +01:00
commit b74501663b
6 files changed
+52 -11

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
Package: AMR
Version: 3.1.0.9021
Version: 3.1.0.9022
Date: 2026-11-09
Title: Antimicrobial Resistance Data Analysis
Description: Functions to simplify and standardise antimicrobial resistance (AMR)
+3 -2
View File
@@ -1,4 +1,4 @@
# AMR 3.1.0.9021
# 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.
@@ -46,7 +46,8 @@ It also adds a `site` argument to `as.sir()`, stores EUCAST MIC breakpoints exac
* 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"
* 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
* *Morganella* is interpreted as reported, with the breakpoints of Enterobacterales or Morganellaceae and not of *M. morganii*, which is now tested (#330)
+29 -6
View File
@@ -36,7 +36,7 @@
#' @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 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
#' @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.
@@ -240,6 +240,21 @@ ggplot_sir <- function(data,
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) +
create_geom_sir(
position = position, x = x, fill = fill, facet = facet, translate_ab = translate_ab,
@@ -280,17 +295,21 @@ ggplot_sir <- function(data,
p <- p + ggplot2::scale_fill_discrete(labels = labels_taxonomy)
}
if (!is.null(facet)) {
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
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 <- tryCatch(
levels(sir_df(data = data, translate_ab = translate_ab, language = language, minimum = minimum, guideline = guideline)$interpretation)[1],
error = function(e) NULL
)
susceptible <- if (is.null(plot_df)) NULL else levels(plot_df$interpretation)[1]
if (!is.null(susceptible)) {
y.title <- paste(y.title, susceptible)
}
@@ -389,6 +408,10 @@ sir_plot_data <- function(data, x, fill, facet, translate_ab, minimum, language,
# 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
@@ -67,7 +67,7 @@ labels_sir_count(
\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.}
+1 -1
View File
@@ -185,7 +185,7 @@ labels_taxonomy(x, ...)
\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.}
+17
View File
@@ -186,10 +186,27 @@ test_that("test-ggplot_sir.R", {
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"))
}
})