diff --git a/DESCRIPTION b/DESCRIPTION index 63807d16..44dda0e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AMR -Version: 1.7.1.9030 -Date: 2021-08-29 +Version: 1.7.1.9031 +Date: 2021-08-30 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/NEWS.md b/NEWS.md index f1d62d84..c158c07b 100755 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,5 @@ -# `AMR` 1.7.1.9030 -## Last updated: 29 August 2021 +# `AMR` 1.7.1.9031 +## Last updated: 30 August 2021 ### Breaking changes * Removed `p_symbol()` and all `filter_*()` functions (except for `filter_first_isolate()`), which were all deprecated in a previous package version diff --git a/R/aa_helper_functions.R b/R/aa_helper_functions.R index 172b5bb0..6d7088fa 100755 --- a/R/aa_helper_functions.R +++ b/R/aa_helper_functions.R @@ -736,6 +736,7 @@ get_current_data <- function(arg_name, call) { if (!is.null(cur_data_all)) { out <- tryCatch(cur_data_all(), error = function(e) NULL) if (is.data.frame(out)) { + messsage("==> RETURNING cur_data_all()") return(structure(out, type = "dplyr_cur_data_all")) } } @@ -748,14 +749,17 @@ get_current_data <- function(arg_name, call) { if (!is.null(env$`.data`) && is.data.frame(env$`.data`)) { # an element `.data` will be in the environment when using `dplyr::select()` # (but not when using `dplyr::filter()`, `dplyr::mutate()` or `dplyr::summarise()`) + messsage("==> RETURNING dplyr_selector") return(structure(env$`.data`, type = "dplyr_selector")) } else if (!is.null(env$xx) && is.data.frame(env$xx)) { # an element `xx` will be in the environment for rows + cols, e.g. `example_isolates[c(1:3), carbapenems()]` + messsage("==> RETURNING base_R 1") return(structure(env$xx, type = "base_R")) } else if (!is.null(env$x) && is.data.frame(env$x)) { # an element `x` will be in the environment for only cols, e.g. `example_isolates[, carbapenems()]` + messsage("==> RETURNING base_R 2") return(structure(env$x, type = "base_R")) } } diff --git a/R/first_isolate.R b/R/first_isolate.R index 9a157c9b..ca4e6cb7 100755 --- a/R/first_isolate.R +++ b/R/first_isolate.R @@ -23,9 +23,9 @@ # how to conduct AMR data analysis: https://msberends.github.io/AMR/ # # ==================================================================== # -#' Determine First (Weighted) Isolates +#' Determine First Isolates #' -#' Determine first (weighted) isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler *et al.* in 2007 (\doi{10.1086/511864}). To determine patient episodes not necessarily based on microorganisms, use [is_new_episode()] that also supports grouping with the `dplyr` package. +#' Determine first isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler *et al.* in 2007 (\doi{10.1086/511864}). To determine patient episodes not necessarily based on microorganisms, use [is_new_episode()] that also supports [grouping with the `dplyr` package][dplyr::group_by()] . #' @inheritSection lifecycle Stable Lifecycle #' @param x a [data.frame] containing isolates. Can be left blank for automatic determination, see *Examples*. #' @param col_date column name of the result date (or date that is was received on the lab), defaults to the first column with a date class @@ -34,7 +34,7 @@ #' @param col_testcode column name of the test codes. Use `col_testcode = NULL` to **not** exclude certain test codes (such as test codes for screening). In that case `testcodes_exclude` will be ignored. #' @param col_specimen column name of the specimen type or group #' @param col_icu column name of the logicals (`TRUE`/`FALSE`) whether a ward or department is an Intensive Care Unit (ICU) -#' @param col_keyantimicrobials (only useful when `method = "phenotype-based"`) column name of the key antimicrobials to determine first (weighted) isolates, see [key_antimicrobials()]. Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use `col_keyantimicrobials = FALSE` to prevent this. Can also be the output of [key_antimicrobials()]. +#' @param col_keyantimicrobials (only useful when `method = "phenotype-based"`) column name of the key antimicrobials to determine first isolates, see [key_antimicrobials()]. Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use `col_keyantimicrobials = FALSE` to prevent this. Can also be the output of [key_antimicrobials()]. #' @param episode_days episode in days after which a genus/species combination will be determined as 'first isolate' again. The default of 365 days is based on the guideline by CLSI, see *Source*. #' @param testcodes_exclude a [character] vector with test codes that should be excluded (case-insensitive) #' @param icu_exclude a [logical] to indicate whether ICU isolates should be excluded (rows with value `TRUE` in the column set with `col_icu`) @@ -102,9 +102,9 @@ #' #' This is a more reliable method, since it also *weighs* the antibiogram (antimicrobial test results) yielding so-called 'first weighted isolates'. There are two different methods to weigh the antibiogram: #' -#' 1. Using `type = "points"` and argument `points_threshold` +#' 1. Using `type = "points"` and argument `points_threshold` (default) #' -#' This method weighs *all* antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as 0.5 points, a difference from S to R (or vice versa) counts as 1 point. When the sum of points exceeds `points_threshold`, which defaults to `2`, an isolate will be selected as a first weighted isolate. +#' This method weighs *all* antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as `0.5` points, a difference from S to R (or vice versa) counts as `1` point. When the sum of points exceeds `points_threshold`, which defaults to `2`, an isolate will be selected as a first weighted isolate. #' #' All antimicrobials are internally selected using the [all_antimicrobials()] function. The output of this function does not need to be passed to the [first_isolate()] function. #' @@ -218,7 +218,7 @@ first_isolate <- function(x = NULL, meet_criteria(col_icu, allow_class = "character", has_length = 1, allow_NULL = TRUE, is_in = colnames(x)) # method method <- coerce_method(method) - meet_criteria(method, allow_class = "character", has_length = 1, is_in = c("phenotype-based", "episode-based", "patient-based", "isolate-based", "p", "e", "i")) + meet_criteria(method, allow_class = "character", has_length = 1, is_in = c("phenotype-based", "episode-based", "patient-based", "isolate-based")) # key antimicrobials if (length(col_keyantimicrobials) > 1) { meet_criteria(col_keyantimicrobials, allow_class = "character", has_length = nrow(x)) @@ -256,11 +256,11 @@ first_isolate <- function(x = NULL, method <- "episode-based" } if (info == TRUE & message_not_thrown_before("first_isolate.method")) { - message_(paste0("Determining first isolates using the '", font_bold(method), "' method", + message_(paste0("Determining first isolates ", ifelse(method %in% c("episode-based", "phenotype-based"), ifelse(is.infinite(episode_days), - " without a specified episode length", - paste(" and an episode length of", episode_days, "days")), + "without a specified episode length", + paste("using an episode length of", episode_days, "days")), "")), as_note = FALSE, add_fn = font_black) @@ -360,7 +360,7 @@ first_isolate <- function(x = NULL, } # remove testcodes if (!is.null(testcodes_exclude) & info == TRUE & message_not_thrown_before("first_isolate.excludingtestcodes")) { - message_("Excluding test codes: ", toString(paste0("'", testcodes_exclude, "'")), + message_("Excluding test codes: ", vector_and(testcodes_exclude, quotes = TRUE), add_fn = font_black, as_note = FALSE) } @@ -454,9 +454,7 @@ first_isolate <- function(x = NULL, episode_days = episode_days), use.names = FALSE) - weighted.notice <- "" if (!is.null(col_keyantimicrobials)) { - weighted.notice <- "weighted " if (info == TRUE & message_not_thrown_before("first_isolate.type")) { if (type == "keyantimicrobials") { message_("Basing inclusion on key antimicrobials, ", @@ -583,16 +581,16 @@ first_isolate <- function(x = NULL, } # mark up number of found n_found <- format(n_found, big.mark = big.mark, decimal.mark = decimal.mark) - if (p_found_total != p_found_scope) { - msg_txt <- paste0("=> Found ", - font_bold(paste0(n_found, " first ", weighted.notice, "isolates")), - " (", method, ", ", p_found_scope, " within scope and ", p_found_total, " of total where a microbial ID was available)") - } else { - msg_txt <- paste0("=> Found ", - font_bold(paste0(n_found, " first ", weighted.notice, "isolates")), - " (", method, ", ", p_found_total, " of total where a microbial ID was available)") - } - message_(msg_txt, add_fn = font_black, as_note = FALSE) + message_(paste0("=> Found ", + font_bold(paste0(n_found, + ifelse(method == "isolate-based", "", paste0(" '", method, "'")), + " first isolates")), + " (", + ifelse(p_found_total != p_found_scope, + paste0(p_found_scope, " within scope and "), + ""), + p_found_total, " of total where a microbial ID was available)"), + add_fn = font_black, as_note = FALSE) } x$newvar_first_isolate @@ -619,7 +617,7 @@ filter_first_isolate <- function(x = NULL, meet_criteria(col_mo, allow_class = "character", has_length = 1, allow_NULL = TRUE, is_in = colnames(x)) meet_criteria(episode_days, allow_class = c("numeric", "integer"), has_length = 1, is_positive = TRUE, is_finite = FALSE) method <- coerce_method(method) - meet_criteria(method, allow_class = "character", has_length = 1, is_in = c("phenotype-based", "episode-based", "patient-based", "isolate-based", "p", "e", "i")) + meet_criteria(method, allow_class = "character", has_length = 1, is_in = c("phenotype-based", "episode-based", "patient-based", "isolate-based")) subset(x, first_isolate(x = x, col_date = col_date, @@ -637,7 +635,7 @@ coerce_method <- function(method) { method <- tolower(as.character(method[1L])) method[method %like% "^(p$|pheno)"] <- "phenotype-based" method[method %like% "^(e$|episode)"] <- "episode-based" - method[method %like% "^patient"] <- "patient-based" + method[method %like% "^pat"] <- "patient-based" method[method %like% "^(i$|iso)"] <- "isolate-based" method } diff --git a/R/guess_ab_col.R b/R/guess_ab_col.R index ea782ec8..e52901fc 100755 --- a/R/guess_ab_col.R +++ b/R/guess_ab_col.R @@ -179,7 +179,7 @@ get_column_abx <- function(x, } else { return(NA_character_) } - }) + }, USE.NAMES = FALSE) x_columns <- x_columns[!is.na(x_columns)] x <- x[, x_columns, drop = FALSE] # without drop = FALSE, x will become a vector when x_columns is length 1 @@ -192,13 +192,27 @@ get_column_abx <- function(x, # add from self-defined dots (...): # such as get_column_abx(example_isolates %>% rename(thisone = AMX), amox = "thisone") + all_okay <- TRUE dots <- list(...) if (length(dots) > 0) { newnames <- suppressWarnings(as.ab(names(dots), info = FALSE)) if (any(is.na(newnames))) { - warning_("Invalid antibiotic reference(s): ", toString(names(dots)[is.na(newnames)]), + if (info == TRUE) { + message_(" WARNING", add_fn = list(font_yellow, font_bold), as_note = FALSE) + } + warning_("Invalid antibiotic reference(s): ", vector_and(names(dots)[is.na(newnames)], quotes = FALSE), call = FALSE, immediate = TRUE) + all_okay <- FALSE + } + unexisting_cols <- which(!vapply(FUN.VALUE = logical(1), dots, function(col) all(col %in% x_columns))) + if (length(unexisting_cols) > 0) { + if (info == TRUE) { + message_(" ERROR", add_fn = list(font_red, font_bold), as_note = FALSE) + } + stop_("Column(s) not found: ", vector_and(unlist(dots[[unexisting_cols]]), quotes = FALSE), + call = FALSE) + all_okay <- FALSE } # turn all NULLs to NAs dots <- unlist(lapply(dots, function(dot) if (is.null(dot)) NA else dot)) @@ -211,7 +225,7 @@ get_column_abx <- function(x, } if (length(out) == 0) { - if (info == TRUE) { + if (info == TRUE & all_okay == TRUE) { message_("No columns found.") } pkg_env$get_column_abx.call <- unique_call_id(entire_session = FALSE) @@ -232,7 +246,7 @@ get_column_abx <- function(x, } # succeeded with auto-guessing - if (info == TRUE) { + if (info == TRUE & all_okay == TRUE) { message_(" OK.", add_fn = list(font_green, font_bold), as_note = FALSE) } diff --git a/data-raw/AMR_latest.tar.gz b/data-raw/AMR_latest.tar.gz index d1a048b8..2d8e5588 100644 Binary files a/data-raw/AMR_latest.tar.gz and b/data-raw/AMR_latest.tar.gz differ diff --git a/docs/articles/datasets.html b/docs/articles/datasets.html index 306ea926..d738f865 100644 --- a/docs/articles/datasets.html +++ b/docs/articles/datasets.html @@ -44,7 +44,7 @@ AMR (for R) - 1.7.1.9030 + 1.7.1.9031 @@ -190,7 +190,7 @@ @@ -240,12 +240,12 @@ Source: NEWS.md -
-

