2018-02-21 11:52:31 +01:00
|
|
|
# ==================================================================== #
|
|
|
|
# TITLE #
|
|
|
|
# Antimicrobial Resistance (AMR) Analysis #
|
|
|
|
# #
|
2019-01-02 23:24:07 +01:00
|
|
|
# SOURCE #
|
2020-07-08 14:48:06 +02:00
|
|
|
# https://github.com/msberends/AMR #
|
2018-02-21 11:52:31 +01:00
|
|
|
# #
|
|
|
|
# LICENCE #
|
2020-01-05 17:22:09 +01:00
|
|
|
# (c) 2018-2020 Berends MS, Luz CF et al. #
|
2018-02-21 11:52:31 +01: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-07-08 14:48:06 +02:00
|
|
|
# Visit our website for more info: https://msberends.github.io/AMR. #
|
2018-02-21 11:52:31 +01:00
|
|
|
# ==================================================================== #
|
|
|
|
|
2020-09-03 12:31:48 +02:00
|
|
|
# faster implementation of left_join than using merge() by poorman - we use match():
|
2020-09-18 16:05:53 +02:00
|
|
|
pm_left_join <- function(x, y, by = NULL, suffix = c(".x", ".y")) {
|
2020-08-14 13:36:10 +02:00
|
|
|
if (is.null(by)) {
|
|
|
|
by <- intersect(names(x), names(y))[1L]
|
|
|
|
if (is.na(by)) {
|
2020-09-18 16:05:53 +02:00
|
|
|
stop_("no common column found for pm_left_join()")
|
2020-08-14 13:36:10 +02:00
|
|
|
}
|
2020-09-19 11:54:01 +02:00
|
|
|
pm_join_message(by)
|
2020-08-14 13:36:10 +02:00
|
|
|
} else if (!is.null(names(by))) {
|
|
|
|
by <- unname(c(names(by), by))
|
|
|
|
}
|
|
|
|
if (length(by) == 1) {
|
|
|
|
by <- rep(by, 2)
|
|
|
|
}
|
2020-08-15 12:54:47 +02:00
|
|
|
|
|
|
|
int_x <- colnames(x) %in% colnames(y) & colnames(x) != by[1]
|
|
|
|
int_y <- colnames(y) %in% colnames(x) & colnames(y) != by[2]
|
|
|
|
colnames(x)[int_x] <- paste0(colnames(x)[int_x], suffix[1L])
|
|
|
|
colnames(y)[int_y] <- paste0(colnames(y)[int_y], suffix[2L])
|
|
|
|
|
2020-08-14 13:36:10 +02:00
|
|
|
merged <- cbind(x,
|
|
|
|
y[match(x[, by[1], drop = TRUE],
|
|
|
|
y[, by[2], drop = TRUE]),
|
|
|
|
colnames(y)[!colnames(y) %in% colnames(x) & !colnames(y) == by[2]],
|
|
|
|
drop = FALSE])
|
|
|
|
|
|
|
|
rownames(merged) <- NULL
|
|
|
|
merged
|
|
|
|
}
|
2020-09-18 16:05:53 +02:00
|
|
|
# pm_filter_join_worker <- function(x, y, by = NULL, type = c("anti", "semi")) {
|
|
|
|
# type <- match.arg(type, choices = c("anti", "semi"), several.ok = FALSE)
|
|
|
|
# if (is.null(by)) {
|
|
|
|
# by <- intersect(names(x), names(y))
|
|
|
|
# join_message(by)
|
|
|
|
# }
|
|
|
|
# rows <- interaction(x[, by]) %in% interaction(y[, by])
|
|
|
|
# if (type == "anti") rows <- !rows
|
|
|
|
# res <- x[rows, , drop = FALSE]
|
|
|
|
# rownames(res) <- NULL
|
|
|
|
# res
|
|
|
|
# }
|
|
|
|
|
|
|
|
quick_case_when <- function(...) {
|
|
|
|
vectors <- list(...)
|
|
|
|
split <- lapply(vectors, function(x) unlist(strsplit(paste(deparse(x), collapse = ""), "~", fixed = TRUE)))
|
|
|
|
for (i in seq_len(length(vectors))) {
|
|
|
|
if (eval(parse(text = split[[i]][1]), envir = parent.frame())) {
|
|
|
|
return(eval(parse(text = split[[i]][2]), envir = parent.frame()))
|
|
|
|
}
|
2020-05-16 13:05:47 +02:00
|
|
|
}
|
2020-09-18 16:05:53 +02:00
|
|
|
return(NA)
|
2020-05-16 13:05:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-04 17:20:03 +02:00
|
|
|
# No export, no Rd
|
|
|
|
addin_insert_in <- function() {
|
2020-06-17 15:14:37 +02:00
|
|
|
import_fn("insertText", "rstudioapi")(" %in% ")
|
2018-07-04 17:20:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# No export, no Rd
|
|
|
|
addin_insert_like <- function() {
|
2020-06-17 15:14:37 +02:00
|
|
|
import_fn("insertText", "rstudioapi")(" %like% ")
|
2018-07-04 17:20:03 +02:00
|
|
|
}
|
|
|
|
|
2020-02-14 19:54:13 +01:00
|
|
|
check_dataset_integrity <- function() {
|
2020-07-28 18:39:57 +02:00
|
|
|
# check if user overwrote our data sets in their global environment
|
|
|
|
data_in_pkg <- data(package = "AMR", envir = asNamespace("AMR"))$results[, "Item", drop = TRUE]
|
|
|
|
data_in_globalenv <- ls(envir = globalenv())
|
|
|
|
overwritten <- data_in_pkg[data_in_pkg %in% data_in_globalenv]
|
|
|
|
stop_if(length(overwritten) > 0,
|
|
|
|
"the following data set is overwritten by your global environment and prevents the AMR package from working correctly:\n",
|
|
|
|
paste0("'", overwritten, "'", collapse = ", "),
|
|
|
|
".\nPlease rename your object before using this function.", call = FALSE)
|
|
|
|
# check if other packages did not overwrite our data sets
|
2020-02-16 22:43:56 +01:00
|
|
|
tryCatch({
|
|
|
|
check_microorganisms <- all(c("mo", "fullname", "kingdom", "phylum",
|
|
|
|
"class", "order", "family", "genus",
|
|
|
|
"species", "subspecies", "rank",
|
2020-05-27 16:37:49 +02:00
|
|
|
"species_id", "source", "ref", "prevalence") %in% colnames(microorganisms),
|
2020-07-08 14:48:06 +02:00
|
|
|
na.rm = TRUE)
|
2020-02-16 22:43:56 +01:00
|
|
|
check_antibiotics <- all(c("ab", "atc", "cid", "name", "group",
|
|
|
|
"atc_group1", "atc_group2", "abbreviations",
|
|
|
|
"synonyms", "oral_ddd", "oral_units",
|
|
|
|
"iv_ddd", "iv_units", "loinc") %in% colnames(antibiotics),
|
|
|
|
na.rm = TRUE)
|
2020-08-14 13:36:10 +02:00
|
|
|
}, error = function(e) {
|
|
|
|
# package not yet loaded
|
|
|
|
require("AMR")
|
|
|
|
})
|
2020-02-14 19:54:13 +01:00
|
|
|
invisible(TRUE)
|
2019-11-23 12:39:57 +01:00
|
|
|
}
|
|
|
|
|
2019-05-23 16:58:59 +02:00
|
|
|
search_type_in_df <- function(x, type) {
|
2019-01-15 12:45:24 +01:00
|
|
|
# try to find columns based on type
|
|
|
|
found <- NULL
|
2020-05-16 13:05:47 +02:00
|
|
|
|
|
|
|
x <- as.data.frame(x, stringsAsFactors = FALSE)
|
2019-05-23 16:58:59 +02:00
|
|
|
colnames(x) <- trimws(colnames(x))
|
2019-11-04 12:08:08 +01:00
|
|
|
|
2019-01-15 12:45:24 +01:00
|
|
|
# -- mo
|
|
|
|
if (type == "mo") {
|
2020-06-11 20:03:13 +02:00
|
|
|
if (any(sapply(x, is.mo))) {
|
|
|
|
found <- sort(colnames(x)[sapply(x, is.mo)])[1]
|
2019-11-04 12:08:08 +01:00
|
|
|
} else if ("mo" %in% colnames(x) &
|
|
|
|
suppressWarnings(
|
|
|
|
all(x$mo %in% c(NA,
|
|
|
|
microorganisms$mo,
|
|
|
|
microorganisms.translation$mo_old)))) {
|
|
|
|
found <- "mo"
|
2020-06-11 20:03:13 +02:00
|
|
|
} else if (any(colnames(x) %like% "^(mo|microorganism|organism|bacteria|ba[ck]terie)s?$")) {
|
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^(mo|microorganism|organism|bacteria|ba[ck]terie)s?$"])[1]
|
|
|
|
} else if (any(colnames(x) %like% "^(microorganism|organism|bacteria|ba[ck]terie)")) {
|
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^(microorganism|organism|bacteria|ba[ck]terie)"])[1]
|
2019-05-23 16:58:59 +02:00
|
|
|
} else if (any(colnames(x) %like% "species")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "species"])[1]
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
2019-11-04 12:08:08 +01:00
|
|
|
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
|
|
|
# -- key antibiotics
|
|
|
|
if (type == "keyantibiotics") {
|
2019-05-23 16:58:59 +02:00
|
|
|
if (any(colnames(x) %like% "^key.*(ab|antibiotics)")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^key.*(ab|antibiotics)"])[1]
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# -- date
|
|
|
|
if (type == "date") {
|
2019-05-23 16:58:59 +02:00
|
|
|
if (any(colnames(x) %like% "^(specimen date|specimen_date|spec_date)")) {
|
2019-03-15 13:57:25 +01:00
|
|
|
# WHONET support
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^(specimen date|specimen_date|spec_date)"])[1]
|
2020-09-18 16:05:53 +02:00
|
|
|
if (!any(class(pm_pull(x, found)) %in% c("Date", "POSIXct"))) {
|
2020-05-16 13:05:47 +02:00
|
|
|
stop(font_red(paste0("ERROR: Found column `", font_bold(found), "` to be used as input for `col_", type,
|
2020-06-11 20:03:13 +02:00
|
|
|
"`, but this column contains no valid dates. Transform its values to valid dates first.")),
|
2019-03-15 13:57:25 +01:00
|
|
|
call. = FALSE)
|
|
|
|
}
|
2020-06-11 20:03:13 +02:00
|
|
|
} else if (any(sapply(x, function(x) inherits(x, c("Date", "POSIXct"))))) {
|
|
|
|
found <- sort(colnames(x)[sapply(x, function(x) inherits(x, c("Date", "POSIXct")))])[1]
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# -- patient id
|
|
|
|
if (type == "patient_id") {
|
2019-05-23 16:58:59 +02:00
|
|
|
if (any(colnames(x) %like% "^(identification |patient|patid)")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^(identification |patient|patid)"])[1]
|
2019-01-29 00:06:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
# -- specimen
|
|
|
|
if (type == "specimen") {
|
2019-05-23 16:58:59 +02:00
|
|
|
if (any(colnames(x) %like% "(specimen type|spec_type)")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "(specimen type|spec_type)"])[1]
|
2019-05-23 16:58:59 +02:00
|
|
|
} else if (any(colnames(x) %like% "^(specimen)")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "^(specimen)"])[1]
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-20 13:19:23 +01:00
|
|
|
# -- UTI (urinary tract infection)
|
|
|
|
if (type == "uti") {
|
|
|
|
if (any(colnames(x) == "uti")) {
|
|
|
|
found <- colnames(x)[colnames(x) == "uti"][1]
|
|
|
|
} else if (any(colnames(x) %like% "(urine|urinary)")) {
|
2020-06-11 20:03:13 +02:00
|
|
|
found <- sort(colnames(x)[colnames(x) %like% "(urine|urinary)"])[1]
|
2020-02-20 13:19:23 +01:00
|
|
|
}
|
|
|
|
if (!is.null(found)) {
|
2020-02-21 21:13:38 +01:00
|
|
|
# this column should contain logicals
|
2020-02-20 13:19:23 +01:00
|
|
|
if (!is.logical(x[, found, drop = TRUE])) {
|
2020-05-16 13:05:47 +02:00
|
|
|
message(font_red(paste0("NOTE: Column `", font_bold(found), "` found as input for `col_", type,
|
2020-06-11 20:03:13 +02:00
|
|
|
"`, but this column does not contain 'logical' values (TRUE/FALSE) and was ignored.")))
|
2020-02-20 13:19:23 +01:00
|
|
|
found <- NULL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 12:45:24 +01:00
|
|
|
if (!is.null(found)) {
|
2020-05-16 13:05:47 +02:00
|
|
|
msg <- paste0("NOTE: Using column `", font_bold(found), "` as input for `col_", type, "`.")
|
2019-01-29 00:06:50 +01:00
|
|
|
if (type %in% c("keyantibiotics", "specimen")) {
|
2020-05-16 13:05:47 +02:00
|
|
|
msg <- paste(msg, "Use", font_bold(paste0("col_", type), "= FALSE"), "to prevent this.")
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
2020-05-16 13:05:47 +02:00
|
|
|
message(font_blue(msg))
|
2019-01-15 12:45:24 +01:00
|
|
|
}
|
|
|
|
found
|
|
|
|
}
|
2019-03-26 14:24:03 +01:00
|
|
|
|
2020-06-22 11:18:40 +02:00
|
|
|
stop_ifnot_installed <- function(package) {
|
2019-06-01 20:40:49 +02:00
|
|
|
# no "utils::installed.packages()" since it requires non-staged install since R 3.6.0
|
|
|
|
# https://developer.r-project.org/Blog/public/2019/02/14/staged-install/index.html
|
2020-06-17 16:29:10 +02:00
|
|
|
sapply(package, function(pkg)
|
|
|
|
tryCatch(get(".packageName", envir = asNamespace(pkg)),
|
2020-07-02 21:12:52 +02:00
|
|
|
error = function(e) {
|
2020-06-17 15:14:37 +02:00
|
|
|
if (package == "rstudioapi") {
|
|
|
|
stop("This function only works in RStudio.", call. = FALSE)
|
2020-06-17 16:29:10 +02:00
|
|
|
} else if (pkg != "base") {
|
2020-08-26 16:13:40 +02:00
|
|
|
stop("This requires the '", pkg, "' package.",
|
2020-06-17 16:29:10 +02:00
|
|
|
"\nTry to install it with: install.packages(\"", pkg, "\")",
|
2020-06-17 15:14:37 +02:00
|
|
|
call. = FALSE)
|
|
|
|
}
|
|
|
|
}))
|
2019-06-01 20:40:49 +02:00
|
|
|
return(invisible())
|
2019-03-26 14:24:03 +01:00
|
|
|
}
|
2019-05-10 16:44:59 +02:00
|
|
|
|
2020-08-10 11:44:58 +02:00
|
|
|
import_fn <- function(name, pkg, error_on_fail = TRUE) {
|
|
|
|
if (isTRUE(error_on_fail)) {
|
|
|
|
stop_ifnot_installed(pkg)
|
|
|
|
}
|
2020-07-08 14:48:06 +02:00
|
|
|
tryCatch(
|
|
|
|
get(name, envir = asNamespace(pkg)),
|
2020-08-10 11:44:58 +02:00
|
|
|
error = function(e) {
|
|
|
|
if (isTRUE(error_on_fail)) {
|
2020-08-26 16:13:40 +02:00
|
|
|
stop_("function ", name, "() not found in package '", pkg,
|
|
|
|
"'. Please create an issue at https://github.com/msberends/AMR/issues. Many thanks!",
|
|
|
|
call = FALSE)
|
2020-08-10 11:44:58 +02:00
|
|
|
} else {
|
|
|
|
return(NULL)
|
|
|
|
}
|
|
|
|
})
|
2020-06-17 15:14:37 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 11:07:01 +02:00
|
|
|
stop_ <- function(..., call = TRUE) {
|
|
|
|
msg <- paste0(c(...), collapse = "")
|
|
|
|
if (!isFALSE(call)) {
|
|
|
|
if (isTRUE(call)) {
|
|
|
|
call <- as.character(sys.call(-1)[1])
|
|
|
|
} else {
|
|
|
|
# so you can go back more than 1 call, as used in rsi_calc(), that now throws a reference to e.g. n_rsi()
|
|
|
|
call <- as.character(sys.call(call)[1])
|
|
|
|
}
|
|
|
|
msg <- paste0("in ", call, "(): ", msg)
|
|
|
|
}
|
|
|
|
stop(msg, call. = FALSE)
|
|
|
|
}
|
|
|
|
|
2020-06-22 11:18:40 +02:00
|
|
|
stop_if <- function(expr, ..., call = TRUE) {
|
|
|
|
if (isTRUE(expr)) {
|
2020-07-01 11:07:01 +02:00
|
|
|
if (isTRUE(call)) {
|
|
|
|
call <- -1
|
|
|
|
}
|
2020-06-26 10:21:22 +02:00
|
|
|
if (!isFALSE(call)) {
|
2020-07-01 11:07:01 +02:00
|
|
|
# since we're calling stop_(), which is another call
|
|
|
|
call <- call - 1
|
2020-06-26 10:21:22 +02:00
|
|
|
}
|
2020-07-01 11:07:01 +02:00
|
|
|
stop_(..., call = call)
|
2020-03-08 11:18:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 11:18:40 +02:00
|
|
|
stop_ifnot <- function(expr, ..., call = TRUE) {
|
|
|
|
if (!isTRUE(expr)) {
|
2020-07-01 11:07:01 +02:00
|
|
|
if (isTRUE(call)) {
|
|
|
|
call <- -1
|
|
|
|
}
|
2020-06-26 10:21:22 +02:00
|
|
|
if (!isFALSE(call)) {
|
2020-07-01 11:07:01 +02:00
|
|
|
# since we're calling stop_(), which is another call
|
|
|
|
call <- call - 1
|
2020-06-26 10:21:22 +02:00
|
|
|
}
|
2020-07-01 11:07:01 +02:00
|
|
|
stop_(..., call = call)
|
2020-06-22 11:18:40 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-08 11:18:59 +01:00
|
|
|
|
2019-05-20 19:12:41 +02:00
|
|
|
"%or%" <- function(x, y) {
|
2019-06-16 21:42:40 +02:00
|
|
|
if (is.null(x) | is.null(y)) {
|
|
|
|
if (is.null(x)) {
|
|
|
|
return(y)
|
|
|
|
} else {
|
|
|
|
return(x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ifelse(!is.na(x),
|
|
|
|
x,
|
|
|
|
ifelse(!is.na(y), y, NA))
|
2019-05-20 19:12:41 +02:00
|
|
|
}
|
2019-08-26 16:02:03 +02:00
|
|
|
|
|
|
|
class_integrity_check <- function(value, type, check_vector) {
|
|
|
|
if (!all(value[!is.na(value)] %in% check_vector)) {
|
|
|
|
warning(paste0("invalid ", type, ", NA generated"), call. = FALSE)
|
|
|
|
value[!value %in% check_vector] <- NA
|
|
|
|
}
|
|
|
|
value
|
|
|
|
}
|
2020-01-27 19:14:23 +01:00
|
|
|
|
|
|
|
# transforms data set to data.frame with only ASCII values, to comply with CRAN policies
|
|
|
|
dataset_UTF8_to_ASCII <- function(df) {
|
|
|
|
trans <- function(vect) {
|
|
|
|
iconv(vect, from = "UTF-8", to = "ASCII//TRANSLIT")
|
|
|
|
}
|
|
|
|
df <- as.data.frame(df, stringsAsFactors = FALSE)
|
|
|
|
for (i in seq_len(NCOL(df))) {
|
|
|
|
col <- df[, i]
|
|
|
|
if (is.list(col)) {
|
|
|
|
col <- lapply(col, function(j) trans(j))
|
|
|
|
df[, i] <- list(col)
|
|
|
|
} else {
|
|
|
|
if (is.factor(col)) {
|
|
|
|
levels(col) <- trans(levels(col))
|
|
|
|
} else if (is.character(col)) {
|
|
|
|
col <- trans(col)
|
|
|
|
} else {
|
|
|
|
col
|
|
|
|
}
|
|
|
|
df[, i] <- col
|
|
|
|
}
|
|
|
|
}
|
|
|
|
df
|
|
|
|
}
|
2020-05-16 13:05:47 +02:00
|
|
|
|
2020-06-17 15:14:37 +02:00
|
|
|
has_colour <- function() {
|
2020-06-05 13:56:05 +02:00
|
|
|
# this is a base R version of crayon::has_color
|
|
|
|
enabled <- getOption("crayon.enabled")
|
|
|
|
if (!is.null(enabled)) {
|
|
|
|
return(isTRUE(enabled))
|
|
|
|
}
|
|
|
|
rstudio_with_ansi_support <- function(x) {
|
|
|
|
if (Sys.getenv("RSTUDIO", "") == "") {
|
|
|
|
return(FALSE)
|
|
|
|
}
|
|
|
|
if ((cols <- Sys.getenv("RSTUDIO_CONSOLE_COLOR", "")) != "" && !is.na(as.numeric(cols))) {
|
|
|
|
return(TRUE)
|
|
|
|
}
|
|
|
|
tryCatch(get("isAvailable", envir = asNamespace("rstudioapi"))(), error = function(e) return(FALSE)) &&
|
|
|
|
tryCatch(get("hasFun", envir = asNamespace("rstudioapi"))("getConsoleHasColor"), error = function(e) return(FALSE))
|
|
|
|
}
|
|
|
|
if (rstudio_with_ansi_support() && sink.number() == 0) {
|
|
|
|
return(TRUE)
|
|
|
|
}
|
|
|
|
if (!isatty(stdout())) {
|
2020-05-16 13:05:47 +02:00
|
|
|
return(FALSE)
|
|
|
|
}
|
|
|
|
if (tolower(Sys.info()["sysname"]) == "windows") {
|
2020-06-05 13:56:05 +02:00
|
|
|
if (Sys.getenv("ConEmuANSI") == "ON") {
|
2020-05-16 13:05:47 +02:00
|
|
|
return(TRUE)
|
|
|
|
}
|
2020-06-05 13:56:05 +02:00
|
|
|
if (Sys.getenv("CMDER_ROOT") != "") {
|
|
|
|
return(TRUE)
|
|
|
|
}
|
|
|
|
return(FALSE)
|
|
|
|
}
|
2020-06-17 15:14:37 +02:00
|
|
|
emacs_version <- function() {
|
2020-06-05 13:56:05 +02:00
|
|
|
ver <- Sys.getenv("INSIDE_EMACS")
|
|
|
|
if (ver == "") {
|
|
|
|
return(NA_integer_)
|
|
|
|
}
|
|
|
|
ver <- gsub("'", "", ver)
|
|
|
|
ver <- strsplit(ver, ",", fixed = TRUE)[[1]]
|
|
|
|
ver <- strsplit(ver, ".", fixed = TRUE)[[1]]
|
|
|
|
as.numeric(ver)
|
2020-05-16 13:05:47 +02:00
|
|
|
}
|
2020-06-05 13:56:05 +02:00
|
|
|
if ((Sys.getenv("EMACS") != "" || Sys.getenv("INSIDE_EMACS") != "") &&
|
|
|
|
!is.na(emacs_version()[1]) && emacs_version()[1] >= 23) {
|
|
|
|
return(TRUE)
|
|
|
|
}
|
|
|
|
if ("COLORTERM" %in% names(Sys.getenv())) {
|
|
|
|
return(TRUE)
|
|
|
|
}
|
|
|
|
if (Sys.getenv("TERM") == "dumb") {
|
|
|
|
return(FALSE)
|
|
|
|
}
|
|
|
|
grepl(pattern = "^screen|^xterm|^vt100|color|ansi|cygwin|linux",
|
|
|
|
x = Sys.getenv("TERM"),
|
|
|
|
ignore.case = TRUE,
|
|
|
|
perl = TRUE)
|
2020-05-16 13:05:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# the crayon colours
|
|
|
|
try_colour <- function(..., before, after, collapse = " ") {
|
|
|
|
txt <- paste0(unlist(list(...)), collapse = collapse)
|
|
|
|
if (isTRUE(has_colour())) {
|
|
|
|
if (is.null(collapse)) {
|
|
|
|
paste0(before, txt, after, collapse = NULL)
|
|
|
|
} else {
|
|
|
|
paste0(before, txt, after, collapse = "")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
txt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
font_black <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[38;5;232m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_blue <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[34m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_green <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[32m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_magenta <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[35m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_red <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[31m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_silver <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[90m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_white <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[37m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_yellow <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[33m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_subtle <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[38;5;246m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_grey <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[38;5;249m", after = "\033[39m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_green_bg <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[42m", after = "\033[49m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_red_bg <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[41m", after = "\033[49m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_yellow_bg <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[43m", after = "\033[49m", collapse = collapse)
|
|
|
|
}
|
2020-08-28 21:55:47 +02:00
|
|
|
font_na <- function(..., collapse = " ") {
|
|
|
|
font_red(..., collapse = collapse)
|
|
|
|
}
|
2020-05-16 13:05:47 +02:00
|
|
|
font_bold <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[1m", after = "\033[22m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_italic <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[3m", after = "\033[23m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_underline <- function(..., collapse = " ") {
|
|
|
|
try_colour(..., before = "\033[4m", after = "\033[24m", collapse = collapse)
|
|
|
|
}
|
|
|
|
font_stripstyle <- function(x) {
|
|
|
|
# from crayon:::ansi_regex
|
|
|
|
gsub("(?:(?:\\x{001b}\\[)|\\x{009b})(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\\x{001b}[A-M]", "", x, perl = TRUE)
|
|
|
|
}
|
|
|
|
|
2020-09-18 16:05:53 +02:00
|
|
|
progress_ticker <- function(n = 1, n_min = 0, ...) {
|
2020-07-29 10:33:47 +02:00
|
|
|
if (!interactive() || n < n_min) {
|
2020-05-16 13:05:47 +02:00
|
|
|
pb <- list()
|
|
|
|
pb$tick <- function() {
|
|
|
|
invisible()
|
|
|
|
}
|
|
|
|
pb$kill <- function() {
|
|
|
|
invisible()
|
|
|
|
}
|
|
|
|
structure(pb, class = "txtProgressBar")
|
2020-07-29 10:33:47 +02:00
|
|
|
} else if (n >= n_min) {
|
|
|
|
pb <- utils::txtProgressBar(max = n, style = 3)
|
|
|
|
pb$tick <- function() {
|
|
|
|
pb$up(pb$getVal() + 1)
|
|
|
|
}
|
|
|
|
pb
|
2020-05-16 13:05:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-28 21:55:47 +02:00
|
|
|
create_pillar_column <- function(x, ...) {
|
|
|
|
new_pillar_shaft_simple <- import_fn("new_pillar_shaft_simple", "pillar", error_on_fail = FALSE)
|
|
|
|
if (!is.null(new_pillar_shaft_simple)) {
|
|
|
|
new_pillar_shaft_simple(x, ...)
|
|
|
|
} else {
|
|
|
|
# does not exist in package 'pillar' anymore
|
|
|
|
structure(list(x),
|
|
|
|
class = "pillar_shaft_simple",
|
|
|
|
...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 08:49:01 +02:00
|
|
|
# copied from vctrs::s3_register by their permission:
|
|
|
|
# https://github.com/r-lib/vctrs/blob/05968ce8e669f73213e3e894b5f4424af4f46316/R/register-s3.R
|
2020-08-28 21:55:47 +02:00
|
|
|
s3_register <- function(generic, class, method = NULL) {
|
|
|
|
stopifnot(is.character(generic), length(generic) == 1)
|
|
|
|
stopifnot(is.character(class), length(class) == 1)
|
|
|
|
pieces <- strsplit(generic, "::")[[1]]
|
|
|
|
stopifnot(length(pieces) == 2)
|
|
|
|
package <- pieces[[1]]
|
|
|
|
generic <- pieces[[2]]
|
|
|
|
caller <- parent.frame()
|
|
|
|
get_method_env <- function() {
|
|
|
|
top <- topenv(caller)
|
|
|
|
if (isNamespace(top)) {
|
|
|
|
asNamespace(environmentName(top))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
caller
|
|
|
|
}
|
|
|
|
}
|
|
|
|
get_method <- function(method, env) {
|
|
|
|
if (is.null(method)) {
|
|
|
|
get(paste0(generic, ".", class), envir = get_method_env())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
method
|
|
|
|
}
|
|
|
|
}
|
|
|
|
method_fn <- get_method(method)
|
|
|
|
stopifnot(is.function(method_fn))
|
|
|
|
setHook(packageEvent(package, "onLoad"), function(...) {
|
|
|
|
ns <- asNamespace(package)
|
|
|
|
method_fn <- get_method(method)
|
|
|
|
registerS3method(generic, class, method_fn, envir = ns)
|
|
|
|
})
|
|
|
|
if (!isNamespaceLoaded(package)) {
|
|
|
|
return(invisible())
|
|
|
|
}
|
|
|
|
envir <- asNamespace(package)
|
|
|
|
if (exists(generic, envir)) {
|
|
|
|
registerS3method(generic, class, method_fn, envir = envir)
|
|
|
|
}
|
|
|
|
invisible()
|
|
|
|
}
|
|
|
|
|
2020-06-22 11:18:40 +02:00
|
|
|
# works exactly like round(), but rounds `round2(44.55, 1)` to 44.6 instead of 44.5
|
2020-05-16 13:05:47 +02:00
|
|
|
# and adds decimal zeroes until `digits` is reached when force_zero = TRUE
|
|
|
|
round2 <- function(x, digits = 0, force_zero = TRUE) {
|
|
|
|
x <- as.double(x)
|
|
|
|
# https://stackoverflow.com/a/12688836/4575331
|
|
|
|
val <- (trunc((abs(x) * 10 ^ digits) + 0.5) / 10 ^ digits) * sign(x)
|
|
|
|
if (digits > 0 & force_zero == TRUE) {
|
|
|
|
values_trans <- val[val != as.integer(val) & !is.na(val)]
|
|
|
|
val[val != as.integer(val) & !is.na(val)] <- paste0(values_trans,
|
|
|
|
strrep("0",
|
|
|
|
max(0,
|
|
|
|
digits - nchar(
|
|
|
|
format(
|
|
|
|
as.double(
|
|
|
|
gsub(".*[.](.*)$",
|
|
|
|
"\\1",
|
|
|
|
values_trans)),
|
|
|
|
scientific = FALSE)))))
|
|
|
|
}
|
|
|
|
as.double(val)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# percentage from our other package: 'cleaner'
|
|
|
|
percentage <- function(x, digits = NULL, ...) {
|
|
|
|
|
|
|
|
# getdecimalplaces() function
|
|
|
|
getdecimalplaces <- function(x, minimum = 0, maximum = 3) {
|
|
|
|
if (maximum < minimum) {
|
|
|
|
maximum <- minimum
|
|
|
|
}
|
|
|
|
if (minimum > maximum) {
|
|
|
|
minimum <- maximum
|
|
|
|
}
|
|
|
|
max_places <- max(unlist(lapply(strsplit(sub("0+$", "",
|
|
|
|
as.character(x * 100)), ".", fixed = TRUE),
|
|
|
|
function(y) ifelse(length(y) == 2, nchar(y[2]), 0))), na.rm = TRUE)
|
|
|
|
max(min(max_places,
|
|
|
|
maximum, na.rm = TRUE),
|
|
|
|
minimum, na.rm = TRUE)
|
|
|
|
}
|
|
|
|
|
|
|
|
# format_percentage() function
|
|
|
|
format_percentage <- function(x, digits = NULL, ...) {
|
|
|
|
if (is.null(digits)) {
|
|
|
|
digits <- getdecimalplaces(x)
|
|
|
|
}
|
|
|
|
|
|
|
|
# round right: percentage(0.4455) and format(as.percentage(0.4455), 1) should return "44.6%", not "44.5%"
|
|
|
|
x_formatted <- format(round2(as.double(x), digits = digits + 2) * 100,
|
|
|
|
scientific = FALSE,
|
|
|
|
digits = digits,
|
|
|
|
nsmall = digits,
|
|
|
|
...)
|
|
|
|
x_formatted <- paste0(x_formatted, "%")
|
|
|
|
x_formatted[!grepl(pattern = "^[0-9.,e-]+$", x = x)] <- NA_character_
|
|
|
|
x_formatted
|
|
|
|
}
|
2020-07-13 09:17:24 +02:00
|
|
|
|
2020-05-16 13:05:47 +02:00
|
|
|
# the actual working part
|
|
|
|
x <- as.double(x)
|
|
|
|
if (is.null(digits)) {
|
|
|
|
# max one digit if undefined
|
|
|
|
digits <- getdecimalplaces(x, minimum = 0, maximum = 1)
|
|
|
|
}
|
|
|
|
format_percentage(structure(.Data = as.double(x),
|
|
|
|
class = c("percentage", "numeric")),
|
|
|
|
digits = digits, ...)
|
|
|
|
}
|
2020-05-18 13:59:34 +02:00
|
|
|
|
|
|
|
# prevent dependency on package 'backports'
|
2020-09-03 12:31:48 +02:00
|
|
|
# these functions were not available in previous versions of R (last checked: R 4.0.2)
|
2020-05-19 12:08:49 +02:00
|
|
|
# see here for the full list: https://github.com/r-lib/backports
|
2020-05-19 13:18:01 +02:00
|
|
|
strrep <- function(x, times) {
|
|
|
|
x <- as.character(x)
|
2020-05-18 13:59:34 +02:00
|
|
|
if (length(x) == 0L)
|
|
|
|
return(x)
|
|
|
|
unlist(.mapply(function(x, times) {
|
|
|
|
if (is.na(x) || is.na(times))
|
|
|
|
return(NA_character_)
|
|
|
|
if (times <= 0L)
|
|
|
|
return("")
|
|
|
|
paste0(replicate(times, x), collapse = "")
|
|
|
|
}, list(x = x, times = times), MoreArgs = list()), use.names = FALSE)
|
|
|
|
}
|
2020-05-19 13:18:01 +02:00
|
|
|
trimws <- function(x, which = c("both", "left", "right")) {
|
|
|
|
which <- match.arg(which)
|
|
|
|
mysub <- function(re, x) sub(re, "", x, perl = TRUE)
|
2020-05-18 13:59:34 +02:00
|
|
|
if (which == "left")
|
|
|
|
return(mysub("^[ \t\r\n]+", x))
|
|
|
|
if (which == "right")
|
|
|
|
return(mysub("[ \t\r\n]+$", x))
|
|
|
|
mysub("[ \t\r\n]+$", mysub("^[ \t\r\n]+", x))
|
|
|
|
}
|
2020-05-19 13:18:01 +02:00
|
|
|
isFALSE <- function(x) {
|
2020-05-18 13:59:34 +02:00
|
|
|
is.logical(x) && length(x) == 1L && !is.na(x) && !x
|
|
|
|
}
|
2020-05-19 13:18:01 +02:00
|
|
|
deparse1 <- function(expr, collapse = " ", width.cutoff = 500L, ...) {
|
2020-05-19 12:08:49 +02:00
|
|
|
paste(deparse(expr, width.cutoff, ...), collapse = collapse)
|
|
|
|
}
|
2020-08-26 15:34:12 +02:00
|
|
|
file.size <- function(...) {
|
2020-09-03 12:31:48 +02:00
|
|
|
file.info(...)$size
|
2020-08-16 21:38:42 +02:00
|
|
|
}
|
2020-08-26 15:34:12 +02:00
|
|
|
file.mtime <- function(...) {
|
2020-09-03 12:31:48 +02:00
|
|
|
file.info(...)$mtime
|
|
|
|
}
|
|
|
|
str2lang <- function(s) {
|
|
|
|
stopifnot(length(s) == 1L)
|
2020-09-14 12:21:23 +02:00
|
|
|
ex <- parse(text = s, keep.source = FALSE)
|
2020-09-03 12:31:48 +02:00
|
|
|
stopifnot(length(ex) == 1L)
|
|
|
|
ex[[1L]]
|
2020-08-16 21:38:42 +02:00
|
|
|
}
|