2018-08-10 15:01:05 +02:00
# ==================================================================== #
# TITLE #
2020-10-08 11:16:03 +02:00
# Antimicrobial Resistance (AMR) Analysis for R #
2018-08-10 15:01:05 +02:00
# #
2019-01-02 23:24:07 +01:00
# SOURCE #
2020-07-08 14:48:06 +02:00
# https://github.com/msberends/AMR #
2018-08-10 15:01:05 +02:00
# #
# LICENCE #
2020-01-05 17:22:09 +01:00
# (c) 2018-2020 Berends MS, Luz CF et al. #
2020-10-08 11:16:03 +02:00
# Developed at the University of Groningen, the Netherlands, in #
# collaboration with non-profit organisations Certe Medical #
# Diagnostics & Advice, and University Medical Center Groningen. #
2018-08-10 15:01:05 +02:00
# #
2019-01-02 23:24:07 +01:00
# This R package is free software; you can freely use and distribute #
# it for both personal and commercial purposes under the terms of the #
# GNU General Public License version 2.0 (GNU GPL-2), as published by #
# the Free Software Foundation. #
2020-01-05 17:22:09 +01:00
# We created this package for both routine data analysis and academic #
# research and it was publicly released in the hope that it will be #
# useful, but it comes WITHOUT ANY WARRANTY OR LIABILITY. #
2020-10-08 11:16:03 +02:00
# #
# Visit our website for the full manual and a complete tutorial about #
# how to conduct AMR analysis: https://msberends.github.io/AMR/ #
2018-08-10 15:01:05 +02:00
# ==================================================================== #
2020-09-25 14:44:50 +02:00
#' Interpret MIC and disk values, or clean raw R/SI data
2018-08-10 15:01:05 +02:00
#'
2020-08-10 11:44:58 +02:00
#' Interpret minimum inhibitory concentration (MIC) values and disk diffusion diameters according to EUCAST or CLSI, or clean up existing R/SI values. This transforms the input to a new class [`rsi`], which is an ordered factor with levels `S < I < R`. Values that cannot be interpreted will be returned as `NA` with a warning.
2020-01-05 17:22:09 +01:00
#' @inheritSection lifecycle Stable lifecycle
2018-08-25 22:01:14 +02:00
#' @rdname as.rsi
2020-02-17 14:38:01 +01:00
#' @param x vector of values (for class [`mic`]: an MIC value in mg/L, for class [`disk`]: a disk diffusion radius in millimetres)
2020-08-10 11:44:58 +02:00
#' @param mo any (vector of) text that can be coerced to a valid microorganism code with [as.mo()], will be determined automatically if the `dplyr` package is installed
2020-02-17 14:38:01 +01:00
#' @param ab any (vector of) text that can be coerced to a valid antimicrobial code with [as.ab()]
2020-10-19 17:09:19 +02:00
#' @param uti (Urinary Tract Infection) A vector with [logical]s (`TRUE` or `FALSE`) to specify whether a UTI specific interpretation from the guideline should be chosen. For using [as.rsi()] on a [data.frame], this can also be a column containing [logical]s or when left blank, the data set will be searched for a 'specimen' and rows containing 'urin' (such as 'urine', 'urina') in that column will be regarded isolates from a UTI. See *Examples*.
2019-05-10 16:44:59 +02:00
#' @inheritParams first_isolate
2020-05-16 13:05:47 +02:00
#' @param guideline defaults to the latest included EUCAST guideline, see Details for all options
2020-07-29 11:46:59 +02:00
#' @param conserve_capped_values a logical to indicate that MIC values starting with `">"` (but not `">="`) must always return "R" , and that MIC values starting with `"<"` (but not `"<="`) must always return "S"
2020-11-12 11:07:23 +01:00
#' @param add_intrinsic_resistance *(only useful when using a EUCAST guideline)* a logical to indicate whether intrinsic antibiotic resistance must also be considered for applicable bug-drug combinations, meaning that e.g. ampicillin will always return "R" in *Klebsiella* species. Determination is based on the [intrinsic_resistant] data set, that itself is based on `r format_eucast_version_nr(3.2)`.
2020-11-11 18:54:03 +01:00
#' @param reference_data a [data.frame] to be used for interpretation, which defaults to the [rsi_translation] data set. Changing this parameter allows for using own interpretation guidelines. This parameter must contain a data set that is equal in structure to the [rsi_translation] data set (same column names and column types). Please note that the `guideline` parameter will be ignored when `reference_data` is manually set.
2019-11-28 22:32:17 +01:00
#' @param threshold maximum fraction of invalid antimicrobial interpretations of `x`, please see *Examples*
2020-09-24 12:38:13 +02:00
#' @param ... for using on a [data.frame]: names of columns to apply [as.rsi()] on (supports tidy selection like `AMX:VAN`). Otherwise: parameters passed on to methods.
2020-05-16 13:05:47 +02:00
#' @details
2020-08-10 11:44:58 +02:00
#' ## How it works
2020-01-27 11:05:39 +01:00
#'
2020-08-10 11:44:58 +02:00
#' The [as.rsi()] function works in four ways:
2020-05-16 13:05:47 +02:00
#'
2020-08-10 11:44:58 +02:00
#' 1. For **cleaning raw / untransformed data**. The data will be cleaned to only contain values S, I and R and will try its best to determine this with some intelligence. For example, mixed values with R/SI interpretations and MIC values such as `"<0.25; S"` will be coerced to `"S"`. Combined interpretations for multiple test methods (as seen in laboratory records) such as `"S; S"` will be coerced to `"S"`, but a value like `"S; I"` will return `NA` with a warning that the input is unclear.
#'
#' 2. For **interpreting minimum inhibitory concentration (MIC) values** according to EUCAST or CLSI. You must clean your MIC values first using [as.mic()], that also gives your columns the new data class [`mic`]. Also, be sure to have a column with microorganism names or codes. It will be found automatically, but can be set manually using the `mo` parameter.
#' * Using `dplyr`, R/SI interpretation can be done very easily with either:
#' ```
#' your_data %>% mutate_if(is.mic, as.rsi) # until dplyr 1.0.0
#' your_data %>% mutate(across(where(is.mic), as.rsi)) # since dplyr 1.0.0
#' ```
#' * Operators like "<=" will be stripped before interpretation. When using `conserve_capped_values = TRUE`, an MIC value of e.g. ">2" will always return "R", even if the breakpoint according to the chosen guideline is ">=4". This is to prevent that capped values from raw laboratory data would not be treated conservatively. The default behaviour (`conserve_capped_values = FALSE`) considers ">2" to be lower than ">=4" and might in this case return "S" or "I".
#'
#' 3. For **interpreting disk diffusion diameters** according to EUCAST or CLSI. You must clean your disk zones first using [as.disk()], that also gives your columns the new data class [`disk`]. Also, be sure to have a column with microorganism names or codes. It will be found automatically, but can be set manually using the `mo` parameter.
#' * Using `dplyr`, R/SI interpretation can be done very easily with either:
#' ```
#' your_data %>% mutate_if(is.disk, as.rsi) # until dplyr 1.0.0
#' your_data %>% mutate(across(where(is.disk), as.rsi)) # since dplyr 1.0.0
#' ```
#'
#' 4. For **interpreting a complete data set**, with automatic determination of MIC values, disk diffusion diameters, microorganism names or codes, and antimicrobial test results. This is done very simply by running `as.rsi(data)`.
#'
#' ## Supported guidelines
#'
#' For interpreting MIC values as well as disk diffusion diameters, supported guidelines to be used as input for the `guideline` parameter are: `r paste0('"', sort(unique(AMR::rsi_translation$guideline)), '"', collapse = ", ")`.
#'
2020-11-11 18:54:03 +01:00
#' Simply using `"CLSI"` or `"EUCAST"` as input will automatically select the latest version of that guideline. You can set your own data set using the `reference_data` parameter. The `guideline` parameter will then be ignored.
2020-08-10 11:44:58 +02:00
#'
#' ## After interpretation
#'
#' After using [as.rsi()], you can use the [eucast_rules()] defined by EUCAST to (1) apply inferred susceptibility and resistance based on results of other antimicrobials and (2) apply intrinsic resistance based on taxonomic properties of a microorganism.
#'
#' ## Machine readable interpretation guidelines
2020-07-29 11:46:59 +02:00
#'
2020-07-29 10:33:47 +02:00
#' The repository of this package [contains a machine readable version](https://github.com/msberends/AMR/blob/master/data-raw/rsi_translation.txt) of all guidelines. This is a CSV file consisting of `r format(nrow(AMR::rsi_translation), big.mark = ",")` rows and `r ncol(AMR::rsi_translation)` columns. This file is machine readable, since it contains one row for every unique combination of the test method (MIC or disk diffusion), the antimicrobial agent and the microorganism. **This allows for easy implementation of these rules in laboratory information systems (LIS)**. Note that it only contains interpretation guidelines for humans - interpretation guidelines from CLSI for animals were removed.
2019-04-09 14:59:17 +02:00
#'
2020-08-10 11:44:58 +02:00
#' ## Other
2019-05-10 16:44:59 +02:00
#'
2019-11-28 22:32:17 +01:00
#' The function [is.rsi.eligible()] returns `TRUE` when a columns contains at most 5% invalid antimicrobial interpretations (not S and/or I and/or R), and `FALSE` otherwise. The threshold of 5% can be set with the `threshold` parameter.
2019-11-29 19:43:23 +01:00
#' @section Interpretation of R and S/I:
2020-10-08 11:16:03 +02:00
#' In 2019, the European Committee on Antimicrobial Susceptibility Testing (EUCAST) has decided to change the definitions of susceptibility testing categories R and S/I as shown below (<https://www.eucast.org/newsiandr/>).
2019-05-13 10:10:16 +02:00
#'
2019-11-29 19:43:23 +01:00
#' - **R = Resistant**\cr
#' A microorganism is categorised as *Resistant* when there is a high likelihood of therapeutic failure even when there is increased exposure. Exposure is a function of how the mode of administration, dose, dosing interval, infusion time, as well as distribution and excretion of the antimicrobial agent will influence the infecting organism at the site of infection.
#' - **S = Susceptible**\cr
#' A microorganism is categorised as *Susceptible, standard dosing regimen*, when there is a high likelihood of therapeutic success using a standard dosing regimen of the agent.
#' - **I = Increased exposure, but still susceptible**\cr
#' A microorganism is categorised as *Susceptible, Increased exposure* when there is a high likelihood of therapeutic success because exposure to the agent is increased by adjusting the dosing regimen or by its concentration at the site of infection.
2019-05-13 10:10:16 +02:00
#'
2019-11-28 22:32:17 +01:00
#' This AMR package honours this new insight. Use [susceptibility()] (equal to [proportion_SI()]) to determine antimicrobial susceptibility and [count_susceptible()] (equal to [count_SI()]) to count susceptible isolates.
2020-09-24 12:38:13 +02:00
#' @return Ordered [factor] with new class [`rsi`]
2019-11-28 23:00:37 +01:00
#' @aliases rsi
2018-08-10 15:01:05 +02:00
#' @export
2020-08-10 11:44:58 +02:00
#' @seealso [as.mic()], [as.disk()], [as.mo()]
2020-08-21 11:40:13 +02:00
#' @inheritSection AMR Reference data publicly available
2019-01-02 23:24:07 +01:00
#' @inheritSection AMR Read more on our website!
2018-08-25 22:01:14 +02:00
#' @examples
2020-06-22 13:18:40 +02:00
#' summary(example_isolates) # see all R/SI results at a glance
#'
2020-09-30 10:24:53 +02:00
#' if (require("skimr")) {
2020-10-21 11:50:43 +02:00
#' # class <rsi> supported in skim() too:
2020-09-30 10:24:53 +02:00
#' skim(example_isolates)
#' }
#'
2020-02-16 22:43:56 +01:00
#' # For INTERPRETING disk diffusion and MIC values -----------------------
2020-02-20 13:19:23 +01:00
#'
#' # a whole data set, even with combined MIC values and disk zones
2020-10-21 11:50:43 +02:00
#' df <- data.frame(microorganism = "Escherichia coli",
2020-02-20 13:19:23 +01:00
#' AMP = as.mic(8),
#' CIP = as.mic(0.256),
#' GEN = as.disk(18),
#' TOB = as.disk(16),
#' NIT = as.mic(32))
#' as.rsi(df)
2020-02-16 22:43:56 +01:00
#'
2020-02-20 13:19:23 +01:00
#' # for single values
2019-05-10 16:44:59 +02:00
#' as.rsi(x = as.mic(2),
#' mo = as.mo("S. pneumoniae"),
2020-02-14 19:54:13 +01:00
#' ab = "AMP",
2019-05-10 16:44:59 +02:00
#' guideline = "EUCAST")
2020-02-14 19:54:13 +01:00
#'
#' as.rsi(x = as.disk(18),
#' mo = "Strep pneu", # `mo` will be coerced with as.mo()
#' ab = "ampicillin", # and `ab` with as.ab()
2019-05-10 16:44:59 +02:00
#' guideline = "EUCAST")
2020-02-16 22:43:56 +01:00
#'
2020-09-29 23:35:46 +02:00
#' \donttest{
#' # the dplyr way
#' if (require("dplyr")) {
#' df %>% mutate_if(is.mic, as.rsi)
#' df %>% mutate_if(function(x) is.mic(x) | is.disk(x), as.rsi)
#' df %>% mutate(across(where(is.mic), as.rsi))
#' df %>% mutate_at(vars(AMP:TOB), as.rsi)
2020-09-30 10:24:53 +02:00
#' df %>% mutate(across(AMP:TOB, as.rsi))
2020-09-29 23:35:46 +02:00
#'
#' df %>%
2020-10-21 11:50:43 +02:00
#' mutate_at(vars(AMP:TOB), as.rsi, mo = .$microorganism)
2020-09-29 23:35:46 +02:00
#'
#' # to include information about urinary tract infections (UTI)
#' data.frame(mo = "E. coli",
#' NIT = c("<= 2", 32),
#' from_the_bladder = c(TRUE, FALSE)) %>%
#' as.rsi(uti = "from_the_bladder")
#'
#' data.frame(mo = "E. coli",
#' NIT = c("<= 2", 32),
#' specimen = c("urine", "blood")) %>%
#' as.rsi() # automatically determines urine isolates
#'
#' df %>%
#' mutate_at(vars(AMP:NIT), as.rsi, mo = "E. coli", uti = TRUE)
#' }
2019-05-10 16:44:59 +02:00
#'
2020-02-16 22:43:56 +01:00
#' # For CLEANING existing R/SI values ------------------------------------
#'
#' as.rsi(c("S", "I", "R", "A", "B", "C"))
#' as.rsi("<= 0.002; S") # will return "S"
2020-09-24 12:38:13 +02:00
2020-02-16 22:43:56 +01:00
#' rsi_data <- as.rsi(c(rep("S", 474), rep("I", 36), rep("R", 370)))
#' is.rsi(rsi_data)
2018-08-25 22:01:14 +02:00
#' plot(rsi_data) # for percentages
#' barplot(rsi_data) # for frequencies
#'
2020-09-29 23:35:46 +02:00
#' # the dplyr way
#' if (require("dplyr")) {
#' example_isolates %>%
#' mutate_at(vars(PEN:RIF), as.rsi)
#' # same:
#' example_isolates %>%
#' as.rsi(PEN:RIF)
#'
#' # fastest way to transform all columns with already valid AMR results to class `rsi`:
#' example_isolates %>%
#' mutate_if(is.rsi.eligible, as.rsi)
#'
#' # note: from dplyr 1.0.0 on, this will be:
#' # example_isolates %>%
2020-09-30 10:24:53 +02:00
#' # mutate(across(where(is.rsi.eligible), as.rsi))
2020-09-29 23:35:46 +02:00
#' }
2020-05-16 21:40:50 +02:00
#' }
2019-05-10 16:44:59 +02:00
as.rsi <- function ( x , ... ) {
UseMethod ( " as.rsi" )
}
2020-07-29 13:48:50 +02:00
#' @rdname as.rsi
#' @export
is.rsi <- function ( x ) {
inherits ( x , " rsi" )
}
#' @rdname as.rsi
#' @export
is.rsi.eligible <- function ( x , threshold = 0.05 ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( threshold , allow_class = " numeric" , has_length = 1 )
2020-07-29 13:48:50 +02:00
stop_if ( NCOL ( x ) > 1 , " `x` must be a one-dimensional vector." )
if ( any ( c ( " logical" ,
" numeric" ,
" integer" ,
" mo" ,
" Date" ,
" POSIXct" ,
" rsi" ,
" raw" ,
" hms" )
%in% class ( x ) ) ) {
# no transformation needed
FALSE
} else {
x <- x [ ! is.na ( x ) & ! is.null ( x ) & ! identical ( x , " " ) ]
if ( length ( x ) == 0 ) {
return ( FALSE )
}
checked <- suppressWarnings ( as.rsi ( x ) )
outcome <- sum ( is.na ( checked ) ) / length ( x )
outcome <= threshold
}
}
2019-05-10 16:44:59 +02:00
#' @export
as.rsi.default <- function ( x , ... ) {
2018-08-25 22:01:14 +02:00
if ( is.rsi ( x ) ) {
2020-11-16 16:57:55 +01:00
return ( x )
}
if ( inherits ( x , " integer" ) & all ( x %in% c ( 1 : 3 , NA ) ) ) {
2019-11-05 11:28:52 +01:00
x [x == 1 ] <- " S"
x [x == 2 ] <- " I"
x [x == 3 ] <- " R"
2020-11-16 16:57:55 +01:00
} else if ( ! all ( is.na ( x ) ) && ! identical ( levels ( x ) , c ( " S" , " I" , " R" ) ) ) {
2020-07-13 09:17:24 +02:00
2020-02-20 17:21:01 +01:00
if ( ! any ( x %like% " (R|S|I)" , na.rm = TRUE ) ) {
2020-09-24 00:30:11 +02:00
# check if they are actually MICs or disks now that the antibiotic name is valid
if ( all_valid_mics ( x ) ) {
2020-11-10 16:35:56 +01:00
warning_ ( " The input seems to be MIC values. Transform them with as.mic() before running as.rsi() to interpret them." )
2020-09-24 00:30:11 +02:00
} else if ( all_valid_disks ( x ) ) {
2020-11-10 16:35:56 +01:00
warning_ ( " The input seems to be disk diffusion values. Transform them with as.disk() before running as.rsi() to interpret them." )
2020-02-20 17:21:01 +01:00
}
}
2019-10-23 14:48:25 +02:00
2020-06-26 10:21:22 +02:00
x <- as.character ( unlist ( x ) )
2018-08-25 22:01:14 +02:00
x.bak <- x
2019-10-23 14:48:25 +02:00
2020-06-26 10:21:22 +02:00
na_before <- length ( x [is.na ( x ) | x == " " ] )
2018-08-25 22:01:14 +02:00
# remove all spaces
2019-10-11 17:21:02 +02:00
x <- gsub ( " +" , " " , x )
2018-08-25 22:01:14 +02:00
# remove all MIC-like values: numbers, operators and periods
2019-10-11 17:21:02 +02:00
x <- gsub ( " [0-9.,;:<=>]+" , " " , x )
2019-03-02 22:47:04 +01:00
# remove everything between brackets, and 'high' and 'low'
x <- gsub ( " ([(].*[)])" , " " , x )
x <- gsub ( " (high|low)" , " " , x , ignore.case = TRUE )
2018-08-25 22:01:14 +02:00
# disallow more than 3 characters
x [nchar ( x ) > 3 ] <- NA
# set to capitals
x <- toupper ( x )
# remove all invalid characters
2019-10-11 17:21:02 +02:00
x <- gsub ( " [^RSI]+" , " " , x )
2018-08-25 22:01:14 +02:00
# in cases of "S;S" keep S, but in case of "S;I" make it NA
2019-10-11 17:21:02 +02:00
x <- gsub ( " ^S+$" , " S" , x )
x <- gsub ( " ^I+$" , " I" , x )
x <- gsub ( " ^R+$" , " R" , x )
x [ ! x %in% c ( " S" , " I" , " R" ) ] <- NA
2020-06-26 10:21:22 +02:00
na_after <- length ( x [is.na ( x ) | x == " " ] )
2019-08-09 14:28:46 +02:00
if ( ! isFALSE ( list ( ... ) $ warn ) ) { # so as.rsi(..., warn = FALSE) will never throw a warning
if ( na_before != na_after ) {
2020-09-18 16:05:53 +02:00
list_missing <- x.bak [is.na ( x ) & ! is.na ( x.bak ) & x.bak != " " ] %pm>%
unique ( ) %pm>%
2019-08-09 14:28:46 +02:00
sort ( )
2019-10-11 17:21:02 +02:00
list_missing <- paste0 ( ' "' , list_missing , ' "' , collapse = " , " )
2020-11-10 16:35:56 +01:00
warning_ ( na_after - na_before , " results truncated (" ,
round ( ( ( na_after - na_before ) / length ( x ) ) * 100 ) ,
" %) that were invalid antimicrobial interpretations: " ,
list_missing , call = FALSE )
2019-08-09 14:28:46 +02:00
}
2018-08-25 22:01:14 +02:00
}
2018-08-23 00:40:36 +02:00
}
2020-11-16 16:57:55 +01:00
set_clean_class ( factor ( x , levels = c ( " S" , " I" , " R" ) , ordered = TRUE ) ,
new_class = c ( " rsi" , " ordered" , " factor" ) )
2018-08-25 22:01:14 +02:00
}
2018-08-23 00:40:36 +02:00
2019-05-10 16:44:59 +02:00
#' @rdname as.rsi
#' @export
2020-07-29 11:46:59 +02:00
as.rsi.mic <- function ( x ,
2020-08-10 11:44:58 +02:00
mo = NULL ,
2020-07-29 11:46:59 +02:00
ab = deparse ( substitute ( x ) ) ,
guideline = " EUCAST" ,
uti = FALSE ,
conserve_capped_values = FALSE ,
2020-08-16 21:38:42 +02:00
add_intrinsic_resistance = FALSE ,
2020-11-11 16:49:27 +01:00
reference_data = AMR :: rsi_translation ,
2020-07-29 11:46:59 +02:00
... ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( x )
meet_criteria ( mo , allow_class = c ( " mo" , " character" ) , allow_NULL = TRUE )
meet_criteria ( ab , allow_class = c ( " ab" , " character" ) )
meet_criteria ( guideline , allow_class = " character" , has_length = 1 )
meet_criteria ( uti , allow_class = " logical" , has_length = c ( 1 , length ( x ) ) )
meet_criteria ( conserve_capped_values , allow_class = " logical" , has_length = 1 )
meet_criteria ( add_intrinsic_resistance , allow_class = " logical" , has_length = 1 )
2020-11-11 16:49:27 +01:00
meet_criteria ( reference_data , allow_class = " data.frame" )
check_reference_data ( reference_data )
2020-08-10 11:44:58 +02:00
# for dplyr's across()
cur_column_dplyr <- import_fn ( " cur_column" , " dplyr" , error_on_fail = FALSE )
if ( ! is.null ( cur_column_dplyr ) ) {
# try to get current column, which will only be available when in across()
ab <- tryCatch ( cur_column_dplyr ( ) ,
error = function ( e ) ab )
}
# for auto-determining mo
mo_var_found <- " "
if ( is.null ( mo ) ) {
peek_mask_dplyr <- import_fn ( " peek_mask" , " dplyr" , error_on_fail = FALSE )
if ( ! is.null ( peek_mask_dplyr ) ) {
try ( {
df <- as.data.frame ( peek_mask_dplyr ( ) $ across_cols ( ) , stringsAsFactors = FALSE )
mo <- suppressMessages ( search_type_in_df ( df , " mo" ) )
if ( ! is.null ( mo ) ) {
mo_var_found <- paste0 ( " based on column `" , font_bold ( mo ) , " `" )
mo <- df [ , mo , drop = TRUE ]
}
} , silent = TRUE )
}
}
if ( is.null ( mo ) ) {
stop_ ( ' No information was supplied about the microorganisms (missing parameter "mo"). See ?as.rsi.\n\n' ,
2020-11-11 16:49:27 +01:00
" To transform certain columns with e.g. mutate_at(), use `data %>% mutate_at(vars(...), as.rsi, mo = .$x)`, where x is your column with microorganisms.\n" ,
2020-08-15 12:54:47 +02:00
" To tranform all MIC values in a data set, use `data %>% as.rsi()` or data %>% mutate_if(is.mic, as.rsi)." , call = FALSE )
2020-08-10 11:44:58 +02:00
}
2020-10-19 17:09:19 +02:00
if ( length ( ab ) == 1 && ab %like% " as.mic" ) {
stop_ ( ' No unambiguous name was supplied about the antibiotic (parameter "ab"). See ?as.rsi.' , call = FALSE )
}
2020-02-20 13:19:23 +01:00
ab_coerced <- suppressWarnings ( as.ab ( ab ) )
mo_coerced <- suppressWarnings ( as.mo ( mo ) )
2020-11-11 16:49:27 +01:00
guideline_coerced <- get_guideline ( guideline , reference_data )
2020-02-20 13:19:23 +01:00
if ( is.na ( ab_coerced ) ) {
2020-10-27 15:56:51 +01:00
message_ ( " Returning NAs for unknown drug: `" , font_bold ( ab ) ,
" `. Rename this column to a drug name or code, and check the output with as.ab()." ,
add_fn = font_red ,
as_note = FALSE )
2020-02-20 13:19:23 +01:00
return ( as.rsi ( rep ( NA , length ( x ) ) ) )
}
if ( length ( mo_coerced ) == 1 ) {
mo_coerced <- rep ( mo_coerced , length ( x ) )
}
if ( length ( uti ) == 1 ) {
uti <- rep ( uti , length ( x ) )
}
2020-10-27 15:56:51 +01:00
message_ ( " => Interpreting MIC values of `" , font_bold ( ab ) , " ` (" ,
ifelse ( ab_coerced != ab , paste0 ( ab_coerced , " , " ) , " " ) ,
ab_name ( ab_coerced , tolower = TRUE ) , " )" , mo_var_found ,
2020-11-11 18:54:03 +01:00
" according to " , ifelse ( identical ( reference_data , AMR :: rsi_translation ) ,
font_bold ( guideline_coerced ) ,
" manually defined 'reference_data'" ) ,
" ... " ,
2020-10-27 15:56:51 +01:00
appendLF = FALSE ,
as_note = FALSE )
2020-09-25 14:44:50 +02:00
2020-02-20 13:19:23 +01:00
result <- exec_as.rsi ( method = " mic" ,
x = x ,
mo = mo_coerced ,
ab = ab_coerced ,
guideline = guideline_coerced ,
2020-07-29 11:46:59 +02:00
uti = uti ,
2020-08-16 21:38:42 +02:00
conserve_capped_values = conserve_capped_values ,
2020-11-11 16:49:27 +01:00
add_intrinsic_resistance = add_intrinsic_resistance ,
reference_data = reference_data ) # exec_as.rsi will return message 'OK'
2020-02-20 13:19:23 +01:00
result
2019-05-10 16:44:59 +02:00
}
#' @rdname as.rsi
#' @export
2020-07-29 11:46:59 +02:00
as.rsi.disk <- function ( x ,
2020-08-10 11:44:58 +02:00
mo = NULL ,
2020-07-29 11:46:59 +02:00
ab = deparse ( substitute ( x ) ) ,
guideline = " EUCAST" ,
uti = FALSE ,
2020-08-16 21:38:42 +02:00
add_intrinsic_resistance = FALSE ,
2020-11-11 16:49:27 +01:00
reference_data = AMR :: rsi_translation ,
2020-07-29 11:46:59 +02:00
... ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( x )
meet_criteria ( mo , allow_class = c ( " mo" , " character" ) , allow_NULL = TRUE )
meet_criteria ( ab , allow_class = c ( " ab" , " character" ) )
meet_criteria ( guideline , allow_class = " character" , has_length = 1 )
meet_criteria ( uti , allow_class = " logical" , has_length = c ( 1 , length ( x ) ) )
meet_criteria ( add_intrinsic_resistance , allow_class = " logical" , has_length = 1 )
2020-11-11 16:49:27 +01:00
meet_criteria ( reference_data , allow_class = " data.frame" )
check_reference_data ( reference_data )
2020-08-10 11:44:58 +02:00
# for dplyr's across()
cur_column_dplyr <- import_fn ( " cur_column" , " dplyr" , error_on_fail = FALSE )
if ( ! is.null ( cur_column_dplyr ) ) {
# try to get current column, which will only be available when in across()
ab <- tryCatch ( cur_column_dplyr ( ) ,
error = function ( e ) ab )
}
# for auto-determining mo
mo_var_found <- " "
if ( is.null ( mo ) ) {
peek_mask_dplyr <- import_fn ( " peek_mask" , " dplyr" , error_on_fail = FALSE )
if ( ! is.null ( peek_mask_dplyr ) ) {
try ( {
df <- as.data.frame ( peek_mask_dplyr ( ) $ across_cols ( ) , stringsAsFactors = FALSE )
mo <- suppressMessages ( search_type_in_df ( df , " mo" ) )
if ( ! is.null ( mo ) ) {
mo_var_found <- paste0 ( " based on column `" , font_bold ( mo ) , " `" )
mo <- df [ , mo , drop = TRUE ]
}
} , silent = TRUE )
}
}
if ( is.null ( mo ) ) {
stop_ ( ' No information was supplied about the microorganisms (missing parameter "mo"). See ?as.rsi.\n\n' ,
2020-11-11 16:49:27 +01:00
" To transform certain columns with e.g. mutate_at(), use `data %>% mutate_at(vars(...), as.rsi, mo = .$x)`, where x is your column with microorganisms.\n" ,
2020-08-15 12:54:47 +02:00
" To tranform all disk diffusion zones in a data set, use `data %>% as.rsi()` or data %>% mutate_if(is.disk, as.rsi)." , call = FALSE )
2020-08-10 11:44:58 +02:00
}
2020-10-19 17:09:19 +02:00
if ( length ( ab ) == 1 && ab %like% " as.disk" ) {
stop_ ( ' No unambiguous name was supplied about the antibiotic (parameter "ab"). See ?as.rsi.' , call = FALSE )
}
2020-02-20 13:19:23 +01:00
ab_coerced <- suppressWarnings ( as.ab ( ab ) )
mo_coerced <- suppressWarnings ( as.mo ( mo ) )
2020-11-11 16:49:27 +01:00
guideline_coerced <- get_guideline ( guideline , reference_data )
2020-02-20 13:19:23 +01:00
if ( is.na ( ab_coerced ) ) {
2020-10-27 15:56:51 +01:00
message_ ( " Returning NAs for unknown drug: `" , font_bold ( ab ) ,
" `. Rename this column to a drug name or code, and check the output with as.ab()." ,
add_fn = font_red ,
as_note = FALSE )
2020-02-20 13:19:23 +01:00
return ( as.rsi ( rep ( NA , length ( x ) ) ) )
}
if ( length ( mo_coerced ) == 1 ) {
mo_coerced <- rep ( mo_coerced , length ( x ) )
}
if ( length ( uti ) == 1 ) {
uti <- rep ( uti , length ( x ) )
}
2020-10-27 15:56:51 +01:00
message_ ( " => Interpreting disk zones of `" , font_bold ( ab ) , " ` (" ,
ifelse ( ab_coerced != ab , paste0 ( ab_coerced , " , " ) , " " ) ,
2020-11-11 18:54:03 +01:00
ab_name ( ab_coerced , tolower = TRUE ) , " )" , mo_var_found ,
" according to " , ifelse ( identical ( reference_data , AMR :: rsi_translation ) ,
font_bold ( guideline_coerced ) ,
" manually defined 'reference_data'" ) ,
" ... " ,
appendLF = FALSE ,
2020-10-27 15:56:51 +01:00
as_note = FALSE )
2020-11-11 18:54:03 +01:00
2020-02-20 13:19:23 +01:00
result <- exec_as.rsi ( method = " disk" ,
x = x ,
mo = mo_coerced ,
ab = ab_coerced ,
guideline = guideline_coerced ,
2020-08-16 21:38:42 +02:00
uti = uti ,
conserve_capped_values = FALSE ,
2020-11-11 16:49:27 +01:00
add_intrinsic_resistance = add_intrinsic_resistance ,
reference_data = reference_data ) # exec_as.rsi will return message 'OK'
2020-02-20 13:19:23 +01:00
result
}
#' @rdname as.rsi
#' @export
2020-09-24 12:38:13 +02:00
as.rsi.data.frame <- function ( x ,
... ,
2020-07-29 11:46:59 +02:00
col_mo = NULL ,
guideline = " EUCAST" ,
uti = NULL ,
conserve_capped_values = FALSE ,
2020-11-11 16:49:27 +01:00
add_intrinsic_resistance = FALSE ,
reference_data = rsi_translation ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( x , allow_class = " data.frame" ) # will also check for dimensions > 0
meet_criteria ( col_mo , allow_class = " character" , is_in = colnames ( x ) , allow_NULL = TRUE )
meet_criteria ( guideline , allow_class = " character" , has_length = 1 )
2020-10-21 11:50:43 +02:00
meet_criteria ( uti , allow_class = c ( " logical" , " character" ) , allow_NULL = TRUE )
2020-10-19 17:09:19 +02:00
meet_criteria ( conserve_capped_values , allow_class = " logical" , has_length = 1 )
meet_criteria ( add_intrinsic_resistance , allow_class = " logical" , has_length = 1 )
2020-11-11 16:49:27 +01:00
meet_criteria ( reference_data , allow_class = " data.frame" )
2020-09-24 12:38:13 +02:00
2020-11-10 19:59:14 +01:00
for ( i in seq_len ( ncol ( x ) ) ) {
# don't keep factors
if ( is.factor ( x [ , i , drop = TRUE ] ) ) {
x [ , i ] <- as.character ( x [ , i , drop = TRUE ] )
}
}
2020-10-20 21:00:57 +02:00
# -- MO
col_mo.bak <- col_mo
if ( is.null ( col_mo ) ) {
col_mo <- search_type_in_df ( x = x , type = " mo" , info = FALSE )
}
2020-02-20 13:19:23 +01:00
# -- UTIs
col_uti <- uti
if ( is.null ( col_uti ) ) {
col_uti <- search_type_in_df ( x = x , type = " uti" )
}
if ( ! is.null ( col_uti ) ) {
if ( is.logical ( col_uti ) ) {
# already a logical vector as input
if ( length ( col_uti ) == 1 ) {
uti <- rep ( col_uti , NROW ( x ) )
} else {
uti <- col_uti
}
} else {
# column found, transform to logical
2020-10-21 11:50:43 +02:00
stop_if ( length ( col_uti ) != 1 | ! col_uti %in% colnames ( x ) ,
" argument `uti` must be a logical vector, of must be a single column name of `x`" )
2020-02-20 13:19:23 +01:00
uti <- as.logical ( x [ , col_uti , drop = TRUE ] )
}
} else {
# look for specimen column and make logicals of the urines
col_specimen <- suppressMessages ( search_type_in_df ( x = x , type = " specimen" ) )
if ( ! is.null ( col_specimen ) ) {
uti <- x [ , col_specimen , drop = TRUE ] %like% " urin"
values <- sort ( unique ( x [uti , col_specimen , drop = TRUE ] ) )
if ( length ( values ) > 1 ) {
plural <- c ( " s" , " " , " " )
} else {
plural <- c ( " " , " s" , " a " )
}
2020-10-27 15:56:51 +01:00
message_ ( " Assuming value" , plural [1 ] , " " ,
paste ( paste0 ( ' "' , values , ' "' ) , collapse = " , " ) ,
" in column `" , font_bold ( col_specimen ) ,
" ` reflect" , plural [2 ] , " " , plural [3 ] , " urinary tract infection" , plural [1 ] ,
" .\n Use `as.rsi(uti = FALSE)` to prevent this." )
2020-02-20 13:19:23 +01:00
} else {
# no data about UTI's found
uti <- FALSE
}
}
2020-07-13 09:17:24 +02:00
2020-02-20 13:19:23 +01:00
i <- 0
2020-09-24 12:38:13 +02:00
sel <- colnames ( pm_select ( x , ... ) )
2020-10-20 21:00:57 +02:00
if ( ! is.null ( col_mo ) ) {
sel <- sel [sel != col_mo ]
}
2020-02-20 20:31:42 +01:00
ab_cols <- colnames ( x ) [sapply ( x , function ( y ) {
2020-02-20 13:19:23 +01:00
i <<- i + 1
check <- is.mic ( y ) | is.disk ( y )
ab <- colnames ( x ) [i ]
2020-10-20 21:00:57 +02:00
if ( ! is.null ( col_mo ) && ab == col_mo ) {
return ( FALSE )
}
if ( ! is.null ( col_uti ) && ab == col_uti ) {
return ( FALSE )
}
2020-09-24 12:38:13 +02:00
if ( length ( sel ) == 0 || ( length ( sel ) > 0 && ab %in% sel ) ) {
ab_coerced <- suppressWarnings ( as.ab ( ab ) )
2020-09-25 14:44:50 +02:00
if ( is.na ( ab_coerced ) || ( length ( sel ) > 0 & ! ab %in% sel ) ) {
2020-09-24 12:38:13 +02:00
# not even a valid AB code
return ( FALSE )
} else {
return ( TRUE )
}
2020-02-20 13:19:23 +01:00
} else {
2020-09-24 12:38:13 +02:00
return ( FALSE )
2020-02-20 13:19:23 +01:00
}
} ) ]
2020-06-22 11:18:40 +02:00
stop_if ( length ( ab_cols ) == 0 ,
2020-09-24 12:38:13 +02:00
" no columns with MIC values, disk zones or antibiotic column names found in this data set. Use as.mic() or as.disk() to transform antimicrobial columns." )
2020-02-20 13:19:23 +01:00
# set type per column
types <- character ( length ( ab_cols ) )
types [sapply ( x [ , ab_cols ] , is.disk ) ] <- " disk"
types [types == " " & sapply ( x [ , ab_cols ] , all_valid_disks ) ] <- " disk"
2020-10-20 21:00:57 +02:00
types [sapply ( x [ , ab_cols ] , is.mic ) ] <- " mic"
types [types == " " & sapply ( x [ , ab_cols ] , all_valid_mics ) ] <- " mic"
2020-09-24 12:38:13 +02:00
types [types == " " & ! sapply ( x [ , ab_cols ] , is.rsi ) ] <- " rsi"
2020-02-20 13:19:23 +01:00
2020-09-29 23:35:46 +02:00
if ( any ( types %in% c ( " mic" , " disk" ) , na.rm = TRUE ) ) {
2020-10-20 21:00:57 +02:00
# now we need an mo column
stop_if ( is.null ( col_mo ) , " `col_mo` must be set" )
# if not null, we already found it, now find again so a message will show
if ( is.null ( col_mo.bak ) ) {
2020-09-29 23:35:46 +02:00
col_mo <- search_type_in_df ( x = x , type = " mo" )
}
}
2020-10-20 21:00:57 +02:00
x_mo <- as.mo ( x %pm>% pm_pull ( col_mo ) )
2020-02-20 13:19:23 +01:00
for ( i in seq_len ( length ( ab_cols ) ) ) {
if ( types [i ] == " mic" ) {
2020-11-11 16:49:27 +01:00
x [ , ab_cols [i ] ] <- as.rsi ( x = x %pm>%
pm_pull ( ab_cols [i ] ) %pm>%
as.character ( ) %pm>%
as.mic ( ) ,
mo = x_mo ,
ab = ab_cols [i ] ,
guideline = guideline ,
uti = uti ,
conserve_capped_values = conserve_capped_values ,
add_intrinsic_resistance = add_intrinsic_resistance ,
reference_data = reference_data )
2020-02-20 13:19:23 +01:00
} else if ( types [i ] == " disk" ) {
2020-11-11 16:49:27 +01:00
x [ , ab_cols [i ] ] <- as.rsi ( x = x %pm>%
pm_pull ( ab_cols [i ] ) %pm>%
as.character ( ) %pm>%
as.disk ( ) ,
mo = x_mo ,
ab = ab_cols [i ] ,
guideline = guideline ,
uti = uti ,
add_intrinsic_resistance = add_intrinsic_resistance ,
reference_data = reference_data )
2020-09-24 12:38:13 +02:00
} else if ( types [i ] == " rsi" ) {
2020-10-20 21:00:57 +02:00
ab <- ab_cols [i ]
ab_coerced <- suppressWarnings ( as.ab ( ab ) )
2020-10-27 15:56:51 +01:00
message_ ( " => Cleaning values in column `" , font_bold ( ab ) , " ` (" ,
ifelse ( ab_coerced != ab , paste0 ( ab_coerced , " , " ) , " " ) ,
ab_name ( ab_coerced , tolower = TRUE ) , " )... " ,
appendLF = FALSE ,
as_note = FALSE )
2020-09-24 12:38:13 +02:00
x [ , ab_cols [i ] ] <- as.rsi.default ( x = x %pm>% pm_pull ( ab_cols [i ] ) )
2020-11-11 16:49:27 +01:00
message_ ( " OK." , add_fn = list ( font_green , font_bold ) , as_note = FALSE )
2020-02-20 13:19:23 +01:00
}
}
x
2019-05-10 16:44:59 +02:00
}
2020-11-11 16:49:27 +01:00
get_guideline <- function ( guideline , reference_data ) {
2020-11-11 18:54:03 +01:00
if ( ! identical ( reference_data , AMR :: rsi_translation ) ) {
return ( guideline )
}
2019-05-13 10:10:16 +02:00
guideline_param <- toupper ( guideline )
if ( guideline_param %in% c ( " CLSI" , " EUCAST" ) ) {
2020-11-11 16:49:27 +01:00
guideline_param <- rev ( sort ( subset ( reference_data , guideline %like% guideline_param ) $ guideline ) ) [1L ]
2019-05-10 16:44:59 +02:00
}
2020-02-21 13:13:34 +01:00
if ( ! guideline_param %like% " " ) {
# like 'EUCAST2020', should be 'EUCAST 2020'
guideline_param <- gsub ( " ([a-z]+)([0-9]+)" , " \\1 \\2" , guideline_param , ignore.case = TRUE )
}
2019-10-23 14:48:25 +02:00
2020-11-11 16:49:27 +01:00
stop_ifnot ( guideline_param %in% reference_data $ guideline ,
2020-06-22 11:18:40 +02:00
" invalid guideline: '" , guideline ,
2020-11-11 16:49:27 +01:00
" '.\nValid guidelines are: " , paste0 ( " '" , unique ( reference_data $ guideline ) , " '" , collapse = " , " ) , call = FALSE )
2019-10-23 14:48:25 +02:00
guideline_param
2020-02-20 13:19:23 +01:00
2019-10-23 14:48:25 +02:00
}
2020-08-16 21:38:42 +02:00
exec_as.rsi <- function ( method ,
x ,
mo ,
ab ,
guideline ,
uti ,
conserve_capped_values ,
2020-11-11 18:54:03 +01:00
add_intrinsic_resistance ,
reference_data ) {
2020-08-16 21:38:42 +02:00
2020-09-18 16:05:53 +02:00
metadata_mo <- get_mo_failures_uncertainties_renamed ( )
2020-11-11 16:49:27 +01:00
x_bak <- data.frame ( x_mo = paste0 ( x , mo ) , stringsAsFactors = FALSE )
2020-08-15 12:54:47 +02:00
df <- unique ( data.frame ( x , mo ) , stringsAsFactors = FALSE )
x <- df $ x
mo <- df $ mo
2019-10-23 14:48:25 +02:00
if ( method == " mic" ) {
2020-02-20 13:19:23 +01:00
x <- as.mic ( x ) # when as.rsi.mic is called directly
2019-10-23 14:48:25 +02:00
} else if ( method == " disk" ) {
2020-02-20 13:19:23 +01:00
x <- as.disk ( x ) # when as.rsi.disk is called directly
2019-10-23 14:48:25 +02:00
}
2020-02-20 13:19:23 +01:00
warned <- FALSE
method_param <- toupper ( method )
2019-10-23 14:48:25 +02:00
2020-09-25 14:44:50 +02:00
genera <- mo_genus ( mo )
mo_genus <- as.mo ( genera )
2019-10-23 14:48:25 +02:00
mo_family <- as.mo ( mo_family ( mo ) )
mo_order <- as.mo ( mo_order ( mo ) )
2020-09-25 14:44:50 +02:00
if ( any ( genera == " Staphylococcus" , na.rm = TRUE ) ) {
mo_becker <- as.mo ( mo , Becker = TRUE )
} else {
mo_becker <- mo
}
if ( any ( genera == " Streptococcus" , na.rm = TRUE ) ) {
mo_lancefield <- as.mo ( mo , Lancefield = TRUE )
} else {
mo_lancefield <- mo
}
2020-04-29 14:33:44 +02:00
mo_other <- as.mo ( rep ( " UNKNOWN" , length ( mo ) ) )
2019-10-23 14:48:25 +02:00
2020-11-11 16:49:27 +01:00
guideline_coerced <- get_guideline ( guideline , reference_data )
2019-10-23 14:48:25 +02:00
if ( guideline_coerced != guideline ) {
2020-10-27 15:56:51 +01:00
message_ ( " Using guideline " , font_bold ( guideline_coerced ) , " as input for `guideline`." )
2019-10-23 14:48:25 +02:00
}
2020-02-17 14:38:01 +01:00
2019-05-10 16:44:59 +02:00
new_rsi <- rep ( NA_character_ , length ( x ) )
2020-02-20 13:19:23 +01:00
ab_param <- ab
2020-11-11 18:54:03 +01:00
if ( identical ( reference_data , AMR :: rsi_translation ) ) {
trans <- reference_data %pm>%
subset ( guideline == guideline_coerced & method == method_param & ab == ab_param )
} else {
trans <- reference_data %pm>%
subset ( method == method_param & ab == ab_param )
}
2020-05-16 13:05:47 +02:00
trans $ lookup <- paste ( trans $ mo , trans $ ab )
2020-02-17 14:38:01 +01:00
2019-05-10 16:44:59 +02:00
lookup_mo <- paste ( mo , ab )
lookup_genus <- paste ( mo_genus , ab )
lookup_family <- paste ( mo_family , ab )
lookup_order <- paste ( mo_order , ab )
lookup_becker <- paste ( mo_becker , ab )
lookup_lancefield <- paste ( mo_lancefield , ab )
2020-02-14 19:54:13 +01:00
lookup_other <- paste ( mo_other , ab )
2019-10-23 14:48:25 +02:00
2020-02-20 13:19:23 +01:00
if ( all ( trans $ uti == TRUE , na.rm = TRUE ) & all ( uti == FALSE ) ) {
2020-10-27 15:56:51 +01:00
message_ ( " WARNING." , add_fn = list ( font_red , font_bold ) , as_note = FALSE )
2020-11-11 16:49:27 +01:00
warning_ ( " Interpretation of " , font_bold ( ab_name ( ab , tolower = TRUE ) ) , " for some microorganisms is only available for (uncomplicated) urinary tract infections (UTI). Use parameter 'uti' to set which isolates are from urine. See ?as.rsi." , call = FALSE )
2020-02-20 13:19:23 +01:00
warned <- TRUE
}
2020-08-16 21:38:42 +02:00
2019-10-11 17:21:02 +02:00
for ( i in seq_len ( length ( x ) ) ) {
2020-08-16 21:38:42 +02:00
if ( isTRUE ( add_intrinsic_resistance ) ) {
if ( ! guideline_coerced %like% " EUCAST" ) {
2020-11-10 16:35:56 +01:00
warning_ ( " Using 'add_intrinsic_resistance' is only useful when using EUCAST guidelines, since the rules for intrinsic resistance are based on EUCAST." , call = FALSE )
2020-08-16 21:38:42 +02:00
} else {
get_record <- subset ( intrinsic_resistant ,
microorganism == mo_name ( mo [i ] , language = NULL ) & antibiotic == ab_name ( ab , language = NULL ) )
if ( nrow ( get_record ) > 0 ) {
new_rsi [i ] <- " R"
next
}
}
}
2020-09-18 16:05:53 +02:00
get_record <- trans %pm>%
2020-08-16 21:38:42 +02:00
# no subsetting to UTI for now
2020-05-16 13:05:47 +02:00
subset ( lookup %in% c ( lookup_mo [i ] ,
2019-05-10 16:44:59 +02:00
lookup_genus [i ] ,
lookup_family [i ] ,
lookup_order [i ] ,
lookup_becker [i ] ,
2020-02-14 19:54:13 +01:00
lookup_lancefield [i ] ,
2020-02-20 13:19:23 +01:00
lookup_other [i ] ) )
if ( isTRUE ( uti [i ] ) ) {
2020-09-18 16:05:53 +02:00
get_record <- get_record %pm>%
2020-02-20 13:19:23 +01:00
# be as specific as possible (i.e. prefer species over genus):
2020-09-18 16:05:53 +02:00
# pm_desc(uti) = TRUE on top and FALSE on bottom
pm_arrange ( pm_desc ( uti ) , pm_desc ( nchar ( mo ) ) ) # 'uti' is a column in data set 'rsi_translation'
2020-02-20 13:19:23 +01:00
} else {
2020-09-18 16:05:53 +02:00
get_record <- get_record %pm>%
pm_filter ( uti == FALSE ) %pm>% # 'uti' is a column in rsi_translation
pm_arrange ( pm_desc ( nchar ( mo ) ) )
2020-02-20 13:19:23 +01:00
}
2020-09-29 10:40:25 +02:00
get_record <- get_record [1L , , drop = FALSE ]
2019-10-23 14:48:25 +02:00
2019-05-10 16:44:59 +02:00
if ( NROW ( get_record ) > 0 ) {
2019-10-23 14:48:25 +02:00
if ( is.na ( x [i ] ) ) {
new_rsi [i ] <- NA_character_
} else if ( method == " mic" ) {
2020-02-20 13:19:23 +01:00
mic_input <- x [i ]
mic_S <- as.mic ( get_record $ breakpoint_S )
mic_R <- as.mic ( get_record $ breakpoint_R )
2020-09-18 16:05:53 +02:00
new_rsi [i ] <- quick_case_when ( isTRUE ( conserve_capped_values ) & mic_input %like% " ^<[0-9]" ~ " S" ,
isTRUE ( conserve_capped_values ) & mic_input %like% " ^>[0-9]" ~ " R" ,
# start interpreting: EUCAST uses <= S and > R, CLSI uses <=S and >= R
isTRUE ( which ( levels ( mic_input ) == mic_input ) <= which ( levels ( mic_S ) == mic_S ) ) ~ " S" ,
2020-09-29 10:40:25 +02:00
guideline_coerced %like% " EUCAST" &
2020-09-18 16:05:53 +02:00
isTRUE ( which ( levels ( mic_input ) == mic_input ) > which ( levels ( mic_R ) == mic_R ) ) ~ " R" ,
guideline_coerced %like% " CLSI" &
isTRUE ( which ( levels ( mic_input ) == mic_input ) >= which ( levels ( mic_R ) == mic_R ) ) ~ " R" ,
# return "I" when not match the bottom or top
! is.na ( get_record $ breakpoint_S ) & ! is.na ( get_record $ breakpoint_R ) ~ " I" ,
# and NA otherwise
TRUE ~ NA_character_ )
2019-05-10 16:44:59 +02:00
} else if ( method == " disk" ) {
2020-09-18 16:05:53 +02:00
new_rsi [i ] <- quick_case_when ( isTRUE ( as.double ( x [i ] ) >= as.double ( get_record $ breakpoint_S ) ) ~ " S" ,
# start interpreting: EUCAST uses >= S and < R, CLSI uses >=S and <= R
2020-09-29 10:40:25 +02:00
guideline_coerced %like% " EUCAST" &
2020-09-18 16:05:53 +02:00
isTRUE ( as.double ( x [i ] ) < as.double ( get_record $ breakpoint_R ) ) ~ " R" ,
guideline_coerced %like% " CLSI" &
isTRUE ( as.double ( x [i ] ) <= as.double ( get_record $ breakpoint_R ) ) ~ " R" ,
# return "I" when not match the bottom or top
! is.na ( get_record $ breakpoint_S ) & ! is.na ( get_record $ breakpoint_R ) ~ " I" ,
# and NA otherwise
TRUE ~ NA_character_ )
2019-05-10 16:44:59 +02:00
}
}
}
2020-08-15 12:54:47 +02:00
2020-09-18 16:05:53 +02:00
new_rsi <- x_bak %pm>%
2020-11-11 16:49:27 +01:00
pm_left_join ( data.frame ( x_mo = paste0 ( df $ x , df $ mo ) , new_rsi ,
stringsAsFactors = FALSE ) ,
by = " x_mo" ) %pm>%
2020-09-18 16:05:53 +02:00
pm_pull ( new_rsi )
2020-08-15 12:54:47 +02:00
2020-02-20 13:19:23 +01:00
if ( warned == FALSE ) {
2020-11-11 16:49:27 +01:00
message_ ( " OK." , add_fn = list ( font_green , font_bold ) , as_note = FALSE )
2020-02-20 13:19:23 +01:00
}
2020-08-15 12:54:47 +02:00
2020-09-18 16:05:53 +02:00
load_mo_failures_uncertainties_renamed ( metadata_mo )
2020-11-16 16:57:55 +01:00
set_clean_class ( factor ( new_rsi , levels = c ( " S" , " I" , " R" ) , ordered = TRUE ) ,
new_class = c ( " rsi" , " ordered" , " factor" ) )
2019-05-10 16:44:59 +02:00
}
2020-08-28 21:55:47 +02:00
# will be exported using s3_register() in R/zzz.R
2020-08-26 11:33:54 +02:00
pillar_shaft.rsi <- function ( x , ... ) {
out <- trimws ( format ( x ) )
out [is.na ( x ) ] <- font_grey ( " NA" )
2020-08-28 21:55:47 +02:00
out [x == " S" ] <- font_green_bg ( font_white ( " S " ) )
out [x == " I" ] <- font_yellow_bg ( font_black ( " I " ) )
out [x == " R" ] <- font_red_bg ( font_white ( " R " ) )
create_pillar_column ( out , align = " left" , width = 5 )
2020-08-26 11:33:54 +02:00
}
2020-08-28 21:55:47 +02:00
# will be exported using s3_register() in R/zzz.R
2020-08-26 11:33:54 +02:00
type_sum.rsi <- function ( x , ... ) {
" rsi"
}
2020-08-28 21:55:47 +02:00
# will be exported using s3_register() in R/zzz.R
freq.rsi <- function ( x , ... ) {
x_name <- deparse ( substitute ( x ) )
x_name <- gsub ( " .*[$]" , " " , x_name )
2020-10-19 17:09:19 +02:00
if ( x_name %in% c ( " x" , " ." ) ) {
# try again going through system calls
2020-10-20 21:00:57 +02:00
x_name <- stats :: na.omit ( sapply ( sys.calls ( ) ,
function ( call ) {
call_txt <- as.character ( call )
ifelse ( call_txt [1 ] %like% " freq$" , call_txt [length ( call_txt ) ] , character ( 0 ) )
} ) ) [1L ]
2020-10-19 17:09:19 +02:00
}
2020-08-28 21:55:47 +02:00
ab <- suppressMessages ( suppressWarnings ( as.ab ( x_name ) ) )
freq.default <- import_fn ( " freq.default" , " cleaner" , error_on_fail = FALSE )
digits <- list ( ... ) $ digits
if ( is.null ( digits ) ) {
digits <- 2
}
if ( ! is.na ( ab ) ) {
freq.default ( x = x , ... ,
.add_header = list ( Drug = paste0 ( ab_name ( ab , language = NULL ) , " (" , ab , " , " , ab_atc ( ab ) , " )" ) ,
`Drug group` = ab_group ( ab , language = NULL ) ,
`%SI` = percentage ( susceptibility ( x , minimum = 0 , as_percent = FALSE ) , digits = digits ) ) )
} else {
freq.default ( x = x , ... ,
.add_header = list ( `%SI` = percentage ( susceptibility ( x , minimum = 0 , as_percent = FALSE ) , digits = digits ) ) )
}
}
2020-09-28 01:08:55 +02:00
# will be exported using s3_register() in R/zzz.R
get_skimmers.rsi <- function ( column ) {
2020-09-28 11:00:59 +02:00
# get the variable name 'skim_variable'
name_call <- function ( .data ) {
2020-09-28 01:08:55 +02:00
calls <- sys.calls ( )
2020-09-28 11:00:59 +02:00
calls_txt <- vapply ( calls , function ( x ) paste ( deparse ( x ) , collapse = " " ) , FUN.VALUE = character ( 1 ) )
if ( any ( calls_txt %like% " skim_variable" , na.rm = TRUE ) ) {
ind <- which ( calls_txt %like% " skim_variable" ) [1L ]
vars <- tryCatch ( eval ( parse ( text = " .data$skim_variable" ) , envir = sys.frame ( ind ) ) ,
error = function ( e ) NULL )
} else {
vars <- NULL
}
2020-09-28 01:08:55 +02:00
i <- tryCatch ( attributes ( calls [ [length ( calls ) ] ] ) $ position ,
error = function ( e ) NULL )
if ( is.null ( vars ) | is.null ( i ) ) {
NA_character_
2020-09-28 11:00:59 +02:00
} else {
2020-09-28 01:08:55 +02:00
lengths <- sapply ( vars , length )
2020-09-28 11:00:59 +02:00
when_starts_rsi <- which ( names ( sapply ( vars , length ) ) == " rsi" )
offset <- sum ( lengths [c ( 1 : when_starts_rsi - 1 ) ] )
var <- vars $ rsi [i - offset ]
if ( ! isFALSE ( var == " data" ) ) {
2020-09-28 01:08:55 +02:00
NA_character_
} else {
ab_name ( var )
}
}
}
sfl <- import_fn ( " sfl" , " skimr" , error_on_fail = FALSE )
sfl (
skim_type = " rsi" ,
2020-09-28 11:00:59 +02:00
ab_name = name_call ,
2020-09-28 01:08:55 +02:00
count_R = count_R ,
count_S = count_susceptible ,
count_I = count_I ,
prop_R = ~ proportion_R ( ., minimum = 0 ) ,
prop_S = ~ susceptibility ( ., minimum = 0 ) ,
prop_I = ~ proportion_I ( ., minimum = 0 )
)
}
2020-05-28 16:48:55 +02:00
#' @method print rsi
2018-08-25 22:01:14 +02:00
#' @export
#' @noRd
print.rsi <- function ( x , ... ) {
2020-05-27 16:37:49 +02:00
cat ( " Class <rsi>\n" )
2018-08-25 22:01:14 +02:00
print ( as.character ( x ) , quote = FALSE )
}
2020-05-28 16:48:55 +02:00
#' @method droplevels rsi
2018-12-29 22:24:19 +01:00
#' @export
#' @noRd
2019-10-11 17:21:02 +02:00
droplevels.rsi <- function ( x , exclude = if ( anyNA ( levels ( x ) ) ) NULL else NA , ... ) {
2018-12-29 22:24:19 +01:00
x <- droplevels.factor ( x , exclude = exclude , ... )
2019-10-11 17:21:02 +02:00
class ( x ) <- c ( " rsi" , " ordered" , " factor" )
2018-12-29 22:24:19 +01:00
x
}
2020-05-28 16:48:55 +02:00
#' @method summary rsi
2018-08-25 22:01:14 +02:00
#' @export
#' @noRd
summary.rsi <- function ( object , ... ) {
x <- object
2020-06-22 11:18:40 +02:00
n <- sum ( ! is.na ( x ) )
S <- sum ( x == " S" , na.rm = TRUE )
I <- sum ( x == " I" , na.rm = TRUE )
R <- sum ( x == " R" , na.rm = TRUE )
2020-08-10 11:44:58 +02:00
pad <- function ( x ) {
if ( x == " 0%" ) {
x <- " 0.0%"
}
if ( nchar ( x ) < 5 ) {
x <- paste0 ( rep ( " " , 5 - nchar ( x ) ) , x )
}
x
}
2020-07-22 10:24:23 +02:00
value <- c (
2019-10-11 17:21:02 +02:00
" Class" = " rsi" ,
2020-08-10 11:44:58 +02:00
" %R" = paste0 ( pad ( percentage ( R / n , digits = 1 ) ) , " (n=" , R , " )" ) ,
" %SI" = paste0 ( pad ( percentage ( ( S + I ) / n , digits = 1 ) ) , " (n=" , S + I , " )" ) ,
" - %S" = paste0 ( pad ( percentage ( S / n , digits = 1 ) ) , " (n=" , S , " )" ) ,
" - %I" = paste0 ( pad ( percentage ( I / n , digits = 1 ) ) , " (n=" , I , " )" )
2018-08-10 15:01:05 +02:00
)
2020-07-22 10:24:23 +02:00
class ( value ) <- c ( " summaryDefault" , " table" )
value
2018-08-25 22:01:14 +02:00
}
2018-08-10 15:01:05 +02:00
2020-05-28 16:48:55 +02:00
#' @method plot rsi
2018-08-25 22:01:14 +02:00
#' @export
2020-09-25 14:44:50 +02:00
#' @importFrom graphics plot text axis
2020-09-12 08:49:01 +02:00
#' @rdname plot
2019-06-16 21:42:40 +02:00
plot.rsi <- function ( x ,
lwd = 2 ,
ylim = NULL ,
2019-10-11 17:21:02 +02:00
ylab = " Percentage" ,
xlab = " Antimicrobial Interpretation" ,
2020-05-28 10:51:56 +02:00
main = paste ( " Resistance Overview of" , deparse ( substitute ( x ) ) ) ,
2019-06-16 21:42:40 +02:00
axes = FALSE ,
... ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( lwd , allow_class = c ( " numeric" , " integer" ) , has_length = 1 )
meet_criteria ( ylim , allow_class = c ( " numeric" , " integer" ) , allow_NULL = TRUE )
meet_criteria ( ylab , allow_class = " character" , has_length = 1 )
meet_criteria ( xlab , allow_class = " character" , has_length = 1 )
meet_criteria ( main , allow_class = " character" , has_length = 1 )
meet_criteria ( axes , allow_class = " logical" , has_length = 1 )
2020-05-28 10:51:56 +02:00
data <- as.data.frame ( table ( x ) , stringsAsFactors = FALSE )
colnames ( data ) <- c ( " x" , " n" )
data $ s <- round ( ( data $ n / sum ( data $ n ) ) * 100 , 1 )
2020-07-13 09:17:24 +02:00
2019-05-10 16:44:59 +02:00
if ( ! " S" %in% data $ x ) {
2020-11-11 16:49:27 +01:00
data <- rbind ( data , data.frame ( x = " S" , n = 0 , s = 0 , stringsAsFactors = FALSE ) ,
stringsAsFactors = FALSE )
2019-05-10 16:44:59 +02:00
}
if ( ! " I" %in% data $ x ) {
2020-11-11 16:49:27 +01:00
data <- rbind ( data , data.frame ( x = " I" , n = 0 , s = 0 , stringsAsFactors = FALSE ) ,
stringsAsFactors = FALSE )
2019-05-10 16:44:59 +02:00
}
if ( ! " R" %in% data $ x ) {
2020-11-11 16:49:27 +01:00
data <- rbind ( data , data.frame ( x = " R" , n = 0 , s = 0 , stringsAsFactors = FALSE ) ,
stringsAsFactors = FALSE )
2019-05-10 16:44:59 +02:00
}
2019-10-23 14:48:25 +02:00
2020-05-28 10:51:56 +02:00
# don't use as.rsi() here, it will confuse plot()
2019-10-11 17:21:02 +02:00
data $ x <- factor ( data $ x , levels = c ( " S" , " I" , " R" ) , ordered = TRUE )
2019-10-23 14:48:25 +02:00
2020-09-18 16:05:53 +02:00
ymax <- pm_if_else ( max ( data $ s ) > 95 , 105 , 100 )
2019-10-23 14:48:25 +02:00
2018-08-25 22:01:14 +02:00
plot ( x = data $ x ,
y = data $ s ,
2019-06-16 21:42:40 +02:00
lwd = lwd ,
2018-08-25 22:01:14 +02:00
ylim = c ( 0 , ymax ) ,
2019-06-16 21:42:40 +02:00
ylab = ylab ,
xlab = xlab ,
main = main ,
axes = axes ,
2018-08-25 22:01:14 +02:00
... )
# x axis
2020-09-18 16:05:53 +02:00
axis ( side = 1 , at = 1 : pm_n_distinct ( data $ x ) , labels = levels ( data $ x ) , lwd = 0 )
2018-08-25 22:01:14 +02:00
# y axis, 0-100%
axis ( side = 2 , at = seq ( 0 , 100 , 5 ) )
2019-10-23 14:48:25 +02:00
2018-08-25 22:01:14 +02:00
text ( x = data $ x ,
y = data $ s + 4 ,
2019-10-11 17:21:02 +02:00
labels = paste0 ( data $ s , " % (n = " , data $ n , " )" ) )
2018-08-25 22:01:14 +02:00
}
2020-05-28 16:48:55 +02:00
#' @method barplot rsi
2018-08-25 22:01:14 +02:00
#' @export
2019-06-16 22:14:43 +02:00
#' @importFrom graphics barplot axis par
2020-09-12 08:49:01 +02:00
#' @rdname plot
2019-06-16 21:42:40 +02:00
barplot.rsi <- function ( height ,
2020-02-20 17:21:01 +01:00
col = c ( " chartreuse4" , " chartreuse3" , " brown3" ) ,
2019-10-11 17:21:02 +02:00
xlab = ifelse ( beside , " Antimicrobial Interpretation" , " " ) ,
2020-05-28 10:51:56 +02:00
main = paste ( " Resistance Overview of" , deparse ( substitute ( height ) ) ) ,
2019-10-11 17:21:02 +02:00
ylab = " Frequency" ,
2019-06-16 21:42:40 +02:00
beside = TRUE ,
axes = beside ,
... ) {
2020-10-19 17:09:19 +02:00
meet_criteria ( col , allow_class = " character" , has_length = 3 )
meet_criteria ( xlab , allow_class = " character" , has_length = 1 )
meet_criteria ( main , allow_class = " character" , has_length = 1 )
meet_criteria ( ylab , allow_class = " character" , has_length = 1 )
meet_criteria ( beside , allow_class = " logical" , has_length = 1 )
meet_criteria ( axes , allow_class = " logical" , has_length = 1 )
2019-10-23 14:48:25 +02:00
2019-06-16 21:42:40 +02:00
if ( axes == TRUE ) {
par ( mar = c ( 5 , 4 , 4 , 2 ) + 0.1 )
} else {
par ( mar = c ( 2 , 4 , 4 , 2 ) + 0.1 )
}
2019-10-23 14:48:25 +02:00
2019-06-16 21:42:40 +02:00
barplot ( as.matrix ( table ( height ) ) ,
col = col ,
xlab = xlab ,
main = main ,
ylab = ylab ,
beside = beside ,
2018-08-25 22:01:14 +02:00
axes = FALSE ,
... )
# y axis, 0-100%
2019-06-16 21:42:40 +02:00
axis ( side = 2 , at = seq ( 0 , max ( table ( height ) ) + max ( table ( height ) ) * 1.1 , by = 25 ) )
if ( axes == TRUE && beside == TRUE ) {
axis ( side = 1 , labels = levels ( height ) , at = c ( 1 , 2 , 3 ) + 0.5 , lwd = 0 )
}
2018-08-10 15:01:05 +02:00
}
2019-08-07 15:37:39 +02:00
2020-05-28 16:48:55 +02:00
#' @method [<- rsi
2020-04-13 21:09:56 +02:00
#' @export
#' @noRd
" [<-.rsi" <- function ( i , j , ... , value ) {
value <- as.rsi ( value )
y <- NextMethod ( )
attributes ( y ) <- attributes ( i )
y
}
2020-05-28 16:48:55 +02:00
#' @method [[<- rsi
2020-04-13 21:09:56 +02:00
#' @export
#' @noRd
" [[<-.rsi" <- function ( i , j , ... , value ) {
value <- as.rsi ( value )
y <- NextMethod ( )
attributes ( y ) <- attributes ( i )
y
}
2020-05-28 16:48:55 +02:00
#' @method c rsi
2020-04-13 21:09:56 +02:00
#' @export
#' @noRd
c.rsi <- function ( x , ... ) {
y <- unlist ( lapply ( list ( ... ) , as.character ) )
x <- as.character ( x )
as.rsi ( c ( x , y ) )
}
2020-09-25 14:44:50 +02:00
#' @method unique rsi
#' @export
#' @noRd
unique.rsi <- function ( x , incomparables = FALSE , ... ) {
y <- NextMethod ( )
attributes ( y ) <- attributes ( x )
y
}
2020-11-11 16:49:27 +01:00
check_reference_data <- function ( reference_data ) {
if ( ! identical ( reference_data , AMR :: rsi_translation ) ) {
class_rsi <- sapply ( rsi_translation , function ( x ) paste0 ( " <" , class ( x ) , " >" , collapse = " and " ) )
class_ref <- sapply ( reference_data , function ( x ) paste0 ( " <" , class ( x ) , " >" , collapse = " and " ) )
if ( ! all ( names ( class_rsi ) == names ( class_ref ) ) ) {
stop_ ( " 'reference_data' must have the same column names as the 'rsi_translation' data set." , call = -2 )
}
if ( ! all ( class_rsi == class_ref ) ) {
class_rsi [class_rsi != class_ref ] [1 ]
stop_ ( " 'reference_data' must be the same structure as the 'rsi_translation' data set. Column '" , names ( class_ref [class_rsi != class_ref ] [1 ] ) , " ' is of class " , class_ref [class_rsi != class_ref ] [1 ] , " , but should be of class " , class_rsi [class_rsi != class_ref ] [1 ] , " ." , call = -2 )
}
}
}