- Unreleased AMR 1.7.1.9030

-
+
+

+ Unreleased AMR 1.7.1.9031

+

-Last updated: 29 August 2021 +Last updated: 30 August 2021

diff --git a/docs/reference/first_isolate.html b/docs/reference/first_isolate.html index 6968ef2b..a3f9824d 100644 --- a/docs/reference/first_isolate.html +++ b/docs/reference/first_isolate.html @@ -6,7 +6,7 @@ -Determine First (Weighted) Isolates — first_isolate • AMR (for R) +Determine First Isolates — first_isolate • AMR (for R) @@ -48,9 +48,9 @@ - - + + @@ -94,7 +94,7 @@ AMR (for R) - 1.7.1.9030 + 1.7.1.9031

@@ -238,14 +238,14 @@
-

Determine first (weighted) isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler et al. in 2007 (doi: 10.1086/511864 -). To determine patient episodes not necessarily based on microorganisms, use is_new_episode() that also supports grouping with the dplyr package.

+

Determine first isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler et al. in 2007 (doi: 10.1086/511864 +). To determine patient episodes not necessarily based on microorganisms, use is_new_episode() that also supports grouping with the dplyr package .

first_isolate(
@@ -314,7 +314,7 @@
     
     
       col_keyantimicrobials
-      

(only useful when method = "phenotype-based") column name of the key antimicrobials to determine first (weighted) isolates, see key_antimicrobials(). Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use col_keyantimicrobials = FALSE to prevent this. Can also be the output of key_antimicrobials().

+

(only useful when method = "phenotype-based") column name of the key antimicrobials to determine first isolates, see key_antimicrobials(). Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use col_keyantimicrobials = FALSE to prevent this. Can also be the output of key_antimicrobials().

episode_days @@ -429,8 +429,8 @@

This is a more reliable method, since it also weighs the antibiogram (antimicrobial test results) yielding so-called 'first weighted isolates'. There are two different methods to weigh the antibiogram:

    -
  1. Using type = "points" and argument points_threshold

    -

    This method weighs all antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as 0.5 points, a difference from S to R (or vice versa) counts as 1 point. When the sum of points exceeds points_threshold, which defaults to 2, an isolate will be selected as a first weighted isolate.

    +
  2. Using type = "points" and argument points_threshold (default)

    +

    This method weighs all antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as 0.5 points, a difference from S to R (or vice versa) counts as 1 point. When the sum of points exceeds points_threshold, which defaults to 2, an isolate will be selected as a first weighted isolate.

    All antimicrobials are internally selected using the all_antimicrobials() function. The output of this function does not need to be passed to the first_isolate() function.

  3. Using type = "keyantimicrobials" and argument ignore_I

    This method only weighs specific antimicrobial agents, called key antimicrobials. Any difference from S to R (or vice versa) in these key antimicrobials will select an isolate as a first weighted isolate. With ignore_I = FALSE, also differences from I to S or R (or vice versa) will lead to this.

    diff --git a/docs/reference/index.html b/docs/reference/index.html index 4c787440..0fb823ab 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -92,7 +92,7 @@ AMR (for R) - 1.7.1.9030 + 1.7.1.9031
@@ -393,7 +393,7 @@

first_isolate() filter_first_isolate()

-

Determine First (Weighted) Isolates

+

Determine First Isolates

diff --git a/inst/tinytest/test-first_isolate.R b/inst/tinytest/test-first_isolate.R index e7ddc857..f6e48fcd 100755 --- a/inst/tinytest/test-first_isolate.R +++ b/inst/tinytest/test-first_isolate.R @@ -133,9 +133,7 @@ if (AMR:::pkg_is_available("dplyr")) { first_isolate(info = TRUE)) # groups - message("get to first isolates L136") x <- example_isolates %>% group_by(ward_icu) %>% mutate(first = first_isolate()) - message("get to first isolates L138") y <- example_isolates %>% group_by(ward_icu) %>% mutate(first = first_isolate(.)) expect_identical(x, y) diff --git a/man/first_isolate.Rd b/man/first_isolate.Rd index d37370b1..0e305900 100755 --- a/man/first_isolate.Rd +++ b/man/first_isolate.Rd @@ -3,7 +3,7 @@ \name{first_isolate} \alias{first_isolate} \alias{filter_first_isolate} -\title{Determine First (Weighted) Isolates} +\title{Determine First Isolates} \source{ Methodology of this function is strictly based on: \itemize{ @@ -60,7 +60,7 @@ filter_first_isolate( \item{col_icu}{column name of the logicals (\code{TRUE}/\code{FALSE}) whether a ward or department is an Intensive Care Unit (ICU)} -\item{col_keyantimicrobials}{(only useful when \code{method = "phenotype-based"}) column name of the key antimicrobials to determine first (weighted) isolates, see \code{\link[=key_antimicrobials]{key_antimicrobials()}}. Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use \code{col_keyantimicrobials = FALSE} to prevent this. Can also be the output of \code{\link[=key_antimicrobials]{key_antimicrobials()}}.} +\item{col_keyantimicrobials}{(only useful when \code{method = "phenotype-based"}) column name of the key antimicrobials to determine first isolates, see \code{\link[=key_antimicrobials]{key_antimicrobials()}}. Defaults to the first column that starts with 'key' followed by 'ab' or 'antibiotics' or 'antimicrobials' (case insensitive). Use \code{col_keyantimicrobials = FALSE} to prevent this. Can also be the output of \code{\link[=key_antimicrobials]{key_antimicrobials()}}.} \item{episode_days}{episode in days after which a genus/species combination will be determined as 'first isolate' again. The default of 365 days is based on the guideline by CLSI, see \emph{Source}.} @@ -90,7 +90,7 @@ filter_first_isolate( A \code{\link{logical}} vector } \description{ -Determine first (weighted) isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler \emph{et al.} in 2007 (\doi{10.1086/511864}). To determine patient episodes not necessarily based on microorganisms, use \code{\link[=is_new_episode]{is_new_episode()}} that also supports grouping with the \code{dplyr} package. +Determine first isolates of all microorganisms of every patient per episode and (if needed) per specimen type. These functions support all four methods as summarised by Hindler \emph{et al.} in 2007 (\doi{10.1086/511864}). To determine patient episodes not necessarily based on microorganisms, use \code{\link[=is_new_episode]{is_new_episode()}} that also supports \link[dplyr:group_by]{grouping with the \code{dplyr} package} . } \details{ To conduct epidemiological analyses on antimicrobial resistance data, only so-called first isolates should be included to prevent overestimation and underestimation of antimicrobial resistance. Different methods can be used to do so, see below. @@ -147,9 +147,9 @@ This is the most common method to correct for duplicate isolates. Patients are c This is a more reliable method, since it also \emph{weighs} the antibiogram (antimicrobial test results) yielding so-called 'first weighted isolates'. There are two different methods to weigh the antibiogram: \enumerate{ -\item Using \code{type = "points"} and argument \code{points_threshold} +\item Using \code{type = "points"} and argument \code{points_threshold} (default) -This method weighs \emph{all} antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as 0.5 points, a difference from S to R (or vice versa) counts as 1 point. When the sum of points exceeds \code{points_threshold}, which defaults to \code{2}, an isolate will be selected as a first weighted isolate. +This method weighs \emph{all} antimicrobial agents available in the data set. Any difference from I to S or R (or vice versa) counts as \code{0.5} points, a difference from S to R (or vice versa) counts as \code{1} point. When the sum of points exceeds \code{points_threshold}, which defaults to \code{2}, an isolate will be selected as a first weighted isolate. All antimicrobials are internally selected using the \code{\link[=all_antimicrobials]{all_antimicrobials()}} function. The output of this function does not need to be passed to the \code{\link[=first_isolate]{first_isolate()}} function. \item Using \code{type = "keyantimicrobials"} and argument \code{ignore_I} diff --git a/tests/tinytest.R b/tests/tinytest.R index 86f63498..6af3eb90 100644 --- a/tests/tinytest.R +++ b/tests/tinytest.R @@ -33,9 +33,13 @@ if (identical(Sys.getenv("R_RUN_TINYTEST"), "true")) { testdir = ifelse(AMR:::dir.exists("inst/tinytest"), "inst/tinytest", "tinytest"), - verbose = FALSE) - cat(attributes(out)$duration, "\n") + verbose = 99, + color = FALSE) + cat("SUMMARY:\n") print(summary(out)) + cat("WARNINGS:\n") print(warnings()) + cat(attributes(out)$duration, "\n") + } }