diff --git a/DESCRIPTION b/DESCRIPTION index 3ba56e875..0e03489be 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: AMR -Version: 3.0.1.9131 -Date: 2026-10-13 +Version: 3.0.1.9132 +Date: 2026-10-14 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 314230129..9ea295eea 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# AMR 3.0.1.9131 +# AMR 3.0.1.9132 Planned as v3.1.0, mid October 2026. @@ -93,6 +93,7 @@ Planned as v3.1.0, mid October 2026. * Author references now strip `emend.` and everything after it, so that `ref` reflects the combination authority rather than the emendation author (e.g., *Rhodococcus equi* now returns "Goodfellow et al., 1977" instead of "Nouioui et al., 2018") * Antimicrobial coercion (`as.ab()`) and `antimicrobials` data set * Considerably faster through the same session cache design as `as.mo()` (#313) + * Input that is not a known name or code is now coerced up to 70 times faster, with identical results, as LOINC codes, synonyms, and abbreviations are looked up in one step instead of per antimicrobial, and translated names are built once per language * Fixed a bug where results of `fast_mode` (used internally) were remembered and reused by later regular calls, which could return `NA` for valid input (#313) * Codes containing "PH" or "TH" (e.g., `ETH`, `PHE`) no longer return `NA` when mixed with unrecognised input (#245) * Input that is not valid UTF-8 (e.g., text exported in a local encoding such as CP949) now returns `NA` with a warning instead of an error @@ -119,6 +120,7 @@ Planned as v3.1.0, mid October 2026. * Translations * Improved French translations, kindly provided by Mathilde Garé (#322) * Improved translations of combination names and antimicrobial groups in 20 languages + * Translating is now around 50 times faster, with identical results, as the translations are prepared once per language and applied to all values at once. This also makes `ab_group()` faster in other languages. * Translations are no longer translated again (e.g., "Ticarcillinee" in Dutch and "groupee" in French), and parts of combination names are no longer left untranslated * Fixed Italian names of CoNS and CoPS (#256), Dutch names of antimicrobials, and output of `sir_df()` in other languages (#272) * Interpretive rules (`interpretive_rules()`, `eucast_rules()`, and `clsi_rules()`) diff --git a/R/aa_session_cache.R b/R/aa_session_cache.R index 1f8dc1b33..7122b1565 100644 --- a/R/aa_session_cache.R +++ b/R/aa_session_cache.R @@ -37,11 +37,15 @@ # 2. Coercion caches, `AMR_env$ab_previously_coerced` and `AMR_env$mo_previously_coerced`, that remember the # outcome of earlier coercions. Both have the same structure: a `key` (the cleaned input together with the # settings that affect the outcome), the original `input`, and the resulting code as `value`. -# 3. For microorganisms, the mapping from (outdated) codes to codes of currently accepted names, which is +# 3. Translated names of antimicrobials and microorganisms, built once per language, see get_ab_translated_index() and +# get_mo_translated_index(). +# 4. For microorganisms, the mapping from (outdated) codes to codes of currently accepted names, which is # memoised per unique code, see synonym_mo_to_accepted_mo(). # # All of these are invalidated with reset_ab_cache() or reset_mo_cache(), which must be called whenever the # lookup tables change, e.g. in add_custom_antimicrobials() and add_custom_microorganisms(). +# The prepared translation tables of translate_into_language(), see get_translation_table(), are kept for the whole +# session, as the internal translations never change. new_coercion_cache <- function() { data.frame( @@ -92,6 +96,7 @@ coercion_cache_add <- function(type, keys, inputs, values) { reset_ab_cache <- function() { AMR_env$AB_index <- NULL + AMR_env$AB_translated <- NULL AMR_env$custom_ab_id_keys <- NULL AMR_env$ab_previously_coerced <- new_coercion_cache() } @@ -111,16 +116,37 @@ get_ab_index <- function() { atc_ab <- rep(ab, lengths(AMR_env$AB_lookup$atc)) synonyms <- tolower(unlist(AMR_env$AB_lookup$synonyms, use.names = FALSE)) synonyms_ab <- rep(ab, lengths(AMR_env$AB_lookup$synonyms)) + # generalised LOINC codes, synonyms, and abbreviations as flat vectors, with the row number in `AMR_env$AB_lookup` + flat <- function(col) { + vals <- AMR_env$AB_lookup[[col]] + list(value = unlist(vals, use.names = FALSE), row = rep(seq_along(vals), lengths(vals))) + } + gen_syn <- flat("generalised_synonyms") + # synonyms of at least 5 characters, for the Levenshtein distance in as.ab() + gen_syn_long <- which(!is.na(gen_syn$value) & nchar(gen_syn$value) >= 5) AMR_env$AB_index <- list( atc = atc[!is.na(atc)], atc_ab = atc_ab[!is.na(atc)], synonyms = synonyms[!is.na(synonyms)], - synonyms_ab = synonyms_ab[!is.na(synonyms)] + synonyms_ab = synonyms_ab[!is.na(synonyms)], + generalised_loinc = flat("generalised_loinc"), + generalised_synonyms = gen_syn, + generalised_synonyms_long = list(value = gen_syn$value[gen_syn_long], row = gen_syn$row[gen_syn_long]), + generalised_abbreviations = flat("generalised_abbreviations") ) } AMR_env$AB_index } +get_ab_translated_index <- function(language) { + # translated and generalised names of all antimicrobials, used by as.ab() for input that could not be matched otherwise + # this is built once per language + if (is.null(AMR_env$AB_translated[[language]])) { + AMR_env$AB_translated[[language]] <- generalise_antibiotic_name(translate_AMR(AMR_env$AB_lookup$name, language = language)) + } + AMR_env$AB_translated[[language]] +} + get_mo_index <- function() { add_MO_lookup_to_AMR_env() if (is.null(AMR_env$MO_index)) { diff --git a/R/ab.R b/R/ab.R index d3009aa50..7910aa186 100755 --- a/R/ab.R +++ b/R/ab.R @@ -299,34 +299,25 @@ as.ab <- function(x, flag_multiple_results = TRUE, language = get_AMR_locale(), } # exact LOINC code - loinc_found <- unlist(lapply( - AMR_env$AB_lookup$generalised_loinc, - function(s) x[i] %in% s - )) - found <- AMR_env$AB_lookup$ab[loinc_found == TRUE] + idx <- get_ab_index()$generalised_loinc + found <- AMR_env$AB_lookup$ab[sort(unique(idx$row[idx$value %in% x[i]]))] if (length(found) > 0) { x_new[i] <- note_if_more_than_one_found(found, i, from_text) next } # exact synonym - synonym_found <- unlist(lapply( - AMR_env$AB_lookup$generalised_synonyms, - function(s) x[i] %in% s - )) - found <- AMR_env$AB_lookup$ab[synonym_found == TRUE] + idx <- get_ab_index()$generalised_synonyms + found <- AMR_env$AB_lookup$ab[sort(unique(idx$row[idx$value %in% x[i]]))] if (length(found) > 0) { x_new[i] <- note_if_more_than_one_found(found, i, from_text) next } # exact abbreviation - abbr_found <- unlist(lapply( - AMR_env$AB_lookup$generalised_abbreviations, - # require at least 2 characters for abbreviations - function(s) x[i] %in% s && nchar(x[i]) >= 2 - )) - found <- AMR_env$AB_lookup$ab[abbr_found == TRUE] + idx <- get_ab_index()$generalised_abbreviations + # require at least 2 characters for abbreviations + found <- AMR_env$AB_lookup$ab[sort(unique(idx$row[idx$value %in% x[i] & nchar(x[i]) >= 2]))] if (length(found) > 0) { x_new[i] <- note_if_more_than_one_found(found, i, from_text) next @@ -392,11 +383,8 @@ as.ab <- function(x, flag_multiple_results = TRUE, language = get_AMR_locale(), } # and try if any synonym starts with it - synonym_found <- unlist(lapply( - AMR_env$AB_lookup$generalised_synonyms, - function(s) any(s %like% paste0("^", x_spelling)) - )) - found <- AMR_env$AB_lookup$ab[synonym_found == TRUE] + idx <- get_ab_index()$generalised_synonyms + found <- AMR_env$AB_lookup$ab[sort(unique(idx$row[idx$value %like% paste0("^", x_spelling)]))] if (length(found) > 0) { x_new[i] <- note_if_more_than_one_found(found, i, from_text) next @@ -413,24 +401,19 @@ as.ab <- function(x, flag_multiple_results = TRUE, language = get_AMR_locale(), costs = c(insertions = 1, deletions = 1, substitutions = 2), counts = FALSE )) - ab_df$lev_syn <- vapply( - FUN.VALUE = double(1), - ab_df$generalised_synonyms, - function(y) { - ifelse(all(is.na(y)) || length(y[nchar(y) >= 5]) == 0, - 999, - min(as.double(utils::adist(x[i], y[nchar(y) >= 5], - ignore.case = FALSE, - fixed = TRUE, - costs = c(insertions = 1, deletions = 1, substitutions = 2), - counts = FALSE - )), na.rm = TRUE) - ) - }, - USE.NAMES = FALSE - ) + # lowest distance to the synonyms of at least 5 characters, 999 if there are none + idx <- get_ab_index()$generalised_synonyms_long + lev_syn <- as.double(utils::adist(x[i], idx$value, + ignore.case = FALSE, + fixed = TRUE, + costs = c(insertions = 1, deletions = 1, substitutions = 2), + counts = FALSE + )) + ab_df$lev_syn <- 999 + lev_syn_min <- vapply(FUN.VALUE = double(1), split(lev_syn, idx$row), min) + ab_df$lev_syn[as.integer(names(lev_syn_min))] <- lev_syn_min if (!is.null(language) && language != "en") { - ab_df$trans <- generalise_antibiotic_name(translate_AMR(ab_df$name, language = language)) + ab_df$trans <- get_ab_translated_index(language) ab_df$lev_trans <- as.double(utils::adist(x[i], ab_df$trans, ignore.case = FALSE, fixed = TRUE, diff --git a/R/ab_property.R b/R/ab_property.R index 8adef951b..71e686cfa 100755 --- a/R/ab_property.R +++ b/R/ab_property.R @@ -180,9 +180,11 @@ ab_group <- function(x, language = get_AMR_locale(), all_groups = FALSE, ...) { # take the first match based on ABX_PRIORITY_LIST grps[[i]] <- grps[[i]][1] } - if (language != "en") { - grps[[i]] <- translate_into_language(grps[[i]], language = language, only_affect_ab_names = TRUE) - } + } + if (language != "en" && length(grps) > 0) { + # translate all groups at once + grps_translated <- translate_into_language(unlist(grps, use.names = FALSE), language = language, only_affect_ab_names = TRUE) + grps[] <- split(grps_translated, factor(rep(seq_along(grps), lengths(grps)), levels = seq_along(grps))) } names(grps) <- x if (length(grps) == 1 || all_groups == FALSE) { diff --git a/R/like.R b/R/like.R index 60e8330ad..7acea8e08 100755 --- a/R/like.R +++ b/R/like.R @@ -49,7 +49,8 @@ #' @seealso [grepl()] #' @examples -#' # data.table has a more limited version of %like%, so detach it (without unloading it, as data.table does not support being unloaded): +#' # data.table has a more limited version of %like%, so detach it +#' # (without unloading it, as data.table does not support being unloaded): #' try(detach("package:data.table"), silent = TRUE) #' #' a <- "This is a test" diff --git a/R/mo_property.R b/R/mo_property.R index c5cfd7ed8..e51afa06e 100755 --- a/R/mo_property.R +++ b/R/mo_property.R @@ -50,7 +50,7 @@ #' #' Determination of human pathogenicity ([mo_pathogenicity()]) is strongly based on Bartlett *et al.* (2022, \doi{10.1099/mic.0.001269}). This function returns a [factor] with the levels *Pathogenic*, *Potentially pathogenic*, *Non-pathogenic*, and *Unknown*. #' -#' Determination of the Gram stain ([mo_gramstain()] is based on the taxonomic domain and phylum. Originally, Cavalier-Smith defined the so-called subkingdoms Negibacteria and Posibacteria (2002, [PMID 11837318](https://pubmed.ncbi.nlm.nih.gov/11837318/)), and only considered these phyla as Posibacteria: Actinobacteria, Chloroflexi, Firmicutes, and Tenericutes. These phyla were later renamed to Actinomycetota, Chloroflexota, Bacillota, and Mycoplasmatota (2021, [PMID 34694987](https://pubmed.ncbi.nlm.nih.gov/34694987/)). Bacteria in these phyla are considered Gram-positive in this `AMR` package, except for members of the class Negativicutes (within phylum Bacillota) which are Gram-negative. All other bacteria are considered Gram-negative. Species outside the kingdom of Bacteria will return a value `NA`. Functions [mo_is_gram_negative()] and [mo_is_gram_positive()] always return `TRUE` or `FALSE` (or `NA` when the input is `NA` or the MO code is `UNKNOWN`), thus always return `FALSE` for species outside the taxonomic kingdom of Bacteria. +#' Determination of the Gram stain ([mo_gramstain()] is based on the taxonomic domain and phylum. Originally, Cavalier-Smith defined the so-called subkingdoms Negibacteria and Posibacteria (2002, \doi{10.1099/00207713-52-1-7}), and only considered these phyla as Posibacteria: Actinobacteria, Chloroflexi, Firmicutes, and Tenericutes. These phyla were later renamed to Actinomycetota, Chloroflexota, Bacillota, and Mycoplasmatota (2021, \doi{10.1099/ijsem.0.005056}). Bacteria in these phyla are considered Gram-positive in this `AMR` package, except for members of the class Negativicutes (within phylum Bacillota) which are Gram-negative. All other bacteria are considered Gram-negative. Species outside the kingdom of Bacteria will return a value `NA`. Functions [mo_is_gram_negative()] and [mo_is_gram_positive()] always return `TRUE` or `FALSE` (or `NA` when the input is `NA` or the MO code is `UNKNOWN`), thus always return `FALSE` for species outside the taxonomic kingdom of Bacteria. #' #' Determination of yeasts ([mo_is_yeast()]) is based on the taxonomic domain and class. *Budding yeasts* are yeasts that reproduce asexually through a process called budding, where a new cell develops from a small protrusion on the parent cell. Taxonomically, these are members of the phylum Ascomycota, class Saccharomycetes (also called Hemiascomycetes) or Pichiomycetes. *True yeasts* quite specifically refers to yeasts in the underlying order Saccharomycetales (such as *Saccharomyces cerevisiae*). Thus, for all microorganisms that are member of the taxonomic class Saccharomycetes or Pichiomycetes, the function will return `TRUE`. It returns `FALSE` otherwise (or `NA` when the input is `NA` or the MO code is `UNKNOWN`). #' diff --git a/R/translate.R b/R/translate.R index 80b1f4374..5d036cd92 100755 --- a/R/translate.R +++ b/R/translate.R @@ -200,9 +200,86 @@ translate_into_language <- function(from, return(from) } - df_trans <- TRANSLATIONS # internal data file + trns <- get_translation_table(lang, only_unknown = only_unknown, only_affect_ab_names = only_affect_ab_names, only_affect_mo_names = only_affect_mo_names) + if (is.null(trns)) { + return(from) + } from.bak <- from from_unique <- unique(from) + + # check if text to look for is in one of the patterns + any_form_in_patterns <- tryCatch( + # the same as `from_unique %like% trns$any_pattern`, but without validating the long pattern on every call + any(grepl(trns$any_pattern, tolower(from_unique), fixed = trns$any_pattern_fixed, perl = !trns$any_pattern_fixed)), + error = function(e) { + warning_("Translation not possible. Please create an issue at {.url https://github.com/msberends/AMR/issues}. Many thanks!") + return(FALSE) + } + ) + if (!any_form_in_patterns) { + return(from) + } + + from_unique_translated <- from_unique + # combinations like "Ticarcillin/clavulanic acid" are translated per part, unless the combination itself is in the translations + is_combination <- grepl("/", from_unique, fixed = TRUE) & !from_unique %in% trns$pattern_all + if (any(is_combination)) { + # all parts of all combinations are translated at once + parts <- strsplit(from_unique[is_combination], "/", fixed = TRUE) + parts_translated <- translate_into_language(unlist(parts, use.names = FALSE), language = lang, only_unknown = only_unknown, only_affect_ab_names = only_affect_ab_names, only_affect_mo_names = only_affect_mo_names) + parts_translated <- split(parts_translated, factor(rep(seq_along(parts), lengths(parts)), levels = seq_along(parts))) + from_unique_translated[is_combination] <- vapply(FUN.VALUE = character(1), parts_translated, paste, collapse = "/", USE.NAMES = FALSE) + } + if (any(!is_combination)) { + # every pattern is applied to all strings at once, in order of the translation table + terms <- from_unique[!is_combination] + for (i in seq_along(trns$pattern)) { + terms <- gsub( + pattern = trns$pattern[i], + replacement = trns$replacement[i], + x = terms, + ignore.case = trns$ignore_case[i], + fixed = trns$fixed[i], + perl = !trns$fixed[i] + ) + } + # replace the placeholders by their translations + has_placeholder <- grepl("\001", terms, fixed = TRUE) + if (any(has_placeholder)) { + found <- gregexpr("\001[^\001\002]{3}\002", terms[has_placeholder], perl = TRUE) + regmatches(terms[has_placeholder], found) <- lapply(regmatches(terms[has_placeholder], found), function(p) trns$literal[match(p, trns$placeholder)]) + } + from_unique_translated[!is_combination] <- terms + } + + # force UTF-8 for diacritics + from_unique_translated <- enc2utf8(from_unique_translated) + + # a kind of left join to get all results back + out <- from_unique_translated[match(from.bak, from_unique)] + + if (!identical(from.bak, out) && get_AMR_locale() == lang && is.null(getOption("AMR_locale", default = NULL)) && message_not_thrown_before("translation", entire_session = TRUE) && interactive()) { + message(font_blue(word_wrap( + "Assuming the ", LANGUAGES_SUPPORTED_NAMES[[lang]]$exonym, " language (", + LANGUAGES_SUPPORTED_NAMES[[lang]]$endonym, ") for the AMR package. See `set_AMR_locale()` to change this or to silence this once-per-session note.", + as_note = TRUE + ))) + } + + out +} + +# the translation table for a language and the settings of translate_into_language(), built once per session as TRANSLATIONS is fixed +get_translation_table <- function(lang, only_unknown = FALSE, only_affect_ab_names = FALSE, only_affect_mo_names = FALSE) { + key <- paste(lang, only_unknown, only_affect_ab_names, only_affect_mo_names, sep = "|") + if (!key %in% names(AMR_env$translation_tables)) { + AMR_env$translation_tables[key] <- list(build_translation_table(lang, only_unknown, only_affect_ab_names, only_affect_mo_names)) + } + AMR_env$translation_tables[[key]] +} + +build_translation_table <- function(lang, only_unknown, only_affect_ab_names, only_affect_mo_names) { + df_trans <- TRANSLATIONS # internal data file # only keep lines where translation is available for this language df_trans <- df_trans[which(!is.na(df_trans[, lang, drop = TRUE])), , drop = FALSE] # and where the original string is not equal to the string in the target language @@ -217,7 +294,7 @@ translate_into_language <- function(from, df_trans <- subset(df_trans, affect_mo_name == TRUE) } if (NROW(df_trans) == 0) { - return(from) + return(NULL) } # default: case sensitive if value if 'case_sensitive' is missing: @@ -225,18 +302,8 @@ translate_into_language <- function(from, # default: not using regular expressions if 'regular_expr' is missing: df_trans$regular_expr[is.na(df_trans$regular_expr)] <- FALSE - # check if text to look for is in one of the patterns - any_form_in_patterns <- tryCatch( - any(from_unique %like% paste0("(", paste(gsub(" +\\(.*", "", df_trans$pattern), collapse = "|"), ")")), - error = function(e) { - warning_("Translation not possible. Please create an issue at {.url https://github.com/msberends/AMR/issues}. Many thanks!") - return(FALSE) - } - ) - - if (NROW(df_trans) == 0 | !any_form_in_patterns) { - return(from) - } + # to check if text to look for is in one of the patterns + any_pattern <- paste0("(", paste(gsub(" +\\(.*", "", df_trans$pattern), collapse = "|"), ")") if (only_affect_ab_names == TRUE) { df_trans$pattern[df_trans$regular_expr == TRUE] <- paste0(df_trans$pattern[df_trans$regular_expr == TRUE], "$") @@ -257,52 +324,16 @@ translate_into_language <- function(from, replacements, vapply(FUN.VALUE = character(1), seq_along(replacements), function(i) sub(literals[i], placeholders[i], replacements[i], fixed = TRUE)) ) - translate_exec <- function(term) { - for (i in seq_len(nrow(trns))) { - term <- gsub( - pattern = trns$pattern[i], - replacement = replacements_placeholder[i], - x = term, - ignore.case = !trns$case_sensitive[i] & trns$regular_expr[i], - fixed = !trns$regular_expr[i], - perl = trns$regular_expr[i] - ) - } - for (i in which(vapply(FUN.VALUE = logical(1), placeholders, grepl, x = term, fixed = TRUE))) { - term <- gsub(placeholders[i], literals[i], term, fixed = TRUE) - } - term - } - - from_unique_translated <- from_unique - # combinations like "Ticarcillin/clavulanic acid" are translated per part, unless the combination itself is in the translations - is_combination <- grepl("/", from_unique, fixed = TRUE) & !from_unique %in% df_trans$pattern - from_unique_translated[is_combination] <- vapply( - FUN.VALUE = character(1), - USE.NAMES = FALSE, - strsplit(from_unique[is_combination], "/", fixed = TRUE), - function(parts) paste(translate_into_language(parts, language = lang, only_unknown = only_unknown, only_affect_ab_names = only_affect_ab_names, only_affect_mo_names = only_affect_mo_names), collapse = "/") + list( + # all patterns, including combinations, to check whether a combination is translated as a whole + pattern_all = df_trans$pattern, + any_pattern = tolower(any_pattern), + any_pattern_fixed = !is_valid_regex(any_pattern), + pattern = trns$pattern, + replacement = replacements_placeholder, + ignore_case = !trns$case_sensitive & trns$regular_expr, + fixed = !trns$regular_expr, + placeholder = placeholders, + literal = literals ) - from_unique_translated[!is_combination] <- vapply( - FUN.VALUE = character(1), - USE.NAMES = FALSE, - from_unique[!is_combination], - translate_exec - ) - - # force UTF-8 for diacritics - from_unique_translated <- enc2utf8(from_unique_translated) - - # a kind of left join to get all results back - out <- from_unique_translated[match(from.bak, from_unique)] - - if (!identical(from.bak, out) && get_AMR_locale() == lang && is.null(getOption("AMR_locale", default = NULL)) && message_not_thrown_before("translation", entire_session = TRUE) && interactive()) { - message(font_blue(word_wrap( - "Assuming the ", LANGUAGES_SUPPORTED_NAMES[[lang]]$exonym, " language (", - LANGUAGES_SUPPORTED_NAMES[[lang]]$endonym, ") for the AMR package. See `set_AMR_locale()` to change this or to silence this once-per-session note.", - as_note = TRUE - ))) - } - - out } diff --git a/cran-comments.md b/cran-comments.md index 957d2b7a0..3772d1ca3 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -2,7 +2,7 @@ This version is a minor release (v3.1.0) following the bugfix release v3.0.1 in This package has two reverse dependencies, `MIC` and `MIMER`, both checked against this version. `MIMER` passes. `MIC` is affected by a change in the output of `as.sir()` for interpretation based on epidemiological cut-off values (ECOFFs), which makes some of its unit tests fail; its maintainer has been informed in advance through an issue in its GitHub repository. -As with all previous >20 releases, some CHECKs on `oldrel` may return a `NOTE` for narrowly exceeding the installation size limit. This has been reduced to a minimum in prior coordination with CRAN maintainers and currently returns only an `INFO` on `release` and `devel`. +As with all previous >20 releases, some CHECKs may return an `INFO` for narrowly exceeding the installation size limit. The size has been reduced to a minimum in prior coordination with CRAN maintainers. We treat this as a high-impact package: it was published in the *Journal of Statistical Software* (2022), is listed in the CRAN Task View "Epidemiology", and (based on cranlogs download statistics) is used globally. If there is anything to address, we would appreciate being informed before archiving the current version. We conduct extensive automated unit testing and have no indication of unresolved issues. diff --git a/man/like.Rd b/man/like.Rd index 7564c1a4a..c13038807 100644 --- a/man/like.Rd +++ b/man/like.Rd @@ -46,7 +46,8 @@ These \code{\link[=like]{like()}} and \verb{\%like\%}/\verb{\%unlike\%} function Using RStudio? The \verb{\%like\%}/\verb{\%unlike\%} functions can also be directly inserted in your code from the Addins menu and can have its own keyboard shortcut like \code{Shift+Ctrl+L} or \code{Shift+Cmd+L} (see menu \code{Tools} > \verb{Modify Keyboard Shortcuts...}). If you keep pressing your shortcut, the inserted text will be iterated over \verb{\%like\%} -> \verb{\%unlike\%} -> \verb{\%like_case\%} -> \verb{\%unlike_case\%}. } \examples{ -# data.table has a more limited version of \%like\%, so detach it (without unloading it, as data.table does not support being unloaded): +# data.table has a more limited version of \%like\%, so detach it +# (without unloading it, as data.table does not support being unloaded): try(detach("package:data.table"), silent = TRUE) a <- "This is a test" diff --git a/man/mo_property.Rd b/man/mo_property.Rd index 40cece8eb..6bf90268f 100644 --- a/man/mo_property.Rd +++ b/man/mo_property.Rd @@ -355,7 +355,7 @@ Following the formal introduction of the new kingdom rank into prokaryotic nomen Determination of human pathogenicity (\code{\link[=mo_pathogenicity]{mo_pathogenicity()}}) is strongly based on Bartlett \emph{et al.} (2022, \doi{10.1099/mic.0.001269}). This function returns a \link{factor} with the levels \emph{Pathogenic}, \emph{Potentially pathogenic}, \emph{Non-pathogenic}, and \emph{Unknown}. -Determination of the Gram stain (\code{\link[=mo_gramstain]{mo_gramstain()}} is based on the taxonomic domain and phylum. Originally, Cavalier-Smith defined the so-called subkingdoms Negibacteria and Posibacteria (2002, \href{https://pubmed.ncbi.nlm.nih.gov/11837318/}{PMID 11837318}), and only considered these phyla as Posibacteria: Actinobacteria, Chloroflexi, Firmicutes, and Tenericutes. These phyla were later renamed to Actinomycetota, Chloroflexota, Bacillota, and Mycoplasmatota (2021, \href{https://pubmed.ncbi.nlm.nih.gov/34694987/}{PMID 34694987}). Bacteria in these phyla are considered Gram-positive in this \code{AMR} package, except for members of the class Negativicutes (within phylum Bacillota) which are Gram-negative. All other bacteria are considered Gram-negative. Species outside the kingdom of Bacteria will return a value \code{NA}. Functions \code{\link[=mo_is_gram_negative]{mo_is_gram_negative()}} and \code{\link[=mo_is_gram_positive]{mo_is_gram_positive()}} always return \code{TRUE} or \code{FALSE} (or \code{NA} when the input is \code{NA} or the MO code is \code{UNKNOWN}), thus always return \code{FALSE} for species outside the taxonomic kingdom of Bacteria. +Determination of the Gram stain (\code{\link[=mo_gramstain]{mo_gramstain()}} is based on the taxonomic domain and phylum. Originally, Cavalier-Smith defined the so-called subkingdoms Negibacteria and Posibacteria (2002, \doi{10.1099/00207713-52-1-7}), and only considered these phyla as Posibacteria: Actinobacteria, Chloroflexi, Firmicutes, and Tenericutes. These phyla were later renamed to Actinomycetota, Chloroflexota, Bacillota, and Mycoplasmatota (2021, \doi{10.1099/ijsem.0.005056}). Bacteria in these phyla are considered Gram-positive in this \code{AMR} package, except for members of the class Negativicutes (within phylum Bacillota) which are Gram-negative. All other bacteria are considered Gram-negative. Species outside the kingdom of Bacteria will return a value \code{NA}. Functions \code{\link[=mo_is_gram_negative]{mo_is_gram_negative()}} and \code{\link[=mo_is_gram_positive]{mo_is_gram_positive()}} always return \code{TRUE} or \code{FALSE} (or \code{NA} when the input is \code{NA} or the MO code is \code{UNKNOWN}), thus always return \code{FALSE} for species outside the taxonomic kingdom of Bacteria. Determination of yeasts (\code{\link[=mo_is_yeast]{mo_is_yeast()}}) is based on the taxonomic domain and class. \emph{Budding yeasts} are yeasts that reproduce asexually through a process called budding, where a new cell develops from a small protrusion on the parent cell. Taxonomically, these are members of the phylum Ascomycota, class Saccharomycetes (also called Hemiascomycetes) or Pichiomycetes. \emph{True yeasts} quite specifically refers to yeasts in the underlying order Saccharomycetales (such as \emph{Saccharomyces cerevisiae}). Thus, for all microorganisms that are member of the taxonomic class Saccharomycetes or Pichiomycetes, the function will return \code{TRUE}. It returns \code{FALSE} otherwise (or \code{NA} when the input is \code{NA} or the MO code is \code{UNKNOWN}). diff --git a/tests/testthat/test-custom_antimicrobials.R b/tests/testthat/test-custom_antimicrobials.R index 4f3773ec5..93113f6cd 100644 --- a/tests/testthat/test-custom_antimicrobials.R +++ b/tests/testthat/test-custom_antimicrobials.R @@ -227,4 +227,10 @@ test_that("test-custom ab synonyms via add_custom_antimicrobials()", { stringsAsFactors = FALSE ))) expect_identical(as.character(as.ab("Meropenemum Somebrand")), "MEM") + + # the translated names used by as.ab() are rebuilt after adding antimicrobials + invisible(AMR:::get_ab_translated_index("de")) + suppressMessages(add_custom_antimicrobials(data.frame(ab = "TESTTRANS", name = "Testtransicillin", group = "Test"))) + expect_identical(length(AMR:::get_ab_translated_index("de")), NROW(AMR:::AMR_env$AB_lookup)) + expect_identical(AMR:::get_ab_translated_index("de")[AMR:::AMR_env$AB_lookup$ab == "TESTTRANS"], "TESTTRANSICILLIN") })