2018-08-10 15:01:05 +02:00
# ==================================================================== #
# TITLE #
# Antimicrobial Resistance (AMR) Analysis #
# #
2019-01-02 23:24:07 +01:00
# SOURCE #
# https://gitlab.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. #
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. #
2019-04-05 18:47:39 +02:00
# Visit our website for more info: https://msberends.gitlab.io/AMR. #
2018-08-10 15:01:05 +02:00
# ==================================================================== #
2018-08-25 22:01:14 +02:00
#' Class 'rsi'
2018-08-10 15:01:05 +02:00
#'
2019-11-29 19:43:23 +01:00
#' Interpret 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`. Invalid antimicrobial interpretations will be translated 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)
#' @param mo any (vector of) text that can be coerced to a valid microorganism code with [as.mo()]
#' @param ab any (vector of) text that can be coerced to a valid antimicrobial code with [as.ab()]
2019-05-10 16:44:59 +02:00
#' @inheritParams first_isolate
2020-02-14 19:54:13 +01:00
#' @param guideline defaults to the latest included EUCAST guideline, run `unique(rsi_translation$guideline)` for all options
2019-11-28 22:32:17 +01:00
#' @param threshold maximum fraction of invalid antimicrobial interpretations of `x`, please see *Examples*
2019-05-10 16:44:59 +02:00
#' @param ... parameters passed on to methods
2020-02-14 19:54:13 +01:00
#' @details Run `unique(rsi_translation$guideline)` for a list of all supported guidelines. The repository of this package contains [this machine readable version](https://gitlab.com/msberends/AMR/blob/master/data-raw/rsi_translation.txt) of these guidelines.
2020-01-27 11:05:39 +01:00
#'
#' These guidelines are machine readable, since [](https://gitlab.com/msberends/AMR/blob/master/data-raw/rsi_translation.txt).
2019-04-09 14:59:17 +02:00
#'
2019-11-28 22:32:17 +01:00
#' After using [as.rsi()], you can use [eucast_rules()] 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.
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-02-16 22:43:56 +01: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 (<http://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.
#' @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
2019-07-29 17:34:57 +02:00
#' @importFrom dplyr %>% desc arrange filter
2019-11-28 22:32:17 +01:00
#' @seealso [as.mic()]
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-02-16 22:43:56 +01:00
#' # For INTERPRETING disk diffusion and MIC values -----------------------
#'
#' # 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
#'
#' # a whole data set, even with combined MIC values and disk zones
#' df <- data.frame(microorganism = "E. coli",
2020-02-17 14:38:01 +01:00
#' AMP = as.mic(8),
#' CIP = as.mic(0.256),
#' GEN = as.disk(18),
#' TOB = as.disk(16))
2020-02-16 22:43:56 +01:00
#' as.rsi(df)
#'
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"
#'
#' 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
#' freq(rsi_data) # frequency table with informative header
#'
#' library(dplyr)
2019-08-27 16:45:42 +02:00
#' example_isolates %>%
2019-05-10 16:44:59 +02:00
#' mutate_at(vars(PEN:RIF), as.rsi)
2018-12-07 12:04:55 +01:00
#'
2020-02-16 22:43:56 +01:00
#' # fastest way to transform all columns with already valid AMR results to class `rsi`:
2019-08-27 16:45:42 +02:00
#' example_isolates %>%
2020-02-16 22:43:56 +01:00
#' mutate_if(is.rsi.eligible, as.rsi)
#'
#' # note: from dplyr 1.0.0 on, this will be:
#' # example_isolates %>%
#' # mutate(across(is.rsi.eligible, as.rsi))
2019-02-04 12:24:07 +01:00
#'
#' # default threshold of `is.rsi.eligible` is 5%.
#' is.rsi.eligible(WHONET$`First name`) # fails, >80% is invalid
2019-05-10 16:44:59 +02:00
#' is.rsi.eligible(WHONET$`First name`, threshold = 0.99) # succeeds
as.rsi <- function ( x , ... ) {
UseMethod ( " as.rsi" )
}
#' @export
as.rsi.default <- function ( x , ... ) {
2018-08-25 22:01:14 +02:00
if ( is.rsi ( x ) ) {
x
2018-12-07 12:04:55 +01:00
} else if ( identical ( levels ( x ) , c ( " S" , " I" , " R" ) ) ) {
2019-10-11 17:21:02 +02:00
structure ( x , class = c ( " rsi" , " ordered" , " factor" ) )
2019-11-05 11:28:52 +01:00
} else if ( identical ( class ( x ) , " integer" ) & all ( x %in% c ( 1 : 3 , NA ) ) ) {
x [x == 1 ] <- " S"
x [x == 2 ] <- " I"
x [x == 3 ] <- " R"
structure ( .Data = factor ( x , levels = c ( " S" , " I" , " R" ) , ordered = TRUE ) ,
class = c ( " rsi" , " ordered" , " factor" ) )
2018-08-23 00:40:36 +02:00
} else {
2019-10-23 14:48:25 +02:00
2018-08-25 22:01:14 +02:00
x <- x %>% unlist ( )
x.bak <- x
2019-10-23 14:48:25 +02:00
2019-10-11 17:21:02 +02:00
na_before <- x [is.na ( x ) | x == " " ] %>% length ( )
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
na_after <- x [is.na ( x ) | x == " " ] %>% length ( )
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 ) {
2019-10-11 17:21:02 +02:00
list_missing <- x.bak [is.na ( x ) & ! is.na ( x.bak ) & x.bak != " " ] %>%
2019-08-09 14:28:46 +02:00
unique ( ) %>%
sort ( )
2019-10-11 17:21:02 +02:00
list_missing <- paste0 ( ' "' , list_missing , ' "' , collapse = " , " )
warning ( na_after - na_before , " results truncated (" ,
2019-08-09 14:28:46 +02:00
round ( ( ( na_after - na_before ) / length ( x ) ) * 100 ) ,
2019-10-11 17:21:02 +02:00
" %) that were invalid antimicrobial interpretations: " ,
2019-08-09 14:28:46 +02:00
list_missing , call. = FALSE )
}
2018-08-25 22:01:14 +02:00
}
2019-08-09 14:28:46 +02:00
2019-05-10 16:44:59 +02:00
structure ( .Data = factor ( x , levels = c ( " S" , " I" , " R" ) , ordered = TRUE ) ,
2019-10-11 17:21:02 +02:00
class = c ( " rsi" , " ordered" , " factor" ) )
2018-08-23 00:40:36 +02:00
}
2018-08-25 22:01:14 +02:00
}
2018-08-23 00:40:36 +02:00
2019-04-09 14:59:17 +02:00
input_resembles_mic <- function ( x ) {
2019-02-09 22:16:24 +01:00
mic <- x %>%
gsub ( " [^0-9.,]+" , " " , .) %>%
unique ( )
mic_valid <- suppressWarnings ( as.mic ( mic ) )
2019-04-05 18:47:39 +02:00
result <- sum ( ! is.na ( mic_valid ) ) / length ( mic )
if ( is.na ( result ) ) {
0
} else {
result
}
2019-02-09 22:16:24 +01:00
}
2019-05-10 16:44:59 +02:00
#' @rdname as.rsi
#' @importFrom dplyr case_when
#' @export
as.rsi.mic <- function ( x , mo , ab , guideline = " EUCAST" , ... ) {
exec_as.rsi ( method = " mic" ,
x = x ,
mo = mo ,
ab = ab ,
guideline = guideline )
}
#' @rdname as.rsi
#' @export
as.rsi.disk <- function ( x , mo , ab , guideline = " EUCAST" , ... ) {
exec_as.rsi ( method = " disk" ,
x = x ,
mo = mo ,
ab = ab ,
guideline = guideline )
}
2019-10-23 14:48:25 +02:00
get_guideline <- function ( guideline ) {
2019-05-13 10:10:16 +02:00
guideline_param <- toupper ( guideline )
if ( guideline_param %in% c ( " CLSI" , " EUCAST" ) ) {
2020-02-14 19:54:13 +01:00
guideline_param <- rsi_translation %>%
2019-05-13 10:10:16 +02:00
filter ( guideline %like% guideline_param ) %>%
2019-05-10 16:44:59 +02:00
pull ( guideline ) %>%
sort ( ) %>%
rev ( ) %>%
.[1 ]
}
2019-10-23 14:48:25 +02:00
2020-02-14 19:54:13 +01:00
if ( ! guideline_param %in% rsi_translation $ guideline ) {
2019-05-10 16:44:59 +02:00
stop ( paste0 ( " invalid guideline: '" , guideline ,
2020-02-14 19:54:13 +01:00
" '.\nValid guidelines are: " , paste0 ( " '" , rev ( sort ( unique ( rsi_translation $ guideline ) ) ) , " '" , collapse = " , " ) ) ,
2019-05-10 16:44:59 +02:00
call. = FALSE )
}
2019-10-23 14:48:25 +02:00
guideline_param
}
exec_as.rsi <- function ( method , x , mo , ab , guideline ) {
if ( method == " mic" ) {
x <- as.double ( as.mic ( x ) ) # when as.rsi.mic is called directly
method_param <- " MIC"
} else if ( method == " disk" ) {
x <- as.double ( as.disk ( x ) ) # when as.rsi.disk is called directly
method_param <- " DISK"
}
mo <- as.mo ( mo )
ab <- as.ab ( ab )
mo_genus <- as.mo ( mo_genus ( mo ) )
mo_family <- as.mo ( mo_family ( mo ) )
mo_order <- as.mo ( mo_order ( mo ) )
mo_becker <- as.mo ( mo , Becker = TRUE )
mo_lancefield <- as.mo ( mo , Lancefield = TRUE )
2020-02-14 19:54:13 +01:00
mo_other <- as.mo ( " other" )
2019-10-23 14:48:25 +02:00
guideline_coerced <- get_guideline ( guideline )
if ( guideline_coerced != guideline ) {
message ( blue ( paste0 ( " Note: Using guideline " , bold ( guideline_coerced ) , " as input for `guideline`." ) ) )
}
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-14 19:54:13 +01:00
trans <- rsi_translation %>%
2019-10-23 14:48:25 +02:00
filter ( guideline == guideline_coerced & method == method_param ) %>%
2019-05-10 16:44:59 +02:00
mutate ( lookup = paste ( mo , 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
2019-10-11 17:21:02 +02:00
for ( i in seq_len ( length ( x ) ) ) {
2019-05-10 16:44:59 +02:00
get_record <- trans %>%
filter ( lookup %in% c ( lookup_mo [i ] ,
lookup_genus [i ] ,
lookup_family [i ] ,
lookup_order [i ] ,
lookup_becker [i ] ,
2020-02-14 19:54:13 +01:00
lookup_lancefield [i ] ,
lookup_other [i ] ) ) %>%
2019-05-10 16:44:59 +02:00
# be as specific as possible (i.e. prefer species over genus):
arrange ( desc ( nchar ( mo ) ) ) %>%
2019-10-11 17:21:02 +02:00
.[1L , ]
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" ) {
new_rsi [i ] <- case_when ( isTRUE ( x [i ] <= get_record $ breakpoint_S ) ~ " S" ,
isTRUE ( x [i ] >= get_record $ breakpoint_R ) ~ " R" ,
! is.na ( get_record $ breakpoint_S ) & ! is.na ( get_record $ breakpoint_R ) ~ " I" ,
2019-05-13 10:10:16 +02:00
TRUE ~ NA_character_ )
2019-05-10 16:44:59 +02:00
} else if ( method == " disk" ) {
2019-10-23 14:48:25 +02:00
new_rsi [i ] <- case_when ( isTRUE ( x [i ] >= get_record $ breakpoint_S ) ~ " S" ,
isTRUE ( x [i ] <= get_record $ breakpoint_R ) ~ " R" ,
! is.na ( get_record $ breakpoint_S ) & ! is.na ( get_record $ breakpoint_R ) ~ " I" ,
2019-05-13 10:10:16 +02:00
TRUE ~ NA_character_ )
2019-05-10 16:44:59 +02:00
}
}
}
structure ( .Data = factor ( new_rsi , levels = c ( " S" , " I" , " R" ) , ordered = TRUE ) ,
2019-10-11 17:21:02 +02:00
class = c ( " rsi" , " ordered" , " factor" ) )
2019-05-10 16:44:59 +02:00
}
#' @rdname as.rsi
2019-10-21 14:12:28 +02:00
#' @importFrom crayon red blue bold
2019-05-10 16:44:59 +02:00
#' @export
as.rsi.data.frame <- function ( x , col_mo = NULL , guideline = " EUCAST" , ... ) {
2019-05-23 16:58:59 +02:00
x <- x
2019-10-23 14:48:25 +02:00
2019-05-23 16:58:59 +02:00
ab_cols <- colnames ( x ) [sapply ( x , function ( y ) is.mic ( y ) | is.disk ( y ) ) ]
2019-05-10 16:44:59 +02:00
if ( length ( ab_cols ) == 0 ) {
2019-08-11 19:07:26 +02:00
stop ( " No columns with MIC values or disk zones found in this data set. Use as.mic or as.disk to transform antimicrobial columns." , call. = FALSE )
2019-05-10 16:44:59 +02:00
}
2019-10-23 14:48:25 +02:00
2019-05-10 16:44:59 +02:00
# try to find columns based on type
# -- mo
if ( is.null ( col_mo ) ) {
2019-05-23 16:58:59 +02:00
col_mo <- search_type_in_df ( x = x , type = " mo" )
2019-05-10 16:44:59 +02:00
}
if ( is.null ( col_mo ) ) {
stop ( " `col_mo` must be set." , call. = FALSE )
}
2019-10-21 14:12:28 +02:00
2019-10-23 14:48:25 +02:00
guideline_coerced <- get_guideline ( guideline )
if ( guideline_coerced != guideline ) {
message ( blue ( paste0 ( " Note: Using guideline " , bold ( guideline_coerced ) , " as input for `guideline`." ) ) )
}
2019-05-10 16:44:59 +02:00
# transform all MICs
2019-05-23 16:58:59 +02:00
ab_cols <- colnames ( x ) [sapply ( x , is.mic ) ]
2019-05-10 16:44:59 +02:00
if ( length ( ab_cols ) > 0 ) {
2019-10-11 17:21:02 +02:00
for ( i in seq_len ( length ( ab_cols ) ) ) {
2019-10-23 14:48:25 +02:00
ab_col_coerced <- suppressWarnings ( as.ab ( ab_cols [i ] ) )
if ( is.na ( ab_col_coerced ) ) {
2019-05-10 16:44:59 +02:00
message ( red ( paste0 ( " Unknown drug: `" , bold ( ab_cols [i ] ) , " `. Rename this column to a drug name or code, and check the output with as.ab()." ) ) )
next
}
2020-02-16 22:43:56 +01:00
message ( blue ( paste0 ( " Interpreting MIC values of column `" , bold ( ab_cols [i ] ) , " ` (" ,
2019-10-23 14:48:25 +02:00
ifelse ( ab_col_coerced != ab_cols [i ] , paste0 ( ab_col_coerced , " , " ) , " " ) ,
ab_name ( ab_col_coerced , tolower = TRUE ) , " )..." ) ) ,
appendLF = FALSE )
2019-05-23 16:58:59 +02:00
x [ , ab_cols [i ] ] <- exec_as.rsi ( method = " mic" ,
2019-10-23 14:48:25 +02:00
x = x %>% pull ( ab_cols [i ] ) ,
mo = x %>% pull ( col_mo ) ,
ab = ab_col_coerced ,
guideline = guideline_coerced )
2019-05-10 16:44:59 +02:00
message ( blue ( " OK." ) )
}
}
# transform all disks
2019-05-23 16:58:59 +02:00
ab_cols <- colnames ( x ) [sapply ( x , is.disk ) ]
2019-05-10 16:44:59 +02:00
if ( length ( ab_cols ) > 0 ) {
2019-10-11 17:21:02 +02:00
for ( i in seq_len ( length ( ab_cols ) ) ) {
2019-10-23 14:48:25 +02:00
ab_col_coerced <- suppressWarnings ( as.ab ( ab_cols [i ] ) )
if ( is.na ( ab_col_coerced ) ) {
2019-05-10 16:44:59 +02:00
message ( red ( paste0 ( " Unknown drug: `" , bold ( ab_cols [i ] ) , " `. Rename this column to a drug name or code, and check the output with as.ab()." ) ) )
next
}
2020-02-16 22:43:56 +01:00
message ( blue ( paste0 ( " Interpreting disk zones of column `" , bold ( ab_cols [i ] ) , " ` (" ,
2019-10-23 14:48:25 +02:00
ifelse ( ab_col_coerced != ab_cols [i ] , paste0 ( ab_col_coerced , " , " ) , " " ) ,
ab_name ( ab_col_coerced , tolower = TRUE ) , " )..." ) ) ,
appendLF = FALSE )
2019-05-23 16:58:59 +02:00
x [ , ab_cols [i ] ] <- exec_as.rsi ( method = " disk" ,
2019-10-23 14:48:25 +02:00
x = x %>% pull ( ab_cols [i ] ) ,
mo = x %>% pull ( col_mo ) ,
ab = ab_col_coerced ,
guideline = guideline_coerced )
2019-05-10 16:44:59 +02:00
message ( blue ( " OK." ) )
}
}
2019-10-23 14:48:25 +02:00
2019-05-23 16:58:59 +02:00
x
2019-05-10 16:44:59 +02:00
}
2018-08-25 22:01:14 +02:00
#' @rdname as.rsi
#' @export
is.rsi <- function ( x ) {
2020-02-10 14:18:15 +01:00
inherits ( x , " rsi" )
2018-08-25 22:01:14 +02:00
}
#' @rdname as.rsi
#' @export
2019-02-04 12:24:07 +01:00
is.rsi.eligible <- function ( x , threshold = 0.05 ) {
if ( NCOL ( x ) > 1 ) {
2019-10-11 17:21:02 +02:00
stop ( " `x` must be a one-dimensional vector." )
2019-02-04 12:24:07 +01:00
}
if ( any ( c ( " logical" ,
" numeric" ,
" integer" ,
" mo" ,
" Date" ,
" POSIXct" ,
" rsi" ,
" raw" ,
" hms" )
%in% class ( x ) ) ) {
2018-11-02 14:55:29 +01:00
# no transformation needed
FALSE
} else {
2019-02-04 12:24:07 +01:00
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
2018-11-02 14:55:29 +01:00
}
2018-08-25 22:01:14 +02:00
}
#' @exportMethod print.rsi
#' @export
#' @importFrom dplyr %>%
#' @noRd
print.rsi <- function ( x , ... ) {
cat ( " Class 'rsi'\n" )
print ( as.character ( x ) , quote = FALSE )
}
2018-12-29 22:24:19 +01:00
#' @exportMethod droplevels.rsi
#' @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
}
2018-08-25 22:01:14 +02:00
#' @exportMethod summary.rsi
#' @export
#' @noRd
summary.rsi <- function ( object , ... ) {
x <- object
c (
2019-10-11 17:21:02 +02:00
" Class" = " rsi" ,
2018-08-25 22:01:14 +02:00
" <NA>" = sum ( is.na ( x ) ) ,
" Sum S" = sum ( x == " S" , na.rm = TRUE ) ,
" Sum IR" = sum ( x %in% c ( " I" , " R" ) , na.rm = TRUE ) ,
" -Sum R" = sum ( x == " R" , na.rm = TRUE ) ,
" -Sum I" = sum ( x == " I" , na.rm = TRUE )
2018-08-10 15:01:05 +02:00
)
2018-08-25 22:01:14 +02:00
}
2018-08-10 15:01:05 +02:00
2018-08-25 22:01:14 +02:00
#' @exportMethod plot.rsi
#' @export
#' @importFrom dplyr %>% group_by summarise filter mutate if_else n_distinct
#' @importFrom graphics plot text
#' @noRd
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" ,
main = paste ( " Susceptibility Analysis of" , deparse ( substitute ( x ) ) ) ,
2019-06-16 21:42:40 +02:00
axes = FALSE ,
... ) {
2018-12-29 22:24:19 +01:00
suppressWarnings (
data <- data.frame ( x = x ,
y = 1 ,
stringsAsFactors = TRUE ) %>%
group_by ( x ) %>%
summarise ( n = sum ( y ) ) %>%
filter ( ! is.na ( x ) ) %>%
mutate ( s = round ( ( n / sum ( n ) ) * 100 , 1 ) )
)
2019-05-10 16:44:59 +02:00
if ( ! " S" %in% data $ x ) {
data <- rbind ( data , data.frame ( x = " S" , n = 0 , s = 0 ) )
}
if ( ! " I" %in% data $ x ) {
data <- rbind ( data , data.frame ( x = " I" , n = 0 , s = 0 ) )
}
if ( ! " R" %in% data $ x ) {
data <- rbind ( data , data.frame ( x = " R" , n = 0 , s = 0 ) )
}
2019-10-23 14:48:25 +02:00
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
2018-08-25 22:01:14 +02:00
ymax <- 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
axis ( side = 1 , at = 1 : n_distinct ( data $ x ) , labels = levels ( data $ x ) , lwd = 0 )
# 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
}
#' @exportMethod barplot.rsi
#' @export
2019-05-10 16:44:59 +02:00
#' @importFrom dplyr %>% group_by summarise
2019-06-16 22:14:43 +02:00
#' @importFrom graphics barplot axis par
2018-08-25 22:01:14 +02:00
#' @noRd
2019-06-16 21:42:40 +02:00
barplot.rsi <- function ( height ,
2019-10-11 17:21:02 +02:00
col = c ( " green3" , " orange2" , " red3" ) ,
xlab = ifelse ( beside , " Antimicrobial Interpretation" , " " ) ,
main = paste ( " Susceptibility Analysis of" , deparse ( substitute ( height ) ) ) ,
ylab = " Frequency" ,
2019-06-16 21:42:40 +02:00
beside = TRUE ,
axes = beside ,
... ) {
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
#' @importFrom pillar type_sum
#' @export
type_sum.rsi <- function ( x ) {
" rsi"
}
#' @importFrom pillar pillar_shaft
2019-10-23 15:44:11 +02:00
#' @importFrom crayon bgGreen bgYellow bgRed black white
2019-08-07 15:37:39 +02:00
#' @export
pillar_shaft.rsi <- function ( x , ... ) {
out <- trimws ( format ( x ) )
2019-08-12 14:48:09 +02:00
out [is.na ( x ) ] <- pillar :: style_subtle ( " NA" )
2019-10-23 15:44:11 +02:00
out [x == " S" ] <- bgGreen ( white ( " S " ) )
2019-08-07 15:37:39 +02:00
out [x == " I" ] <- bgYellow ( black ( " I " ) )
2019-10-23 15:44:11 +02:00
out [x == " R" ] <- bgRed ( white ( " R " ) )
2019-10-23 14:48:25 +02:00
pillar :: new_pillar_shaft_simple ( out , align = " left" , width = 3 )
2019-08-07 15:37:39 +02:00
}