From 3b14459612d078f64872f480cd05f1eb92d70101 Mon Sep 17 00:00:00 2001 From: gmillot <gael.millot@pasteur.fr> Date: Fri, 3 Feb 2023 20:45:14 +0100 Subject: [PATCH] release v11.6: bug fixed in fun_comp_2d, since identical(data1, data2) returns FALSE if both are identical but data1 is typeof double and data2 is typeof integer --- README.md | 5 + check/TODO.txt | 3 +- cute_little_R_functions.R | 26643 +++++++++++++++++---------------- cute_little_R_functions.docx | Bin 499127 -> 499498 bytes fun_gg_boxplot.docx | Bin 115871 -> 116015 bytes fun_gg_scatter.docx | Bin 121152 -> 121237 bytes 6 files changed, 13334 insertions(+), 13317 deletions(-) diff --git a/README.md b/README.md index ee7618e..f934bd2 100755 --- a/README.md +++ b/README.md @@ -170,6 +170,11 @@ Gitlab developers ## WHAT'S NEW IN +### v11.6.0 + +1) bug fixed in fun_comp_2d, since identical(data1, data2) returns FALSE if both are identical but data1 is typeof double and data2 is typeof integer + + ### v11.5.0 1) modification of ggplot2::guides() in fun_gg_boxplot() and fun_gg_scatter() to do not have the warning message about FALSE replaced by "none" diff --git a/check/TODO.txt b/check/TODO.txt index ce76e92..68e93d7 100644 --- a/check/TODO.txt +++ b/check/TODO.txt @@ -84,7 +84,8 @@ Argument and return : leave it to me For packages : See mails with cuteathon - +For speed, use rbenchmark : +https://towardsdatascience.com/r-is-slow-and-its-your-fault-2fcedacc7abb diff --git a/cute_little_R_functions.R b/cute_little_R_functions.R index 35485dd..0ca1133 100755 --- a/cute_little_R_functions.R +++ b/cute_little_R_functions.R @@ -20,7 +20,7 @@ # https://stackoverflow.com/questions/17552917/merging-existing-pdf-files-using-r # https://www.r-bloggers.com/2019/04/join-split-and-compress-pdf-files-with-pdftools/ # https://rdrr.io/cran/staplr/man/staple_pdf.html - +# For speed, use rbenchmark : https://towardsdatascience.com/r-is-slow-and-its-your-fault-2fcedacc7abb @@ -98,343 +98,343 @@ # -> transferred into the cute package # Do not modify this function in cute_little_R_function anymore. See the cute repo fun_check <- function( -data, -class = NULL, -typeof = NULL, -mode = NULL, -length = NULL, -prop = FALSE, -double.as.integer.allowed = FALSE, -options = NULL, -all.options.in.data = FALSE, -na.contain = FALSE, -neg.values = TRUE, -print = FALSE, -data.name = NULL, -fun.name = NULL + data, + class = NULL, + typeof = NULL, + mode = NULL, + length = NULL, + prop = FALSE, + double.as.integer.allowed = FALSE, + options = NULL, + all.options.in.data = FALSE, + na.contain = FALSE, + neg.values = TRUE, + print = FALSE, + data.name = NULL, + fun.name = NULL ){ -# AIM -# Check the class, type, mode and length of the data argument -# Mainly used to check the arguments of other functions -# Check also other kind of data parameters, is it a proportion? Is it type double but numbers without decimal part? -# If options == NULL, then at least class or type or mode or length argument must be non-null -# If options is non-null, then class, type and mode must be NULL, and length can be NULL or specified -# WARNINGS -# The function tests what is written in its arguments, even if what is written is incoherent. For instance, fun_check(data = factor(1), class = "factor", mode = "character") will return a problem, whatever the object tested in the data argument, because no object can be class "factor" and mode "character" (factors are class "factor" and mode "numeric"). Of note, length of object of class "environment" is always 0 -# If the tested object is NULL, then the function will always return a checking problem -# Since R >= 4.0.0, class(matrix()) returns "matrix" "array", and not "matrix" alone as before. However, use argument class = "matrix" to check for matrix object (of class "matrix" "array" in R >= 4.0.0) and use argument class = "array" to check for array object (of class "array" in R >= 4.0.0) -# ARGUMENTS -# data: object to test -# class: character string. Either one of the class() result (But see the warning section above) or "vector" or "ggplot2" (i.e., objects of class c("gg", "ggplot")) or NULL -# typeof: character string. Either one of the typeof() result or NULL -# mode: character string. Either one of the mode() result (for non-vector object) or NULL -# length: numeric value indicating the length of the object. Not considered if NULL -# prop: logical. Are the numeric values between 0 and 1 (proportion)? If TRUE, can be used alone, without considering class, etc. -# double.as.integer.allowed: logical. If TRUE, no error is reported in the cheking message if argument is set to typeof == "integer" or class == "integer", while the reality is typeof == "double" or class == "numeric" but the numbers strictly have zero as modulo (remainder of a division). This means that i <- 1, which is typeof(i) -> "double" is considered as integer with double.as.integer.allowed = TRUE. WARNING: data%%1 == 0L but not isTRUE(all.equal(data%%1, 0)) is used here because the argument checks for integers stored as double (does not check for decimal numbers that are approximate integers) -# options: a vector of character strings or integers indicating all the possible option values for the data argument, or NULL. Numbers of type "double" are accepted if they have a 0 modulo -# all.options.in.data: logical. If TRUE, all of the options must be present at least once in the data argument, and nothing else. If FALSE, some or all of the options must be present in the data argument, and nothing else. Ignored if options is NULL -# na.contain: logical. Can the data argument contain NA? -# neg.values: logical. Are negative numeric values authorized? Warning: the default setting is TRUE, meaning that, in that case, no check is performed for the presence of negative values. The neg.values argument is activated only when set to FALSE. In addition, (1) neg.values = FALSE can only be used when class, typeof or mode arguments are not NULL, otherwise return an error message, (2) the presence of negative values is not checked with neg.values = FALSE if the tested object is a factor and the following checking message is returned "OBJECT MUST BE MADE OF NON NEGATIVE VALUES BUT IS A FACTOR" -# print: logical. Print the message if $problem is TRUE? Warning: set by default to FALSE, which facilitates the control of the checking message output when using fun_check() inside functions. See the example section -# data.name: character string indicating the name of the object to test. If NULL, use what is assigned to the data argument for the returned message -# fun.name: character string indicating the name of the function checked (i.e., when fun_check() is used to check the arguments of this function). If non-null, the value of fun.name will be added into the message returned by fun_check() -# RETURN -# A list containing: -# $problem: logical. Is there any problem detected? -# $text: message indicating the details of the problem, or the absence of problem -# $object.name: value of the data.name argument (i.e., name of the checked object if provided, NULL otherwise) -# REQUIRED PACKAGES -# None -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# None -# EXAMPLE -# test <- matrix(1:3) ; fun_check(data = test, print = TRUE, class = "vector", mode = "numeric") -# see http -# DEBUGGING -# data = mean ; class = NULL ; typeof = NULL ; mode = NULL ; length = NULL ; prop = FALSE ; double.as.integer.allowed = FALSE ; options = "a" ; all.options.in.data = FALSE ; na.contain = FALSE ; neg.values = TRUE ; print = TRUE ; data.name = NULL ; fun.name = NULL -# function name -# no used in this function for the error message, to avoid env colliding -# end function name -# required function checking -# end required function checking -# reserved words -# end reserved words -# fun.name checked first because required next -if( ! is.null(fun.name)){ # I have to use this way to deal with every kind of class for fun.name -if(all(base::class(fun.name) == "character")){ # all() without na.rm -> ok because class(NA) is "logical" -if(base::length(fun.name) != 1){ -tempo.cat <- paste0("ERROR IN fun_check(): THE fun.name ARGUMENT MUST BE A CHARACTER VECTOR OF LENGTH 1: ", paste(fun.name, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(any(is.na(fun.name))){ # normally no NA with is.na() -tempo.cat <- paste0("ERROR IN fun_check(): NO ARGUMENT EXCEPT data AND options CAN HAVE NA VALUES\nPROBLEMATIC ARGUMENT IS fun.name") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -tempo.cat <- paste0("ERROR IN fun_check(): THE fun.name ARGUMENT MUST BE A CHARACTER VECTOR OF LENGTH 1") # paste(fun.name, collapse = " ") removed here because does not work with objects like function -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end fun.name checked first because required next -# arg with no default values -mandat.args <- c( -"data" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status -# end argument primary checking -# second round of checking and data preparation -# management of special classes -basic.class <- c( -"NULL", # because class(NULL) is "NULL". The NULL aspect will be dealt later -"logical", -"integer", -"numeric", -# "complex", -"character" -# "matrix", -# "array", -# "data.frame", -# "list", -# "factor", -# "table", -# "expression", -# "name", -# "symbol", -# "function", -# "uneval", -# "environment", -# "ggplot2", -# "ggplot_built", -# "call" -) -tempo.arg.base <-c( # no names(formals(fun = sys.function(sys.parent(n = 2)))) used with fun_check() to be sure to deal with the correct environment -"class", -"typeof", -"mode", -"length", -"prop", -"double.as.integer.allowed", -"options", -"all.options.in.data", -"na.contain", -"neg.values", -"print", -"data.name", -"fun.name" -) -tempo.class <-list( # no get() used to be sure to deal with the correct environment -base::class(class), -base::class(typeof), -base::class(mode), -base::class(length), -base::class(prop), -base::class(double.as.integer.allowed), -base::class(options), -base::class(all.options.in.data), -base::class(na.contain), -base::class(neg.values), -base::class(print), -base::class(data.name), -base::class(fun.name) -) -tempo <- ! sapply(lapply(tempo.class, FUN = "%in%", basic.class), FUN = all) -if(any(tempo)){ -tempo.cat1 <- tempo.arg.base[tempo] -tempo.cat2 <- sapply(tempo.class[tempo], FUN = paste0, collapse = " ") -tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": ANY ARGUMENT EXCEPT data MUST HAVE A BASIC CLASS\nPROBLEMATIC ARGUMENT", ifelse(base::length(tempo.cat1) > 1, "S", ""), " AND ASSOCIATED CLASS", ifelse(base::length(tempo.cat1) > 1, "ES ARE", " IS"), ":\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n")) # normally no NA with is.na() -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of special classes -# management of NA arguments -if(any(is.na(data.name)) | any(is.na(class)) | any(is.na(typeof)) | any(is.na(mode)) | any(is.na(length)) | any(is.na(prop)) | any(is.na(double.as.integer.allowed)) | any(is.na(all.options.in.data)) | any(is.na(na.contain)) | any(is.na(neg.values)) | any(is.na(print)) | any(is.na(fun.name))){ # normally no NA with is.na() -tempo <- c("data.name", "class", "typeof", "mode", "length", "prop", "double.as.integer.allowed", "all.options.in.data", "na.contain", "neg.values", "print", "fun.name")[c(any(is.na(data.name)), any(is.na(class)), any(is.na(typeof)), any(is.na(mode)), any(is.na(length)), any(is.na(prop)), any(is.na(double.as.integer.allowed)), any(is.na(all.options.in.data)), any(is.na(na.contain)), any(is.na(neg.values)), any(is.na(print)), any(is.na(fun.name)))] -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": NO ARGUMENT EXCEPT data AND options CAN HAVE NA VALUES\nPROBLEMATIC ARGUMENT", ifelse(length(tempo) > 1, "S ARE", " IS"), ":\n", paste(tempo, collapse = "\n")) # normally no NA with is.na() -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"prop", -"double.as.integer.allowed", -"all.options.in.data", -"na.contain", -"neg.values", -"print" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){ # normally no NA with is.null() -tempo.cat <- paste0("ERROR IN fun.check():\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT BE NULL:\n", paste0(tempo.arg[tempo.log], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# dealing with logical -# tested below -# end dealing with logical -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -# end warning initiation -# other checkings -if( ! is.null(data.name)){ -if( ! (base::length(data.name) == 1L & all(base::class(data.name) == "character"))){ # all() without na.rm -> ok because class(NA) is "logical" -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": data.name ARGUMENT MUST BE A SINGLE CHARACTER ELEMENT AND NOT ", paste(data.name, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(is.null(options) & is.null(class) & is.null(typeof) & is.null(mode) & prop == FALSE & is.null(length)){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": AT LEAST ONE OF THE options, class, typeof, mode, prop, OR length ARGUMENT MUST BE SPECIFIED (I.E, TRUE FOR prop)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(options) & ( ! is.null(class) | ! is.null(typeof) | ! is.null(mode) | prop == TRUE)){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE class, typeof, mode ARGUMENTS MUST BE NULL, AND prop FALSE, IF THE options ARGUMENT IS SPECIFIED\nTHE options ARGUMENT MUST BE NULL IF THE class AND/OR typeof AND/OR mode AND/OR prop ARGUMENT IS SPECIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (all(base::class(neg.values) == "logical") & base::length(neg.values) == 1L)){ # all() without na.rm -> ok because class(NA) is "logical" -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE neg.values ARGUMENT MUST BE TRUE OR FALSE ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(neg.values == FALSE & is.null(class) & is.null(typeof) & is.null(mode)){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE neg.values ARGUMENT CANNOT BE SWITCHED TO FALSE IF class, typeof AND mode ARGUMENTS ARE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(class)){ # may add "formula" and "Date" as in https://renenyffenegger.ch/notes/development/languages/R/functions/class -if( ! all(class %in% c("vector", "logical", "integer", "numeric", "complex", "character", "matrix", "array", "data.frame", "list", "factor", "table", "expression", "name", "symbol", "function", "uneval", "environment", "ggplot2", "ggplot_built", "call") & base::length(class) == 1L)){ # length == 1L here because of class(matrix()) since R4.0.0 # all() without na.rm -> ok because class cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT MUST BE ONE OF THESE VALUE:\n\"vector\", \"logical\", \"integer\", \"numeric\", \"complex\", \"character\", \"matrix\", \"array\", \"data.frame\", \"list\", \"factor\", \"table\", \"expression\", \"name\", \"symbol\", \"function\", \"environment\", \"ggplot2\", \"ggplot_built\", \"call\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(neg.values == FALSE & ! any(class %in% c("vector", "numeric", "integer", "matrix", "array", "data.frame", "table"))){ # no need of na.rm = TRUE for any() because %in% does not output NA -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT CANNOT BE OTHER THAN \"vector\", \"numeric\", \"integer\", \"matrix\", \"array\", \"data.frame\", \"table\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(typeof)){ # all the types are here: https://renenyffenegger.ch/notes/development/languages/R/functions/typeof -if( ! (all(typeof %in% c("logical", "integer", "double", "complex", "character", "list", "expression", "symbol", "closure", "special", "builtin", "environment", "S4", "language")) & base::length(typeof) == 1L)){ # "language" is the type of object of class "call" # all() without na.rm -> ok because typeof cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT MUST BE ONE OF THESE VALUE:\n\"logical\", \"integer\", \"double\", \"complex\", \"character\", \"list\", \"expression\", \"name\", \"symbol\", \"closure\", \"special\", \"builtin\", \"environment\", \"S4\", \"language\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(neg.values == FALSE & ! typeof %in% c("double", "integer")){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT CANNOT BE OTHER THAN \"double\" OR \"integer\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(mode)){ # all the types are here: https://renenyffenegger.ch/notes/development/languages/R/functions/typeof -if( ! (all(mode %in% c("logical", "numeric", "complex", "character", "list", "expression", "name", "symbol", "function", "environment", "S4", "call")) & base::length(mode) == 1L)){ # all() without na.rm -> ok because mode cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT MUST BE ONE OF THESE VALUE:\n\"logical\", \"numeric\", \"complex\", \"character\", \"list\", \"expression\", \"name\", \"symbol\", \"function\", \"environment\", \"S4\", \"call\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(neg.values == FALSE & mode != "numeric"){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT CANNOT BE OTHER THAN \"numeric\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(length)){ -if( ! (is.numeric(length) & base::length(length) == 1L & all( ! grepl(length, pattern = "\\.")))){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": length ARGUMENT MUST BE A SINGLE INTEGER VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! (is.logical(prop) & base::length(prop) == 1L)){ # is.na() already checked for prop -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": prop ARGUMENT MUST BE TRUE OR FALSE ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(prop == TRUE){ -if( ! is.null(class)){ -if( ! any(class %in% c("vector", "numeric", "matrix", "array", "data.frame", "table"))){ # no need of na.rm = TRUE for any() because %in% does not output NA -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT CANNOT BE OTHER THAN NULL, \"vector\", \"numeric\", \"matrix\", \"array\", \"data.frame\", \"table\" IF prop ARGUMENT IS TRUE") # not integer because prop -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(mode)){ -if(mode != "numeric"){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT CANNOT BE OTHER THAN NULL OR \"numeric\" IF prop ARGUMENT IS TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(typeof)){ -if(typeof != "double"){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT CANNOT BE OTHER THAN NULL OR \"double\" IF prop ARGUMENT IS TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -} -if( ! (all(base::class(double.as.integer.allowed) == "logical") & base::length(double.as.integer.allowed) == 1L)){ # all() without na.rm -> ok because class() never returns NA -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE double.as.integer.allowed ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(double.as.integer.allowed, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (is.logical(all.options.in.data) & base::length(all.options.in.data) == 1L)){ -tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": all.options.in.data ARGUMENT MUST BE A SINGLE LOGICAL VALUE (TRUE OR FALSE ONLY): ", paste(all.options.in.data, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (all(base::class(na.contain) == "logical") & base::length(na.contain) == 1L)){ # all() without na.rm -> ok because class() never returns NA -tempo.cat <- paste0("ERROR IN fun_check(): THE na.contain ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(na.contain, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (all(base::class(print) == "logical") & base::length(print) == 1L)){ # all() without na.rm -> ok because class() never returns NA -tempo.cat <- paste0("ERROR IN fun_check(): THE print ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(print, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# data.name and fun.name tested at the beginning -# end other checkings -# end second round of checking and data preparation -# package checking -# end package checking -# main code -if(is.null(data.name)){ -data.name <- deparse(substitute(data)) -} -problem <- FALSE -text <- paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT") -if(( ! is.null(options)) & (all(base::typeof(data) == "character") | all(base::typeof(data) == "integer") | all(base::typeof(data) == "double"))){ # all() without na.rm -> ok because typeof() never returns NA -if(all(base::typeof(data) == "double")){ -if( ! all(data %% 1 == 0L, na.rm = TRUE)){ -problem <- TRUE -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nBUT IS NOT EVEN TYPE CHARACTER OR INTEGER") -} -}else{ -text <- "" -if( ! all(data %in% options)){ # no need of na.rm = TRUE for all() because %in% does not output NA -problem <- TRUE -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nTHE PROBLEMATIC ELEMENTS OF ", data.name, " ARE: ", paste(unique(data[ ! (data %in% options)]), collapse = " ")) -} -if(all.options.in.data == TRUE){ -if( ! all(options %in% data)){ # no need of na.rm = TRUE for all() because %in% does not output NA -problem <- TRUE -text <- paste0(ifelse(text == "", "", paste0(text, "\n")), ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE MADE OF ALL THESE OPTIONS: ", paste(options, collapse = " "), "\nTHE MISSING ELEMENTS OF THE options ARGUMENT ARE: ", paste(unique(options[ ! (options %in% data)]), collapse = " ")) -} -} -if( ! is.null(length)){ -if(base::length(data) != length){ -problem <- TRUE -text <- paste0(ifelse(text == "", "", paste0(text, "\n")), ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE LENGTH OF ", data.name, " MUST BE ", length, " AND NOT ", base::length(data)) -} -} -if(text == ""){ -text <- paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT") -} -} -}else if( ! is.null(options)){ -problem <- TRUE -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nBUT IS NOT EVEN TYPE CHARACTER OR INTEGER") -} -arg.names <- c("class", "typeof", "mode", "length") -if( ! is.null(class)){ -if(class == "matrix"){ # because of class(matric()) since R4.0.0 -class <- c("matrix", "array") -}else if(class == "factor" & all(base::class(data) %in% c("factor", "ordered"))){ # to deal with ordered factors # all() without na.rm -> ok because class(NA) is "logical" -class <- c("factor", "ordered") -} -} -if(is.null(options)){ -for(i2 in 1:base::length(arg.names)){ -if( ! is.null(get(arg.names[i2], env = sys.nframe(), inherit = FALSE))){ -# script to execute -tempo.script <- ' + # AIM + # Check the class, type, mode and length of the data argument + # Mainly used to check the arguments of other functions + # Check also other kind of data parameters, is it a proportion? Is it type double but numbers without decimal part? + # If options == NULL, then at least class or type or mode or length argument must be non-null + # If options is non-null, then class, type and mode must be NULL, and length can be NULL or specified + # WARNINGS + # The function tests what is written in its arguments, even if what is written is incoherent. For instance, fun_check(data = factor(1), class = "factor", mode = "character") will return a problem, whatever the object tested in the data argument, because no object can be class "factor" and mode "character" (factors are class "factor" and mode "numeric"). Of note, length of object of class "environment" is always 0 + # If the tested object is NULL, then the function will always return a checking problem + # Since R >= 4.0.0, class(matrix()) returns "matrix" "array", and not "matrix" alone as before. However, use argument class = "matrix" to check for matrix object (of class "matrix" "array" in R >= 4.0.0) and use argument class = "array" to check for array object (of class "array" in R >= 4.0.0) + # ARGUMENTS + # data: object to test + # class: character string. Either one of the class() result (But see the warning section above) or "vector" or "ggplot2" (i.e., objects of class c("gg", "ggplot")) or NULL + # typeof: character string. Either one of the typeof() result or NULL + # mode: character string. Either one of the mode() result (for non-vector object) or NULL + # length: numeric value indicating the length of the object. Not considered if NULL + # prop: logical. Are the numeric values between 0 and 1 (proportion)? If TRUE, can be used alone, without considering class, etc. + # double.as.integer.allowed: logical. If TRUE, no error is reported in the cheking message if argument is set to typeof == "integer" or class == "integer", while the reality is typeof == "double" or class == "numeric" but the numbers strictly have zero as modulo (remainder of a division). This means that i <- 1, which is typeof(i) -> "double" is considered as integer with double.as.integer.allowed = TRUE. WARNING: data%%1 == 0L but not isTRUE(all.equal(data%%1, 0)) is used here because the argument checks for integers stored as double (does not check for decimal numbers that are approximate integers) + # options: a vector of character strings or integers indicating all the possible option values for the data argument, or NULL. Numbers of type "double" are accepted if they have a 0 modulo + # all.options.in.data: logical. If TRUE, all of the options must be present at least once in the data argument, and nothing else. If FALSE, some or all of the options must be present in the data argument, and nothing else. Ignored if options is NULL + # na.contain: logical. Can the data argument contain NA? + # neg.values: logical. Are negative numeric values authorized? Warning: the default setting is TRUE, meaning that, in that case, no check is performed for the presence of negative values. The neg.values argument is activated only when set to FALSE. In addition, (1) neg.values = FALSE can only be used when class, typeof or mode arguments are not NULL, otherwise return an error message, (2) the presence of negative values is not checked with neg.values = FALSE if the tested object is a factor and the following checking message is returned "OBJECT MUST BE MADE OF NON NEGATIVE VALUES BUT IS A FACTOR" + # print: logical. Print the message if $problem is TRUE? Warning: set by default to FALSE, which facilitates the control of the checking message output when using fun_check() inside functions. See the example section + # data.name: character string indicating the name of the object to test. If NULL, use what is assigned to the data argument for the returned message + # fun.name: character string indicating the name of the function checked (i.e., when fun_check() is used to check the arguments of this function). If non-null, the value of fun.name will be added into the message returned by fun_check() + # RETURN + # A list containing: + # $problem: logical. Is there any problem detected? + # $text: message indicating the details of the problem, or the absence of problem + # $object.name: value of the data.name argument (i.e., name of the checked object if provided, NULL otherwise) + # REQUIRED PACKAGES + # None + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # None + # EXAMPLE + # test <- matrix(1:3) ; fun_check(data = test, print = TRUE, class = "vector", mode = "numeric") + # see http + # DEBUGGING + # data = mean ; class = NULL ; typeof = NULL ; mode = NULL ; length = NULL ; prop = FALSE ; double.as.integer.allowed = FALSE ; options = "a" ; all.options.in.data = FALSE ; na.contain = FALSE ; neg.values = TRUE ; print = TRUE ; data.name = NULL ; fun.name = NULL + # function name + # no used in this function for the error message, to avoid env colliding + # end function name + # required function checking + # end required function checking + # reserved words + # end reserved words + # fun.name checked first because required next + if( ! is.null(fun.name)){ # I have to use this way to deal with every kind of class for fun.name + if(all(base::class(fun.name) == "character")){ # all() without na.rm -> ok because class(NA) is "logical" + if(base::length(fun.name) != 1){ + tempo.cat <- paste0("ERROR IN fun_check(): THE fun.name ARGUMENT MUST BE A CHARACTER VECTOR OF LENGTH 1: ", paste(fun.name, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(any(is.na(fun.name))){ # normally no NA with is.na() + tempo.cat <- paste0("ERROR IN fun_check(): NO ARGUMENT EXCEPT data AND options CAN HAVE NA VALUES\nPROBLEMATIC ARGUMENT IS fun.name") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + tempo.cat <- paste0("ERROR IN fun_check(): THE fun.name ARGUMENT MUST BE A CHARACTER VECTOR OF LENGTH 1") # paste(fun.name, collapse = " ") removed here because does not work with objects like function + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end fun.name checked first because required next + # arg with no default values + mandat.args <- c( + "data" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status + # end argument primary checking + # second round of checking and data preparation + # management of special classes + basic.class <- c( + "NULL", # because class(NULL) is "NULL". The NULL aspect will be dealt later + "logical", + "integer", + "numeric", + # "complex", + "character" + # "matrix", + # "array", + # "data.frame", + # "list", + # "factor", + # "table", + # "expression", + # "name", + # "symbol", + # "function", + # "uneval", + # "environment", + # "ggplot2", + # "ggplot_built", + # "call" + ) + tempo.arg.base <-c( # no names(formals(fun = sys.function(sys.parent(n = 2)))) used with fun_check() to be sure to deal with the correct environment + "class", + "typeof", + "mode", + "length", + "prop", + "double.as.integer.allowed", + "options", + "all.options.in.data", + "na.contain", + "neg.values", + "print", + "data.name", + "fun.name" + ) + tempo.class <-list( # no get() used to be sure to deal with the correct environment + base::class(class), + base::class(typeof), + base::class(mode), + base::class(length), + base::class(prop), + base::class(double.as.integer.allowed), + base::class(options), + base::class(all.options.in.data), + base::class(na.contain), + base::class(neg.values), + base::class(print), + base::class(data.name), + base::class(fun.name) + ) + tempo <- ! sapply(lapply(tempo.class, FUN = "%in%", basic.class), FUN = all) + if(any(tempo)){ + tempo.cat1 <- tempo.arg.base[tempo] + tempo.cat2 <- sapply(tempo.class[tempo], FUN = paste0, collapse = " ") + tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": ANY ARGUMENT EXCEPT data MUST HAVE A BASIC CLASS\nPROBLEMATIC ARGUMENT", ifelse(base::length(tempo.cat1) > 1, "S", ""), " AND ASSOCIATED CLASS", ifelse(base::length(tempo.cat1) > 1, "ES ARE", " IS"), ":\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n")) # normally no NA with is.na() + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of special classes + # management of NA arguments + if(any(is.na(data.name)) | any(is.na(class)) | any(is.na(typeof)) | any(is.na(mode)) | any(is.na(length)) | any(is.na(prop)) | any(is.na(double.as.integer.allowed)) | any(is.na(all.options.in.data)) | any(is.na(na.contain)) | any(is.na(neg.values)) | any(is.na(print)) | any(is.na(fun.name))){ # normally no NA with is.na() + tempo <- c("data.name", "class", "typeof", "mode", "length", "prop", "double.as.integer.allowed", "all.options.in.data", "na.contain", "neg.values", "print", "fun.name")[c(any(is.na(data.name)), any(is.na(class)), any(is.na(typeof)), any(is.na(mode)), any(is.na(length)), any(is.na(prop)), any(is.na(double.as.integer.allowed)), any(is.na(all.options.in.data)), any(is.na(na.contain)), any(is.na(neg.values)), any(is.na(print)), any(is.na(fun.name)))] + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": NO ARGUMENT EXCEPT data AND options CAN HAVE NA VALUES\nPROBLEMATIC ARGUMENT", ifelse(length(tempo) > 1, "S ARE", " IS"), ":\n", paste(tempo, collapse = "\n")) # normally no NA with is.na() + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "prop", + "double.as.integer.allowed", + "all.options.in.data", + "na.contain", + "neg.values", + "print" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){ # normally no NA with is.null() + tempo.cat <- paste0("ERROR IN fun.check():\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT BE NULL:\n", paste0(tempo.arg[tempo.log], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # dealing with logical + # tested below + # end dealing with logical + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + # end warning initiation + # other checkings + if( ! is.null(data.name)){ + if( ! (base::length(data.name) == 1L & all(base::class(data.name) == "character"))){ # all() without na.rm -> ok because class(NA) is "logical" + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": data.name ARGUMENT MUST BE A SINGLE CHARACTER ELEMENT AND NOT ", paste(data.name, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(is.null(options) & is.null(class) & is.null(typeof) & is.null(mode) & prop == FALSE & is.null(length)){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": AT LEAST ONE OF THE options, class, typeof, mode, prop, OR length ARGUMENT MUST BE SPECIFIED (I.E, TRUE FOR prop)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(options) & ( ! is.null(class) | ! is.null(typeof) | ! is.null(mode) | prop == TRUE)){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE class, typeof, mode ARGUMENTS MUST BE NULL, AND prop FALSE, IF THE options ARGUMENT IS SPECIFIED\nTHE options ARGUMENT MUST BE NULL IF THE class AND/OR typeof AND/OR mode AND/OR prop ARGUMENT IS SPECIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (all(base::class(neg.values) == "logical") & base::length(neg.values) == 1L)){ # all() without na.rm -> ok because class(NA) is "logical" + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE neg.values ARGUMENT MUST BE TRUE OR FALSE ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(neg.values == FALSE & is.null(class) & is.null(typeof) & is.null(mode)){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE neg.values ARGUMENT CANNOT BE SWITCHED TO FALSE IF class, typeof AND mode ARGUMENTS ARE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(class)){ # may add "formula" and "Date" as in https://renenyffenegger.ch/notes/development/languages/R/functions/class + if( ! all(class %in% c("vector", "logical", "integer", "numeric", "complex", "character", "matrix", "array", "data.frame", "list", "factor", "table", "expression", "name", "symbol", "function", "uneval", "environment", "ggplot2", "ggplot_built", "call") & base::length(class) == 1L)){ # length == 1L here because of class(matrix()) since R4.0.0 # all() without na.rm -> ok because class cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT MUST BE ONE OF THESE VALUE:\n\"vector\", \"logical\", \"integer\", \"numeric\", \"complex\", \"character\", \"matrix\", \"array\", \"data.frame\", \"list\", \"factor\", \"table\", \"expression\", \"name\", \"symbol\", \"function\", \"environment\", \"ggplot2\", \"ggplot_built\", \"call\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(neg.values == FALSE & ! any(class %in% c("vector", "numeric", "integer", "matrix", "array", "data.frame", "table"))){ # no need of na.rm = TRUE for any() because %in% does not output NA + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT CANNOT BE OTHER THAN \"vector\", \"numeric\", \"integer\", \"matrix\", \"array\", \"data.frame\", \"table\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(typeof)){ # all the types are here: https://renenyffenegger.ch/notes/development/languages/R/functions/typeof + if( ! (all(typeof %in% c("logical", "integer", "double", "complex", "character", "list", "expression", "symbol", "closure", "special", "builtin", "environment", "S4", "language")) & base::length(typeof) == 1L)){ # "language" is the type of object of class "call" # all() without na.rm -> ok because typeof cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT MUST BE ONE OF THESE VALUE:\n\"logical\", \"integer\", \"double\", \"complex\", \"character\", \"list\", \"expression\", \"name\", \"symbol\", \"closure\", \"special\", \"builtin\", \"environment\", \"S4\", \"language\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(neg.values == FALSE & ! typeof %in% c("double", "integer")){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT CANNOT BE OTHER THAN \"double\" OR \"integer\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(mode)){ # all the types are here: https://renenyffenegger.ch/notes/development/languages/R/functions/typeof + if( ! (all(mode %in% c("logical", "numeric", "complex", "character", "list", "expression", "name", "symbol", "function", "environment", "S4", "call")) & base::length(mode) == 1L)){ # all() without na.rm -> ok because mode cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT MUST BE ONE OF THESE VALUE:\n\"logical\", \"numeric\", \"complex\", \"character\", \"list\", \"expression\", \"name\", \"symbol\", \"function\", \"environment\", \"S4\", \"call\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(neg.values == FALSE & mode != "numeric"){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT CANNOT BE OTHER THAN \"numeric\" IF neg.values ARGUMENT IS SWITCHED TO FALSE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(length)){ + if( ! (is.numeric(length) & base::length(length) == 1L & all( ! grepl(length, pattern = "\\.")))){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": length ARGUMENT MUST BE A SINGLE INTEGER VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! (is.logical(prop) & base::length(prop) == 1L)){ # is.na() already checked for prop + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": prop ARGUMENT MUST BE TRUE OR FALSE ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(prop == TRUE){ + if( ! is.null(class)){ + if( ! any(class %in% c("vector", "numeric", "matrix", "array", "data.frame", "table"))){ # no need of na.rm = TRUE for any() because %in% does not output NA + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": class ARGUMENT CANNOT BE OTHER THAN NULL, \"vector\", \"numeric\", \"matrix\", \"array\", \"data.frame\", \"table\" IF prop ARGUMENT IS TRUE") # not integer because prop + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(mode)){ + if(mode != "numeric"){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": mode ARGUMENT CANNOT BE OTHER THAN NULL OR \"numeric\" IF prop ARGUMENT IS TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(typeof)){ + if(typeof != "double"){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": typeof ARGUMENT CANNOT BE OTHER THAN NULL OR \"double\" IF prop ARGUMENT IS TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + } + if( ! (all(base::class(double.as.integer.allowed) == "logical") & base::length(double.as.integer.allowed) == 1L)){ # all() without na.rm -> ok because class() never returns NA + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": THE double.as.integer.allowed ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(double.as.integer.allowed, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (is.logical(all.options.in.data) & base::length(all.options.in.data) == 1L)){ + tempo.cat <- paste0("ERROR IN fun_check()", ifelse(is.null(fun.name), "", paste0(" INSIDE ", fun.name)), ": all.options.in.data ARGUMENT MUST BE A SINGLE LOGICAL VALUE (TRUE OR FALSE ONLY): ", paste(all.options.in.data, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (all(base::class(na.contain) == "logical") & base::length(na.contain) == 1L)){ # all() without na.rm -> ok because class() never returns NA + tempo.cat <- paste0("ERROR IN fun_check(): THE na.contain ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(na.contain, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (all(base::class(print) == "logical") & base::length(print) == 1L)){ # all() without na.rm -> ok because class() never returns NA + tempo.cat <- paste0("ERROR IN fun_check(): THE print ARGUMENT MUST BE TRUE OR FALSE ONLY: ", paste(print, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # data.name and fun.name tested at the beginning + # end other checkings + # end second round of checking and data preparation + # package checking + # end package checking + # main code + if(is.null(data.name)){ + data.name <- deparse(substitute(data)) + } + problem <- FALSE + text <- paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT") + if(( ! is.null(options)) & (all(base::typeof(data) == "character") | all(base::typeof(data) == "integer") | all(base::typeof(data) == "double"))){ # all() without na.rm -> ok because typeof() never returns NA + if(all(base::typeof(data) == "double")){ + if( ! all(data %% 1 == 0L, na.rm = TRUE)){ + problem <- TRUE + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nBUT IS NOT EVEN TYPE CHARACTER OR INTEGER") + } + }else{ + text <- "" + if( ! all(data %in% options)){ # no need of na.rm = TRUE for all() because %in% does not output NA + problem <- TRUE + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nTHE PROBLEMATIC ELEMENTS OF ", data.name, " ARE: ", paste(unique(data[ ! (data %in% options)]), collapse = " ")) + } + if(all.options.in.data == TRUE){ + if( ! all(options %in% data)){ # no need of na.rm = TRUE for all() because %in% does not output NA + problem <- TRUE + text <- paste0(ifelse(text == "", "", paste0(text, "\n")), ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE MADE OF ALL THESE OPTIONS: ", paste(options, collapse = " "), "\nTHE MISSING ELEMENTS OF THE options ARGUMENT ARE: ", paste(unique(options[ ! (options %in% data)]), collapse = " ")) + } + } + if( ! is.null(length)){ + if(base::length(data) != length){ + problem <- TRUE + text <- paste0(ifelse(text == "", "", paste0(text, "\n")), ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE LENGTH OF ", data.name, " MUST BE ", length, " AND NOT ", base::length(data)) + } + } + if(text == ""){ + text <- paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT") + } + } + }else if( ! is.null(options)){ + problem <- TRUE + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE SOME OF THESE OPTIONS: ", paste(options, collapse = " "), "\nBUT IS NOT EVEN TYPE CHARACTER OR INTEGER") + } + arg.names <- c("class", "typeof", "mode", "length") + if( ! is.null(class)){ + if(class == "matrix"){ # because of class(matric()) since R4.0.0 + class <- c("matrix", "array") + }else if(class == "factor" & all(base::class(data) %in% c("factor", "ordered"))){ # to deal with ordered factors # all() without na.rm -> ok because class(NA) is "logical" + class <- c("factor", "ordered") + } + } + if(is.null(options)){ + for(i2 in 1:base::length(arg.names)){ + if( ! is.null(get(arg.names[i2], env = sys.nframe(), inherit = FALSE))){ + # script to execute + tempo.script <- ' problem <- TRUE ; if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": THE ", data.name, " OBJECT MUST BE ") ; @@ -443,75 +443,75 @@ text <- paste0(text, " AND ") ; } text <- paste0(text, toupper(arg.names[i2]), " ", if(all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) %in% c("matrix", "array"))){"matrix"}else if(all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) %in% c("factor", "ordered"))){"factor"}else{get(arg.names[i2], env = sys.nframe(), inherit = FALSE)}) ' # no need of na.rm = TRUE for all() because %in% does not output NA -# end script to execute -if(base::typeof(data) == "double" & double.as.integer.allowed == TRUE & ((arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "integer")) | (arg.names[i2] == "typeof" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "integer")))){ # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names # typeof(data) == "double" means no factor allowed -if( ! all(data %% 1 == 0L, na.rm = TRUE)){ # to check integers (use %%, meaning the remaining of a division): see the precedent line. isTRUE(all.equal(data%%1, rep(0, length(data)))) not used because we strictly need zero as a result. Warning: na.rm = TRUE required here for all() -eval(parse(text = tempo.script)) # execute tempo.script -} -}else if( ! any(all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) %in% c("vector", "ggplot2"))) & ! all(eval(parse(text = paste0(arg.names[i2], "(data)"))) %in% get(arg.names[i2], env = sys.nframe(), inherit = FALSE))){ # test the four c("class", "typeof", "mode", "length") arguments with their corresponding function. No need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for any() because get get(arg.names) does not contain NA -eval(parse(text = tempo.script)) # execute tempo.script -}else if(arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "vector") & ! (all(base::class(data) %in% "numeric") | all(base::class(data) %in% "integer") | all(base::class(data) %in% "character") | all(base::class(data) %in% "logical"))){ # test class == "vector". No need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names -eval(parse(text = tempo.script)) # execute tempo.script -}else if(arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "ggplot2") & ! all(base::class(data) %in% c("gg", "ggplot"))){ # test ggplot object # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names # no need of na.rm = TRUE for all() because %in% does not output NA -eval(parse(text = tempo.script)) # execute tempo.script -} -} -} -} + # end script to execute + if(base::typeof(data) == "double" & double.as.integer.allowed == TRUE & ((arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "integer")) | (arg.names[i2] == "typeof" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "integer")))){ # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names # typeof(data) == "double" means no factor allowed + if( ! all(data %% 1 == 0L, na.rm = TRUE)){ # to check integers (use %%, meaning the remaining of a division): see the precedent line. isTRUE(all.equal(data%%1, rep(0, length(data)))) not used because we strictly need zero as a result. Warning: na.rm = TRUE required here for all() + eval(parse(text = tempo.script)) # execute tempo.script + } + }else if( ! any(all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) %in% c("vector", "ggplot2"))) & ! all(eval(parse(text = paste0(arg.names[i2], "(data)"))) %in% get(arg.names[i2], env = sys.nframe(), inherit = FALSE))){ # test the four c("class", "typeof", "mode", "length") arguments with their corresponding function. No need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for any() because get get(arg.names) does not contain NA + eval(parse(text = tempo.script)) # execute tempo.script + }else if(arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "vector") & ! (all(base::class(data) %in% "numeric") | all(base::class(data) %in% "integer") | all(base::class(data) %in% "character") | all(base::class(data) %in% "logical"))){ # test class == "vector". No need of na.rm = TRUE for all() because %in% does not output NA # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names + eval(parse(text = tempo.script)) # execute tempo.script + }else if(arg.names[i2] == "class" & all(get(arg.names[i2], env = sys.nframe(), inherit = FALSE) == "ggplot2") & ! all(base::class(data) %in% c("gg", "ggplot"))){ # test ggplot object # no need of na.rm = TRUE for all() because == does not output NA if no NA in left of ==, which is the case for arg.names # no need of na.rm = TRUE for all() because %in% does not output NA + eval(parse(text = tempo.script)) # execute tempo.script + } + } + } + } if(prop == TRUE & all(base::typeof(data) == "double")){ # all() without na.rm -> ok because typeof(NA) is "logical" -if(is.null(data) | any(data < 0 | data > 1, na.rm = TRUE)){ # works if data is NULL # Warning: na.rm = TRUE required here for any() # typeof(data) == "double" means no factor allowed -problem <- TRUE -if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") -}else{ -text <- paste0(text, " AND ") -} -text <- paste0(text, "THE ", data.name, " OBJECT MUST BE DECIMAL VALUES BETWEEN 0 AND 1") -} + if(is.null(data) | any(data < 0 | data > 1, na.rm = TRUE)){ # works if data is NULL # Warning: na.rm = TRUE required here for any() # typeof(data) == "double" means no factor allowed + problem <- TRUE + if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") + }else{ + text <- paste0(text, " AND ") + } + text <- paste0(text, "THE ", data.name, " OBJECT MUST BE DECIMAL VALUES BETWEEN 0 AND 1") + } }else if(prop == TRUE){ -problem <- TRUE -if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") -}else{ -text <- paste0(text, " AND ") -} -text <- paste0(text, "THE ", data.name, " OBJECT MUST BE DECIMAL VALUES BETWEEN 0 AND 1") + problem <- TRUE + if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") + }else{ + text <- paste0(text, " AND ") + } + text <- paste0(text, "THE ", data.name, " OBJECT MUST BE DECIMAL VALUES BETWEEN 0 AND 1") } if(all(base::class(data) %in% "expression")){ # no need of na.rm = TRUE for all() because %in% does not output NA -data <- as.character(data) # to evaluate the presence of NA + data <- as.character(data) # to evaluate the presence of NA } if(na.contain == FALSE & (base::mode(data) %in% c("logical", "numeric", "complex", "character", "list"))){ # before it was ! (class(data) %in% c("function", "environment")) -if(any(is.na(data)) == TRUE){ # not on the same line because when data is class envir or function , do not like that # normally no NA with is.na() -problem <- TRUE -if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") -}else{ -text <- paste0(text, " AND ") -} -text <- paste0(text, "THE ", data.name, " OBJECT CONTAINS NA WHILE NOT AUTHORIZED") -} + if(any(is.na(data)) == TRUE){ # not on the same line because when data is class envir or function , do not like that # normally no NA with is.na() + problem <- TRUE + if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") + }else{ + text <- paste0(text, " AND ") + } + text <- paste0(text, "THE ", data.name, " OBJECT CONTAINS NA WHILE NOT AUTHORIZED") + } } if(neg.values == FALSE & all(base::mode(data) %in% "numeric") & ! any(base::class(data) %in% "factor")){ # no need of na.rm = TRUE for all() because %in% does not output NA -if(any(data < 0, na.rm = TRUE)){ # Warning: na.rm = TRUE required here for any() -problem <- TRUE -if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") -}else{ -text <- paste0(text, " AND ") -} -text <- paste0(text, "THE ", data.name, " OBJECT MUST BE MADE OF NON NEGATIVE NUMERIC VALUES") -} + if(any(data < 0, na.rm = TRUE)){ # Warning: na.rm = TRUE required here for any() + problem <- TRUE + if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") + }else{ + text <- paste0(text, " AND ") + } + text <- paste0(text, "THE ", data.name, " OBJECT MUST BE MADE OF NON NEGATIVE NUMERIC VALUES") + } }else if(neg.values == FALSE){ -problem <- TRUE -if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ -text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") -}else{ -text <- paste0(text, " AND ") -} -text <- paste0(text, "THE ", data.name, " OBJECT MUST BE MADE OF NON NEGATIVE VALUES BUT IS ", ifelse(any(base::class(data) %in% "factor"), "A FACTOR", "NOT EVEN MODE NUMERIC")) + problem <- TRUE + if(identical(text, paste0(ifelse(is.null(fun.name), "", paste0("IN ", fun.name, ": ")), "NO PROBLEM DETECTED FOR THE ", data.name, " OBJECT"))){ + text <- paste0(ifelse(is.null(fun.name), "ERROR", paste0("ERROR IN ", fun.name)), ": ") + }else{ + text <- paste0(text, " AND ") + } + text <- paste0(text, "THE ", data.name, " OBJECT MUST BE MADE OF NON NEGATIVE VALUES BUT IS ", ifelse(any(base::class(data) %in% "factor"), "A FACTOR", "NOT EVEN MODE NUMERIC")) } if(print == TRUE & problem == TRUE){ -cat(paste0("\n\n================\n\n", text, "\n\n================\n\n")) + cat(paste0("\n\n================\n\n", text, "\n\n================\n\n")) } # output output <- list(problem = problem, text = text, object.name = data.name) @@ -525,104 +525,104 @@ return(output) fun_secu <- function(pos = 1, name = NULL){ -# AIM -# Verify that variables in the environment defined by the pos parameter are not present in the above environment (following R Scope). This can be used to avoid R scope preference of functions like get() -# ARGUMENTS -# pos: single integer indicating the position of the environment checked (argument n of parent.frame()). Value 1 means one step above the fun_secu() local environment (by default). This means that when fun_secu(pos = 1) is used inside a function A, it checks if variables in the local environment of this function A are also present in above environments (following R Scope). When fun_secu(pos = 1) is used in the Global environment, it checks the objects of this environment -# name: single character string indicating the name of the function checked. If NULL, fun_secu() checks all the variables of the environment indicated by pos, as explained in the pos argument description. If non-null, fun_secu() checks all the variables presents in the local env of the function will be checked in the above envs (which includes the working environment (Global env) -# RETURN -# A character string of the local variables that match variables in the different environments of the R scope, or NULL if no match -# REQUIRED PACKAGES -# None -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_secu() -# fun_secu(pos = 2) -# mean <- 0 ; fun1 <- function(){sd <- 1 ; fun_secu(name = as.character(sys.calls()[[length(sys.calls())]]))} ; fun2 <- function(){cor <- 2 ; fun1()} ; fun1() ; fun2() ; rm(mean) # sys.calls() gives the function name at top stack of the imbricated functions, sys.calls()[[length(sys.calls())]] the name of the just above function. This can also been used for the above function: as.character(sys.call(1)) -# test.pos <- 2 ; mean <- 0 ; fun1 <- function(){sd <- 1 ; fun_secu(pos = test.pos, name = if(length(sys.calls()) >= test.pos){as.character(sys.calls()[[length(sys.calls()) + 1 - test.pos]])}else{search()[ (1:length(search()))[test.pos - length(sys.calls())]]})} ; fun2 <- function(){cor <- 2 ; fun1()} ; fun1() ; fun2() ; rm(mean) # for argument name, here is a way to have the name of the tested environment according to test.pos value -# DEBUGGING -# pos = 1 ; name = NULL # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument primary checking -# arg with no default values -# end arg with no default values -# using fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = pos, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(name)){ -tempo <- fun_check(data = name, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <- c( -"pos" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT BE NULL:", paste0(tempo.arg[tempo.log], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# end second round of checking and data preparation -# main code -# match.list <- vector("list", length = (length(sys.calls()) - 1 + length(search()) + ifelse(length(sys.calls()) == 1L, -1, 0))) # match.list is a list of all the environment tested (local of functions and R envs), length(sys.calls()) - 1 to remove the level of the fun_secu() function, sys.calls() giving all the names of the imbricated functions, including fun_secu, ifelse(length(sys.calls()) == 1L, -1, 0) to remove Global env if this one is tested -tempo.name <- rev(as.character(unlist(sys.calls()))) # get names of frames (i.e., enclosed env) -tempo.frame <- rev(sys.frames()) # get frames (i.e., enclosed env) -# dealing with source() -# source() used in the Global env creates three frames above the Global env, which should be removed because not very interesting for variable duplications. Add a <<-(sys.frames()) in this code and source anova_contrasts code to see this. With ls(a[[4]]), we can see the content of each env, which are probably elements of source() -if(any(sapply(tempo.frame, FUN = environmentName) %in% "R_GlobalEnv")){ -global.pos <- which(sapply(tempo.frame, FUN = environmentName) %in% "R_GlobalEnv") -# remove the global env (because already in search(), and all the oabove env -tempo.name <- tempo.name[-c(global.pos:length(tempo.frame))] -tempo.frame <- tempo.frame[-c(global.pos:length(tempo.frame))] -} -# end dealing with source() -# might have a problem if(length(tempo.name) == 0L){ -match.list <- vector("list", length = length(tempo.name) + length(search())) # match.list is a list of all the environment tested (local of functions and R envs), length(sys.calls()) - 1 to remove the level of the fun_secu() function, sys.calls() giving all the names of the imbricated functions, including fun_secu, ifelse(length(sys.calls()) == 1L, -1, 0) to remove Global env if this one is tested -ls.names <- c(tempo.name, search()) # names of the functions + names of the search() environments -ls.input <- c(tempo.frame, as.list(search())) # environements of the functions + names of the search() environments -names(match.list) <- ls.names # -match.list <- match.list[-c(1:(pos + 1))] # because we check only above pos -ls.tested <- ls.input[[pos + 1]] -ls.input <- ls.input[-c(1:(pos + 1))] -for(i1 in 1:length(match.list)){ -if(any(ls(name = ls.input[[i1]], all.names = TRUE) %in% ls(name = ls.tested, all.names = TRUE))){ -match.list[i1] <- list(ls(name = ls.input[[i1]], all.names = TRUE)[ls(name = ls.input[[i1]], all.names = TRUE) %in% ls(name = ls.tested, all.names = TRUE)]) -} -} -if( ! all(sapply(match.list, FUN = is.null))){ -output <- paste0("SOME VARIABLES ", ifelse(is.null(name), "OF THE CHECKED ENVIRONMENT", paste0("OF ", name)), " ARE ALSO PRESENT IN :\n", paste0(names(match.list[ ! sapply(match.list, FUN = is.null)]), ": ", sapply(match.list[ ! sapply(match.list, FUN = is.null)], FUN = paste0, collapse = " "), collapse = "\n")) -}else{ -output <- NULL -} -return(output) + # AIM + # Verify that variables in the environment defined by the pos parameter are not present in the above environment (following R Scope). This can be used to avoid R scope preference of functions like get() + # ARGUMENTS + # pos: single integer indicating the position of the environment checked (argument n of parent.frame()). Value 1 means one step above the fun_secu() local environment (by default). This means that when fun_secu(pos = 1) is used inside a function A, it checks if variables in the local environment of this function A are also present in above environments (following R Scope). When fun_secu(pos = 1) is used in the Global environment, it checks the objects of this environment + # name: single character string indicating the name of the function checked. If NULL, fun_secu() checks all the variables of the environment indicated by pos, as explained in the pos argument description. If non-null, fun_secu() checks all the variables presents in the local env of the function will be checked in the above envs (which includes the working environment (Global env) + # RETURN + # A character string of the local variables that match variables in the different environments of the R scope, or NULL if no match + # REQUIRED PACKAGES + # None + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_secu() + # fun_secu(pos = 2) + # mean <- 0 ; fun1 <- function(){sd <- 1 ; fun_secu(name = as.character(sys.calls()[[length(sys.calls())]]))} ; fun2 <- function(){cor <- 2 ; fun1()} ; fun1() ; fun2() ; rm(mean) # sys.calls() gives the function name at top stack of the imbricated functions, sys.calls()[[length(sys.calls())]] the name of the just above function. This can also been used for the above function: as.character(sys.call(1)) + # test.pos <- 2 ; mean <- 0 ; fun1 <- function(){sd <- 1 ; fun_secu(pos = test.pos, name = if(length(sys.calls()) >= test.pos){as.character(sys.calls()[[length(sys.calls()) + 1 - test.pos]])}else{search()[ (1:length(search()))[test.pos - length(sys.calls())]]})} ; fun2 <- function(){cor <- 2 ; fun1()} ; fun1() ; fun2() ; rm(mean) # for argument name, here is a way to have the name of the tested environment according to test.pos value + # DEBUGGING + # pos = 1 ; name = NULL # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument primary checking + # arg with no default values + # end arg with no default values + # using fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = pos, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(name)){ + tempo <- fun_check(data = name, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <- c( + "pos" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT BE NULL:", paste0(tempo.arg[tempo.log], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # end second round of checking and data preparation + # main code + # match.list <- vector("list", length = (length(sys.calls()) - 1 + length(search()) + ifelse(length(sys.calls()) == 1L, -1, 0))) # match.list is a list of all the environment tested (local of functions and R envs), length(sys.calls()) - 1 to remove the level of the fun_secu() function, sys.calls() giving all the names of the imbricated functions, including fun_secu, ifelse(length(sys.calls()) == 1L, -1, 0) to remove Global env if this one is tested + tempo.name <- rev(as.character(unlist(sys.calls()))) # get names of frames (i.e., enclosed env) + tempo.frame <- rev(sys.frames()) # get frames (i.e., enclosed env) + # dealing with source() + # source() used in the Global env creates three frames above the Global env, which should be removed because not very interesting for variable duplications. Add a <<-(sys.frames()) in this code and source anova_contrasts code to see this. With ls(a[[4]]), we can see the content of each env, which are probably elements of source() + if(any(sapply(tempo.frame, FUN = environmentName) %in% "R_GlobalEnv")){ + global.pos <- which(sapply(tempo.frame, FUN = environmentName) %in% "R_GlobalEnv") + # remove the global env (because already in search(), and all the oabove env + tempo.name <- tempo.name[-c(global.pos:length(tempo.frame))] + tempo.frame <- tempo.frame[-c(global.pos:length(tempo.frame))] + } + # end dealing with source() + # might have a problem if(length(tempo.name) == 0L){ + match.list <- vector("list", length = length(tempo.name) + length(search())) # match.list is a list of all the environment tested (local of functions and R envs), length(sys.calls()) - 1 to remove the level of the fun_secu() function, sys.calls() giving all the names of the imbricated functions, including fun_secu, ifelse(length(sys.calls()) == 1L, -1, 0) to remove Global env if this one is tested + ls.names <- c(tempo.name, search()) # names of the functions + names of the search() environments + ls.input <- c(tempo.frame, as.list(search())) # environements of the functions + names of the search() environments + names(match.list) <- ls.names # + match.list <- match.list[-c(1:(pos + 1))] # because we check only above pos + ls.tested <- ls.input[[pos + 1]] + ls.input <- ls.input[-c(1:(pos + 1))] + for(i1 in 1:length(match.list)){ + if(any(ls(name = ls.input[[i1]], all.names = TRUE) %in% ls(name = ls.tested, all.names = TRUE))){ + match.list[i1] <- list(ls(name = ls.input[[i1]], all.names = TRUE)[ls(name = ls.input[[i1]], all.names = TRUE) %in% ls(name = ls.tested, all.names = TRUE)]) + } + } + if( ! all(sapply(match.list, FUN = is.null))){ + output <- paste0("SOME VARIABLES ", ifelse(is.null(name), "OF THE CHECKED ENVIRONMENT", paste0("OF ", name)), " ARE ALSO PRESENT IN :\n", paste0(names(match.list[ ! sapply(match.list, FUN = is.null)]), ": ", sapply(match.list[ ! sapply(match.list, FUN = is.null)], FUN = paste0, collapse = " "), collapse = "\n")) + }else{ + output <- NULL + } + return(output) } @@ -638,273 +638,273 @@ return(output) # -> transferred into the cute package # Do not modify this function in cute_little_R_function anymore. See the cute repo fun_info <- function( -data, -n = NULL, -warn.print = TRUE + data, + n = NULL, + warn.print = TRUE ){ -# AIM -# Provide a broad description of an object -# WARNINGS -# None -# ARGUMENTS -# data: object to analyse -# n: positive integer value indicating the n first number of elements to display per compartment of the output list (i.e., head(..., n)). Write NULL to return all the elements. Does not apply for the $STRUCTURE compartment output -# warn.print: logical. Print potential warnings at the end of the execution? If FALSE the warning messages are added in the output list as an additional compartment (or NULL if no message). -# RETURN -# A list containing information, depending on the class and type of data. The backbone is generally: -# $NAME: name of the object -# $CLASS: class of the object (class() value) -# $TYPE: type of the object (typeof() value) -# $LENGTH: length of the object (length() value) -# $NA.NB: number of NA and NaN (only for type "logical", "integer", "double", "complex", "character" or "list") -# $HEAD: head of the object (head() value) -# $TAIL: tail of the object (tail() value) -# $DIMENSION: dimension (only for object with dimensions) -# $SUMMARY: object summary (summary() value) -# $STRUCTURE: object structure (str() value) -# $WARNING: warning messages (only if the warn.print argument is FALSE) -# If data is made of numerics, provide also: -# $INF.NB: number of Inf and -Inf -# $RANGE: range after removing Inf and NA -# $SUM: sum after removing Inf and NA -# $MEAN: mean after removing Inf and NA -# If data is a 2D object, provide also: -# $ROW_NAMES: row names -# $COL_NAMES: column names -# If data is a data frame, provide also: -# $COLUMN_TYPE: type of each column (typeof() value) -# If data is a list, provide also: -# $COMPARTMENT_NAMES: names of the comprtments -# $COMPARTMENT_TYPE: type of each compartment (typeof() value) -# REQUIRED PACKAGES -# None -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# fun_check() -# fun_get_message() -# EXAMPLE -# fun_info(data = 1:3) -# see http -# DEBUGGING -# mat1 <- matrix(1:3) ; data = env1 ; n = NULL ; warn.print = TRUE # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check", -"fun_get_message" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words -# end reserved words -# arg with no default values -mandat.args <- c( -"data" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if( ! is.null(n)){ -tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = n, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ # normally no NA -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"data", -# "n", # because can be NULL -"warn.print" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -# warn.count <- 0 # not required -# end warning initiation -# other checkings -if( ! is.null(n)){ -if(n < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A POSITIVE AND NON NULL INTEGER") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(is.finite(n)){ -# warn.count <- warn.count + 1 -tempo.warn <- paste0("SOME COMPARTMENTS CAN BE TRUNCATED (n ARGUMENT IS ", n, ")") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end other checkings -# reserved word checking -# end reserved word checking -# end second round of checking and data preparation -# package checking -# end package checking -# main code -# new environment -env.name <- paste0("env", as.numeric(Sys.time())) -if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -assign(env.name, new.env()) -assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test -} -# end new environment -data.name <- deparse(substitute(data)) -output <- list("NAME" = data.name) -tempo.try.error <- fun_get_message(data = "class(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("CLASS" = class(data)) -output <- c(output, tempo) -} -tempo.try.error <- fun_get_message(data = "typeof(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("TYPE" = typeof(data)) -output <- c(output, tempo) -} -tempo.try.error <- fun_get_message(data = "length(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("LENGTH" = length(data)) -output <- c(output, tempo) -} -if(all(typeof(data) %in% c("integer", "numeric", "double")) & ! any(class(data) %in% "factor")){ # all() without na.rm -> ok because typeof(NA) is "logical" # any() without na.rm -> ok because class(NA) is "logical" -tempo <- list("INF.NB" = sum(is.infinite(data))) -output <- c(output, tempo) -tempo <- list("RANGE" = range(data[ ! is.infinite(data)], na.rm = TRUE)) -output <- c(output, tempo) -tempo <- list("SUM" = sum(data[ ! is.infinite(data)], na.rm = TRUE)) -output <- c(output, tempo) -tempo <- list("MEAN" = mean(data[ ! is.infinite(data)], na.rm = TRUE)) -output <- c(output, tempo) -} -if(all(typeof(data) %in% c("logical", "integer", "double", "complex", "character", "list"))){ # all() without na.rm -> ok because typeof(NA) is "logical" -tempo.try.error <- fun_get_message(data = "is.na(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("NA.NB" = sum(is.na(data))) -output <- c(output, tempo) -} -} -tempo.try.error <- fun_get_message(data = "head(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("HEAD" = head(data)) -output <- c(output, tempo) -tempo <- list("TAIL" = tail(data)) # no reason that tail() does not work if head() works -output <- c(output, tempo) -} -tempo.try.error <- fun_get_message(data = "dim(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -if(length(dim(data)) > 0){ -tempo <- list("DIMENSION" = dim(data)) -if(length(tempo[[1]]) == 2L){ -names(tempo[[1]]) <- c("NROW", "NCOL") -} -output <- c(output, tempo) -} -} -if(all(class(data) == "data.frame") | all(class(data) %in% c("matrix", "array")) | all(class(data) == "table")){ # all() without na.rm -> ok because typeof(NA) is "logical" -if(length(dim(data)) > 1){ # to avoid 1D table -tempo <- list("ROW_NAMES" = dimnames(data)[[1]]) -output <- c(output, tempo) -tempo <- list("COLUM_NAMES" = dimnames(data)[[2]]) -output <- c(output, tempo) -} -} -tempo.try.error <- fun_get_message(data = "summary(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- list("SUMMARY" = summary(data)) -output <- c(output, tempo) -} -tempo.try.error <- fun_get_message(data = "noquote(matrix(capture.output(str(data))))", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) -if(is.null(tempo.try.error)){ -tempo <- capture.output(str(data)) -tempo <- list("STRUCTURE" = noquote(matrix(tempo, dimnames = list(rep("", length(tempo)), "")))) # str() print automatically, ls.str() not but does not give the order of the data.frame -output <- c(output, tempo) -} -if(all(class(data) == "data.frame")){ # all() without na.rm -> ok because class(NA) is "logical" -tempo <- list("COLUMN_TYPE" = sapply(data, FUN = "typeof")) -if(any(sapply(data, FUN = "class") %in% "factor")){ # if an ordered factor is present, then sapply(data, FUN = "class") return a list but works with any(sapply(data, FUN = "class") %in% "factor") # any() without na.rm -> ok because class(NA) is "logical" -tempo.class <- sapply(data, FUN = "class") -if(any(unlist(tempo.class) %in% "ordered")){ # any() without na.rm -> ok because class(NA) is "logical" -tempo2 <- sapply(tempo.class, paste, collapse = " ") # paste the "ordered" factor" in "ordered factor" -}else{ -tempo2 <- unlist(tempo.class) -} -tempo[["COLUMN_TYPE"]][grepl(x = tempo2, pattern = "factor")] <- tempo2[grepl(x = tempo2, pattern = "factor")] -} -output <- c(output, tempo) -} -if(all(class(data) == "list")){ # all() without na.rm -> ok because class(NA) is "logical" -tempo <- list("COMPARTMENT_NAMES" = names(data)) -output <- c(output, tempo) -tempo <- list("COMPARTMENT_TYPE" = sapply(data, FUN = "typeof")) -if(any(unlist(sapply(data, FUN = "class")) %in% "factor")){ # if an ordered factor is present, then sapply(data, FUN = "class") return a list but works with any(sapply(data, FUN = "class") %in% "factor") # any() without na.rm -> ok because class(NA) is "logical" -tempo.class <- sapply(data, FUN = "class") -if(any(unlist(tempo.class) %in% "ordered")){ # any() without na.rm -> ok because class(NA) is "logical" -tempo2 <- sapply(tempo.class, paste, collapse = " ") # paste the "ordered" factor" in "ordered factor" -}else{ -tempo2 <- unlist(tempo.class) -} -tempo[["COMPARTMENT_TYPE"]][grepl(x = tempo2, pattern = "factor")] <- tempo2[grepl(x = tempo2, pattern = "factor")] -} -output <- c(output, tempo) -} -if( ! is.null(n)){ -output[names(output) != "STRUCTURE"] <- lapply(X = output[names(output) != "STRUCTURE"], FUN = head, n = n, simplify = FALSE) -} -# output -if(warn.print == FALSE){ -output <- c(output, WARNING = warn) -}else if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -return(output) -# end output -# end main code + # AIM + # Provide a broad description of an object + # WARNINGS + # None + # ARGUMENTS + # data: object to analyse + # n: positive integer value indicating the n first number of elements to display per compartment of the output list (i.e., head(..., n)). Write NULL to return all the elements. Does not apply for the $STRUCTURE compartment output + # warn.print: logical. Print potential warnings at the end of the execution? If FALSE the warning messages are added in the output list as an additional compartment (or NULL if no message). + # RETURN + # A list containing information, depending on the class and type of data. The backbone is generally: + # $NAME: name of the object + # $CLASS: class of the object (class() value) + # $TYPE: type of the object (typeof() value) + # $LENGTH: length of the object (length() value) + # $NA.NB: number of NA and NaN (only for type "logical", "integer", "double", "complex", "character" or "list") + # $HEAD: head of the object (head() value) + # $TAIL: tail of the object (tail() value) + # $DIMENSION: dimension (only for object with dimensions) + # $SUMMARY: object summary (summary() value) + # $STRUCTURE: object structure (str() value) + # $WARNING: warning messages (only if the warn.print argument is FALSE) + # If data is made of numerics, provide also: + # $INF.NB: number of Inf and -Inf + # $RANGE: range after removing Inf and NA + # $SUM: sum after removing Inf and NA + # $MEAN: mean after removing Inf and NA + # If data is a 2D object, provide also: + # $ROW_NAMES: row names + # $COL_NAMES: column names + # If data is a data frame, provide also: + # $COLUMN_TYPE: type of each column (typeof() value) + # If data is a list, provide also: + # $COMPARTMENT_NAMES: names of the comprtments + # $COMPARTMENT_TYPE: type of each compartment (typeof() value) + # REQUIRED PACKAGES + # None + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # fun_check() + # fun_get_message() + # EXAMPLE + # fun_info(data = 1:3) + # see http + # DEBUGGING + # mat1 <- matrix(1:3) ; data = env1 ; n = NULL ; warn.print = TRUE # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check", + "fun_get_message" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words + # end reserved words + # arg with no default values + mandat.args <- c( + "data" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if( ! is.null(n)){ + tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = n, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ # normally no NA + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "data", + # "n", # because can be NULL + "warn.print" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + # warn.count <- 0 # not required + # end warning initiation + # other checkings + if( ! is.null(n)){ + if(n < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A POSITIVE AND NON NULL INTEGER") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(is.finite(n)){ + # warn.count <- warn.count + 1 + tempo.warn <- paste0("SOME COMPARTMENTS CAN BE TRUNCATED (n ARGUMENT IS ", n, ")") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end other checkings + # reserved word checking + # end reserved word checking + # end second round of checking and data preparation + # package checking + # end package checking + # main code + # new environment + env.name <- paste0("env", as.numeric(Sys.time())) + if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + assign(env.name, new.env()) + assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test + } + # end new environment + data.name <- deparse(substitute(data)) + output <- list("NAME" = data.name) + tempo.try.error <- fun_get_message(data = "class(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("CLASS" = class(data)) + output <- c(output, tempo) + } + tempo.try.error <- fun_get_message(data = "typeof(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("TYPE" = typeof(data)) + output <- c(output, tempo) + } + tempo.try.error <- fun_get_message(data = "length(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("LENGTH" = length(data)) + output <- c(output, tempo) + } + if(all(typeof(data) %in% c("integer", "numeric", "double")) & ! any(class(data) %in% "factor")){ # all() without na.rm -> ok because typeof(NA) is "logical" # any() without na.rm -> ok because class(NA) is "logical" + tempo <- list("INF.NB" = sum(is.infinite(data))) + output <- c(output, tempo) + tempo <- list("RANGE" = range(data[ ! is.infinite(data)], na.rm = TRUE)) + output <- c(output, tempo) + tempo <- list("SUM" = sum(data[ ! is.infinite(data)], na.rm = TRUE)) + output <- c(output, tempo) + tempo <- list("MEAN" = mean(data[ ! is.infinite(data)], na.rm = TRUE)) + output <- c(output, tempo) + } + if(all(typeof(data) %in% c("logical", "integer", "double", "complex", "character", "list"))){ # all() without na.rm -> ok because typeof(NA) is "logical" + tempo.try.error <- fun_get_message(data = "is.na(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("NA.NB" = sum(is.na(data))) + output <- c(output, tempo) + } + } + tempo.try.error <- fun_get_message(data = "head(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("HEAD" = head(data)) + output <- c(output, tempo) + tempo <- list("TAIL" = tail(data)) # no reason that tail() does not work if head() works + output <- c(output, tempo) + } + tempo.try.error <- fun_get_message(data = "dim(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + if(length(dim(data)) > 0){ + tempo <- list("DIMENSION" = dim(data)) + if(length(tempo[[1]]) == 2L){ + names(tempo[[1]]) <- c("NROW", "NCOL") + } + output <- c(output, tempo) + } + } + if(all(class(data) == "data.frame") | all(class(data) %in% c("matrix", "array")) | all(class(data) == "table")){ # all() without na.rm -> ok because typeof(NA) is "logical" + if(length(dim(data)) > 1){ # to avoid 1D table + tempo <- list("ROW_NAMES" = dimnames(data)[[1]]) + output <- c(output, tempo) + tempo <- list("COLUM_NAMES" = dimnames(data)[[2]]) + output <- c(output, tempo) + } + } + tempo.try.error <- fun_get_message(data = "summary(data)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- list("SUMMARY" = summary(data)) + output <- c(output, tempo) + } + tempo.try.error <- fun_get_message(data = "noquote(matrix(capture.output(str(data))))", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)) + if(is.null(tempo.try.error)){ + tempo <- capture.output(str(data)) + tempo <- list("STRUCTURE" = noquote(matrix(tempo, dimnames = list(rep("", length(tempo)), "")))) # str() print automatically, ls.str() not but does not give the order of the data.frame + output <- c(output, tempo) + } + if(all(class(data) == "data.frame")){ # all() without na.rm -> ok because class(NA) is "logical" + tempo <- list("COLUMN_TYPE" = sapply(data, FUN = "typeof")) + if(any(sapply(data, FUN = "class") %in% "factor")){ # if an ordered factor is present, then sapply(data, FUN = "class") return a list but works with any(sapply(data, FUN = "class") %in% "factor") # any() without na.rm -> ok because class(NA) is "logical" + tempo.class <- sapply(data, FUN = "class") + if(any(unlist(tempo.class) %in% "ordered")){ # any() without na.rm -> ok because class(NA) is "logical" + tempo2 <- sapply(tempo.class, paste, collapse = " ") # paste the "ordered" factor" in "ordered factor" + }else{ + tempo2 <- unlist(tempo.class) + } + tempo[["COLUMN_TYPE"]][grepl(x = tempo2, pattern = "factor")] <- tempo2[grepl(x = tempo2, pattern = "factor")] + } + output <- c(output, tempo) + } + if(all(class(data) == "list")){ # all() without na.rm -> ok because class(NA) is "logical" + tempo <- list("COMPARTMENT_NAMES" = names(data)) + output <- c(output, tempo) + tempo <- list("COMPARTMENT_TYPE" = sapply(data, FUN = "typeof")) + if(any(unlist(sapply(data, FUN = "class")) %in% "factor")){ # if an ordered factor is present, then sapply(data, FUN = "class") return a list but works with any(sapply(data, FUN = "class") %in% "factor") # any() without na.rm -> ok because class(NA) is "logical" + tempo.class <- sapply(data, FUN = "class") + if(any(unlist(tempo.class) %in% "ordered")){ # any() without na.rm -> ok because class(NA) is "logical" + tempo2 <- sapply(tempo.class, paste, collapse = " ") # paste the "ordered" factor" in "ordered factor" + }else{ + tempo2 <- unlist(tempo.class) + } + tempo[["COMPARTMENT_TYPE"]][grepl(x = tempo2, pattern = "factor")] <- tempo2[grepl(x = tempo2, pattern = "factor")] + } + output <- c(output, tempo) + } + if( ! is.null(n)){ + output[names(output) != "STRUCTURE"] <- lapply(X = output[names(output) != "STRUCTURE"], FUN = head, n = n, simplify = FALSE) + } + # output + if(warn.print == FALSE){ + output <- c(output, WARNING = warn) + }else if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + return(output) + # end output + # end main code } @@ -912,62 +912,62 @@ return(output) fun_head <- function( -data1, -n = 6, -side = "l" + data1, + n = 6, + side = "l" ){ -# AIM -# as head() but display the left or right head of big 2D objects -# ARGUMENTS -# data1: any object but more dedicated for matrix, data frame or table -# n: as in head() but for for matrix, data frame or table, number of dimension to print (10 means 10 rows and columns) -# side: either "l" or "r" for the left or right side of the 2D object (only for matrix, data frame or table) -# BEWARE: other arguments of head() not used -# RETURN -# the head -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# obs1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:6], LETTERS[1:5])) ; obs1 ; fun_head(obs1, 3) -# DEBUGGING -# data1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = n, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = side, options = c("l", "r"), length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) -return(head(data1, n)) -}else{ -obs.dim <- dim(data1) -row <- 1:ifelse(obs.dim[1] < n, obs.dim[1], n) -if(side == "l"){ -col <- 1:ifelse(obs.dim[2] < n, obs.dim[2], n) -} -if(side == "r"){ -col <- ifelse(obs.dim[2] < n, 1, obs.dim[2] - n + 1):obs.dim[2] -} -return(data1[row, col]) -} + # AIM + # as head() but display the left or right head of big 2D objects + # ARGUMENTS + # data1: any object but more dedicated for matrix, data frame or table + # n: as in head() but for for matrix, data frame or table, number of dimension to print (10 means 10 rows and columns) + # side: either "l" or "r" for the left or right side of the 2D object (only for matrix, data frame or table) + # BEWARE: other arguments of head() not used + # RETURN + # the head + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # obs1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:6], LETTERS[1:5])) ; obs1 ; fun_head(obs1, 3) + # DEBUGGING + # data1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = n, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = side, options = c("l", "r"), length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) + return(head(data1, n)) + }else{ + obs.dim <- dim(data1) + row <- 1:ifelse(obs.dim[1] < n, obs.dim[1], n) + if(side == "l"){ + col <- 1:ifelse(obs.dim[2] < n, obs.dim[2], n) + } + if(side == "r"){ + col <- ifelse(obs.dim[2] < n, 1, obs.dim[2] - n + 1):obs.dim[2] + } + return(data1[row, col]) + } } @@ -975,62 +975,62 @@ return(data1[row, col]) fun_tail <- function( -data1, -n = 6, -side = "l" + data1, + n = 6, + side = "l" ){ -# AIM -# as tail() but display the left or right head of big 2D objects -# ARGUMENTS -# data1: any object but more dedicated for matrix, data frame or table -# n: as in tail() but for for matrix, data frame or table, number of dimension to print (10 means 10 rows and columns) -# side: either "l" or "r" for the left or right side of the 2D object (only for matrix, data frame or table) -# BEWARE: other arguments of tail() not used -# RETURN -# the tail -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# obs1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:6], LETTERS[1:5])) ; obs1 ; fun_tail(obs1, 3, "r") -# DEBUGGING -# data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = n, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = side, options = c("l", "r"), length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) -return(tail(data1, n)) -}else{ -obs.dim <- dim(data1) -row <- ifelse(obs.dim[1] < n, 1, obs.dim[1] - n + 1):obs.dim[1] -if(side == "l"){ -col <- 1:ifelse(obs.dim[2] < n, obs.dim[2], n) -} -if(side == "r"){ -col <- ifelse(obs.dim[2] < n, 1, obs.dim[2] - n + 1):obs.dim[2] -} -return(data1[row, col]) -} + # AIM + # as tail() but display the left or right head of big 2D objects + # ARGUMENTS + # data1: any object but more dedicated for matrix, data frame or table + # n: as in tail() but for for matrix, data frame or table, number of dimension to print (10 means 10 rows and columns) + # side: either "l" or "r" for the left or right side of the 2D object (only for matrix, data frame or table) + # BEWARE: other arguments of tail() not used + # RETURN + # the tail + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # obs1 = matrix(1:30, ncol = 5, dimnames = list(letters[1:6], LETTERS[1:5])) ; obs1 ; fun_tail(obs1, 3, "r") + # DEBUGGING + # data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = n, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = side, options = c("l", "r"), length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) + return(tail(data1, n)) + }else{ + obs.dim <- dim(data1) + row <- ifelse(obs.dim[1] < n, 1, obs.dim[1] - n + 1):obs.dim[1] + if(side == "l"){ + col <- 1:ifelse(obs.dim[2] < n, obs.dim[2], n) + } + if(side == "r"){ + col <- ifelse(obs.dim[2] < n, 1, obs.dim[2] - n + 1):obs.dim[2] + } + return(data1[row, col]) + } } @@ -1038,12371 +1038,12377 @@ return(data1[row, col]) fun_comp_1d <- function(data1, data2){ -# AIM -# compare two 1D datasets (vector or factor or 1D table, or 1D matrix or 1D array) of the same class or not. Check and report in a list if the 2 datasets have: -# same class -# common elements -# common element names (except factors) -# common levels (factors only) -# ARGUMENTS -# data1: vector or factor or 1D table, or 1D matrix or 1D array -# data2: vector or factor or 1D table, or 1D matrix or 1D array -# RETURN -# a list containing: -# $same.class: logical. Are class identical? -# $class: class of the 2 datasets (NULL otherwise) -# $same.length: logical. Are number of elements identical? -# $length: number of elements in the 2 datasets (NULL otherwise) -# $same.levels: logical. Are levels identical? NULL if data1 and data2 are not factors -# $levels: levels of the 2 datasets if identical (NULL otherwise or NULL if data1 and data2 are not factors) -# $any.id.levels: logical. Is there any identical levels? (NULL if data1 and data2 are not factors) -# $same.levels.pos1: positions, in data1, of the levels identical in data2 (NULL otherwise or NULL if data1 and data2 are not factors) -# $same.levels.pos2: positions, in data2, of the levels identical in data1 (NULL otherwise or NULL if data1 and data2 are not factors) -# $same.levels.match1: positions, in data2, of the levels that match the levels in data1, as given by match(data1, data2) (NULL otherwise or NULL if data1 and data2 are not factors) -# $same.levels.match2: positions, in data1, of the levels that match the levels in data2, as given by match(data1, data2) (NULL otherwise or NULL if data1 and data2 are not factors) -# $common.levels: common levels between data1 and data2 (can be a subset of $levels or not). NULL if no common levels or if data1 and data2 are not factors -# $same.names: logical. Are element names identical? NULL if data1 and data2 have no names -# $name: name of elements of the 2 datasets if identical (NULL otherwise) -# $any.id.name: logical. Is there any element names identical ? -# $same.names.pos1: positions, in data1, of the element names identical in data2. NULL if no identical names -# $same.names.pos2: positions, in data2, of the elements names identical in data1. NULL if no identical names -# $same.names.match1: positions, in data2, of the names that match the names in data1, as given by match(data1, data2) (NULL otherwise) -# $same.names.match2: positions, in data1, of the names that match the names in data2, as given by match(data1, data2) (NULL otherwise) -# $common.names: common element names between data1 and data2 (can be a subset of $name or not). NULL if no common element names -# $any.id.element: logical. is there any identical elements ? -# $same.elements.pos1: positions, in data1, of the elements identical in data2. NULL if no identical elements -# $same.elements.pos2: positions, in data2, of the elements identical in data1. NULL if no identical elements -# $same.elements.match1: positions, in data2, of the elements that match the elements in data1, as given by match(data1, data2) (NULL otherwise) -# $same.elements.match2: positions, in data1, of the elements that match the elements in data2, as given by match(data1, data2) (NULL otherwise) -# $common.elements: common elements between data1 and data2. NULL if no common elements -# $same.order: logical. Are all elements in the same order? TRUE or FALSE if elements of data1 and data2 are identical but not necessary in the same order. NULL otherwise (different length for instance) -# $order1: order of all elements of data1. NULL if $same.order is FALSE -# $order2: order of all elements of data2. NULL if $same.order is FALSE -# $identical.object: logical. Are objects identical (kind of object, element names, content, including content order)? -# $identical.content: logical. Are content objects identical (identical elements, including order, excluding kind of object and element names)? -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# none -# EXAMPLES -# obs1 = 1:5 ; obs2 = 1:5 ; names(obs1) <- LETTERS[1:5] ; names(obs2) <- LETTERS[1:5] ; fun_comp_1d(obs1, obs2) -# obs1 = 1:5 ; obs2 = 1:5 ; names(obs1) <- LETTERS[1:5] ; fun_comp_1d(obs1, obs2) -# obs1 = 1:5 ; obs2 = 3:6 ; names(obs1) <- LETTERS[1:5] ; names(obs2) <- LETTERS[1:4] ; fun_comp_1d(obs1, obs2) -# obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[1:5]) ; fun_comp_1d(obs1, obs2) -# obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[10:11]) ; fun_comp_1d(obs1, obs2) -# obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[4:7]) ; fun_comp_1d(obs1, obs2) -# obs1 = factor(c(LETTERS[1:4], "E")) ; obs2 = factor(c(LETTERS[1:4], "F")) ; fun_comp_1d(obs1, obs2) -# obs1 = 1:5 ; obs2 = factor(LETTERS[1:5]) ; fun_comp_1d(obs1, obs2) -# obs1 = 1:5 ; obs2 = 1.1:6.1 ; fun_comp_1d(obs1, obs2) -# obs1 = as.table(1:5); obs2 = as.table(1:5) ; fun_comp_1d(obs1, obs2) -# obs1 = as.table(1:5); obs2 = 1:5 ; fun_comp_1d(obs1, obs2) -# DEBUGGING -# data1 = 1:5 ; data2 = 1:5 ; names(data1) <- LETTERS[1:5] ; names(data2) <- LETTERS[1:5] # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# argument checking -if( ! any(class(data1) %in% c("logical", "integer", "numeric", "character", "factor", "table"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A NON NULL VECTOR, FACTOR OR 1D TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(all(class(data1) %in% "table")){ -if(length(dim(data1)) > 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A 1D TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + # AIM + # compare two 1D datasets (vector or factor or 1D table, or 1D matrix or 1D array) of the same class or not. Check and report in a list if the 2 datasets have: + # same class + # common elements + # common element names (except factors) + # common levels (factors only) + # ARGUMENTS + # data1: vector or factor or 1D table, or 1D matrix or 1D array + # data2: vector or factor or 1D table, or 1D matrix or 1D array + # RETURN + # a list containing: + # $same.class: logical. Are class identical? + # $class: class of the 2 datasets (NULL otherwise) + # $same.length: logical. Are number of elements identical? + # $length: number of elements in the 2 datasets (NULL otherwise) + # $same.levels: logical. Are levels identical? NULL if data1 and data2 are not factors + # $levels: levels of the 2 datasets if identical (NULL otherwise or NULL if data1 and data2 are not factors) + # $any.id.levels: logical. Is there any identical levels? (NULL if data1 and data2 are not factors) + # $same.levels.pos1: positions, in data1, of the levels identical in data2 (NULL otherwise or NULL if data1 and data2 are not factors) + # $same.levels.pos2: positions, in data2, of the levels identical in data1 (NULL otherwise or NULL if data1 and data2 are not factors) + # $same.levels.match1: positions, in data2, of the levels that match the levels in data1, as given by match(data1, data2) (NULL otherwise or NULL if data1 and data2 are not factors) + # $same.levels.match2: positions, in data1, of the levels that match the levels in data2, as given by match(data1, data2) (NULL otherwise or NULL if data1 and data2 are not factors) + # $common.levels: common levels between data1 and data2 (can be a subset of $levels or not). NULL if no common levels or if data1 and data2 are not factors + # $same.names: logical. Are element names identical? NULL if data1 and data2 have no names + # $name: name of elements of the 2 datasets if identical (NULL otherwise) + # $any.id.name: logical. Is there any element names identical ? + # $same.names.pos1: positions, in data1, of the element names identical in data2. NULL if no identical names + # $same.names.pos2: positions, in data2, of the elements names identical in data1. NULL if no identical names + # $same.names.match1: positions, in data2, of the names that match the names in data1, as given by match(data1, data2) (NULL otherwise) + # $same.names.match2: positions, in data1, of the names that match the names in data2, as given by match(data1, data2) (NULL otherwise) + # $common.names: common element names between data1 and data2 (can be a subset of $name or not). NULL if no common element names + # $any.id.element: logical. is there any identical elements ? + # $same.elements.pos1: positions, in data1, of the elements identical in data2. NULL if no identical elements + # $same.elements.pos2: positions, in data2, of the elements identical in data1. NULL if no identical elements + # $same.elements.match1: positions, in data2, of the elements that match the elements in data1, as given by match(data1, data2) (NULL otherwise) + # $same.elements.match2: positions, in data1, of the elements that match the elements in data2, as given by match(data1, data2) (NULL otherwise) + # $common.elements: common elements between data1 and data2. NULL if no common elements + # $same.order: logical. Are all elements in the same order? TRUE or FALSE if elements of data1 and data2 are identical but not necessary in the same order. NULL otherwise (different length for instance) + # $order1: order of all elements of data1. NULL if $same.order is FALSE + # $order2: order of all elements of data2. NULL if $same.order is FALSE + # $identical.object: logical. Are objects identical (kind of object, element names, content, including content order)? + # $identical.content: logical. Are content objects identical (identical elements, including order, excluding kind of object and element names)? + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # none + # EXAMPLES + # obs1 = 1:5 ; obs2 = 1:5 ; names(obs1) <- LETTERS[1:5] ; names(obs2) <- LETTERS[1:5] ; fun_comp_1d(obs1, obs2) + # obs1 = 1:5 ; obs2 = 1:5 ; names(obs1) <- LETTERS[1:5] ; fun_comp_1d(obs1, obs2) + # obs1 = 1:5 ; obs2 = 3:6 ; names(obs1) <- LETTERS[1:5] ; names(obs2) <- LETTERS[1:4] ; fun_comp_1d(obs1, obs2) + # obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[1:5]) ; fun_comp_1d(obs1, obs2) + # obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[10:11]) ; fun_comp_1d(obs1, obs2) + # obs1 = factor(LETTERS[1:5]) ; obs2 = factor(LETTERS[4:7]) ; fun_comp_1d(obs1, obs2) + # obs1 = factor(c(LETTERS[1:4], "E")) ; obs2 = factor(c(LETTERS[1:4], "F")) ; fun_comp_1d(obs1, obs2) + # obs1 = 1:5 ; obs2 = factor(LETTERS[1:5]) ; fun_comp_1d(obs1, obs2) + # obs1 = 1:5 ; obs2 = 1.1:6.1 ; fun_comp_1d(obs1, obs2) + # obs1 = as.table(1:5); obs2 = as.table(1:5) ; fun_comp_1d(obs1, obs2) + # obs1 = as.table(1:5); obs2 = 1:5 ; fun_comp_1d(obs1, obs2) + # DEBUGGING + # data1 = 1:5 ; data2 = 1:5 ; names(data1) <- LETTERS[1:5] ; names(data2) <- LETTERS[1:5] # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # argument checking + if( ! any(class(data1) %in% c("logical", "integer", "numeric", "character", "factor", "table"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A NON NULL VECTOR, FACTOR OR 1D TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(all(class(data1) %in% "table")){ + if(length(dim(data1)) > 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A 1D TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! any(class(data2) %in% c("logical", "integer", "numeric", "character", "factor", "table"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A NON NULL VECTOR, FACTOR OR 1D TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(all(class(data2) %in% "table")){ + if(length(dim(data2)) > 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A 1D TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status + # end argument checking + # main code + same.class <- FALSE + class <- NULL + same.length <- FALSE + length <- NULL + same.levels <- NULL # not FALSE to deal with no factors + levels <- NULL + any.id.levels <- FALSE + same.levels.pos1 <- NULL + same.levels.pos2 <- NULL + same.levels.match1 <- NULL + same.levels.match2 <- NULL + common.levels <- NULL + same.names <- NULL # not FALSE to deal with absence of name + name <- NULL + any.id.name <- FALSE + same.names.pos1 <- NULL + same.names.pos2 <- NULL + same.names.match1 <- NULL + same.names.match2 <- NULL + common.names <- NULL + any.id.element <- FALSE + same.elements.pos1 <- NULL + same.elements.pos2 <- NULL + same.elements.match1 <- NULL + same.elements.match2 <- NULL + common.elements <- NULL + same.order <- NULL + order1 <- NULL + order2 <- NULL + identical.object <- FALSE + identical.content <- FALSE + if(identical(data1, data2)){ + same.class <- TRUE + class <- class(data1) + same.length <- TRUE + length <- length(data1) + if(any(class(data1) %in% "factor")){ + same.levels <- TRUE + levels <- levels(data1) + any.id.levels <- TRUE + same.levels.pos1 <- 1:length(levels(data1)) + same.levels.pos2 <- 1:length(levels(data2)) + same.levels.match1 <- 1:length(levels(data1)) + same.levels.match2 <- 1:length(levels(data2)) + common.levels <- levels(data1) + } + if( ! is.null(names(data1))){ + same.names <- TRUE + name <- names(data1) + any.id.name <- TRUE + same.names.pos1 <- 1:length(data1) + same.names.pos2 <- 1:length(data2) + same.names.match1 <- 1:length(data1) + same.names.match2 <- 1:length(data2) + common.names <- names(data1) + } + any.id.element <- TRUE + same.elements.pos1 <- 1:length(data1) + same.elements.pos2 <- 1:length(data2) + same.elements.match1 <- 1:length(data1) + same.elements.match2 <- 1:length(data2) + common.elements <- data1 + same.order <- TRUE + order1 <- order(data1) + order2 <- order(data2) + identical.object <- TRUE + identical.content <- TRUE + }else{ + if(identical(class(data1), class(data2))){ + same.class <- TRUE + class <- class(data1) + } + if(identical(length(data1), length(data2))){ + same.length<- TRUE + length <- length(data1) + } + if(any(class(data1) %in% "factor") & any(class(data2) %in% "factor")){ + if(identical(levels(data1), levels(data2))){ + same.levels <- TRUE + levels <- levels(data1) + }else{ + same.levels <- FALSE + } + if(any(levels(data1) %in% levels(data2))){ + any.id.levels <- TRUE + same.levels.pos1 <- which(levels(data1) %in% levels(data2)) + same.levels.match1 <- match(levels(data1), levels(data2)) + } + if(any(levels(data2) %in% levels(data1))){ + any.id.levels <- TRUE + same.levels.pos2 <- which(levels(data2) %in% levels(data1)) + same.levels.match2 <- match(levels(data2), levels(data1)) + } + if(any.id.levels == TRUE){ + common.levels <- unique(c(levels(data1)[same.levels.pos1], levels(data2)[same.levels.pos2])) + } + } + if(any(class(data1) %in% "factor")){ # to compare content + data1 <- as.character(data1) + } + if(any(class(data2) %in% "factor")){ # to compare content + data2 <- as.character(data2) + } + if( ! (is.null(names(data1)) & is.null(names(data2)))){ + if(identical(names(data1), names(data2))){ + same.names <- TRUE + name <- names(data1) + }else{ + same.names <- FALSE + } + if(any(names(data1) %in% names(data2))){ + any.id.name <- TRUE + same.names.pos1 <- which(names(data1) %in% names(data2)) + same.names.match1 <- match(names(data1), names(data2)) + } + if(any(names(data2) %in% names(data1))){ + any.id.name <- TRUE + same.names.pos2 <- which(names(data2) %in% names(data1)) + same.names.match2 <- match(names(data2), names(data1)) + } + if(any.id.name == TRUE){ + common.names <- unique(c(names(data1)[same.names.pos1], names(data2)[same.names.pos2])) + } + } + names(data1) <- NULL # names solved -> to do not be disturbed by names + names(data2) <- NULL # names solved -> to do not be disturbed by names + if(any(data1 %in% data2)){ + any.id.element <- TRUE + same.elements.pos1 <- which(data1 %in% data2) + same.elements.match1 <- match(data1, data2) + } + if(any(data2 %in% data1)){ + any.id.element <- TRUE + same.elements.pos2 <- which(data2 %in% data1) + same.elements.match2 <- match(data2, data1) + } + if(any.id.element == TRUE){ + common.elements <- unique(c(data1[same.elements.pos1], data2[same.elements.pos2])) + } + if(identical(data1, data2)){ + identical.content <- TRUE + same.order <- TRUE + }else if(identical(sort(data1), sort(data2))){ + same.order <- FALSE + order1 <- order(data1) + order2 <- order(data2) + } + } + output <- list(same.class = same.class, class = class, same.length = same.length, length = length, same.levels = same.levels, levels = levels, any.id.levels = any.id.levels, same.levels.pos1 = same.levels.pos1, same.levels.pos2 = same.levels.pos2, same.levels.match1 = same.levels.match1, same.levels.match2 = same.levels.match2, common.levels = common.levels, same.names = same.names, name = name, any.id.name = any.id.name, same.names.pos1 = same.names.pos1, same.names.pos2 = same.names.pos2, same.names.match1 = same.names.match1, same.names.match2 = same.names.match2, common.names = common.names, any.id.element = any.id.element, same.elements.pos1 = same.elements.pos1, same.elements.pos2 = same.elements.pos2, same.elements.match1 = same.elements.match1, same.elements.match2 = same.elements.match2, common.elements = common.elements, same.order = same.order, order1 = order1, order2 = order2, identical.object = identical.object, identical.content = identical.content) + return(output) } + + +######## fun_comp_2d() #### comparison of two 2D datasets (row & col names, dimensions, etc.) + + +fun_comp_2d <- function(data1, data2){ + # AIM + # compare two 2D datasets of the same class or not. Check and report in a list if the 2 datasets have: + # same class + # common row names + # common column names + # same row number + # same column number + # potential identical rows between the 2 datasets + # potential identical columns between the 2 datasets + # WARNINGS + # For data frames: content are compared after conversion of content into characters. This means that the comparison of the content of data frame, either row to row, or column to column, does not take into account the mode in the different columns. This concern the results in $any.id.row, $same.row.pos1, $same.row.pos2, $same.row.match1, $same.row.match2, $any.id.col, $same.row.col1, $same.row.col2, $same.col.match1, $same.col.match2 and $identical.content result + # "TOO BIG FOR EVALUATION" returned in $same.row.pos1, $same.row.pos2, $same.row.match1 and $same.row.match2 when nrow(data1) * nrow(data2) > 1e6 and $any.id.row remains NULL + # "TOO BIG FOR EVALUATION" returned in $same.row.col1, $same.row.col2, $same.col.match1 and $same.col.match2 when ncol(data1) * ncol(data2) > 1e6 and $any.id.col remains NULL + # ARGUMENTS + # data1: matrix, data frame or table + # data2: matrix, data frame or table + # RETURN + # a list containing: + # $same.class: logical. Are class identical ? + # $class: classes of the 2 datasets (NULL otherwise) + # $same.dim: logical. Are dimension identical ? + # $dim: dimension of the 2 datasets (NULL otherwise) + # $same.row.nb: logical. Are number of rows identical ? + # $row.nb: nb of rows of the 2 datasets if identical (NULL otherwise) + # $same.col.nb: logical. Are number of columns identical ? + # $col.nb: nb of columns of the 2 datasets if identical (NULL otherwise) + # $same.row.name: logical. Are row names identical ? NULL if no row names in the two 2D datasets + # $row.name: name of rows of the 2 datasets if identical (NULL otherwise) + # $any.id.row.name: logical. Is there any row names identical ? NULL if no row names in the two 2D datasets + # $same.row.names.pos1: positions, in data1, of the row names identical in data2 + # $same.row.names.pos2: positions, in data2, of the row names identical in data1 + # $same.row.names.match1: positions, in data2, of the row names that match the row names in data1, as given by match(data1, data2) (NULL otherwise) + # $same.row.names.match2: positions, in data1, of the row names that match the row names in data2, as given by match(data1, data2) (NULL otherwise) + # $common.row.names: common row names between data1 and data2 (can be a subset of $name or not). NULL if no common row names + # $same.col.name: logical. Are column names identical ? NULL if no col names in the two 2D datasets + # $col.name: name of columns of the 2 datasets if identical (NULL otherwise) + # $any.id.col.name: logical. Is there any column names identical ? NULL if no col names in the two 2D datasets + # $same.col.names.pos1: positions, in data1, of the column names identical in data2 + # $same.col.names.pos2: positions, in data2, of the column names identical in data1 + # $same.col.names.match1: positions, in data2, of the column names that match the column names in data1, as given by match(data1, data2) (NULL otherwise) + # $same.col.names.match2: positions, in data1, of the column names that match the column names in data2, as given by match(data1, data2) (NULL otherwise) + # $common.col.names: common column names between data1 and data2 (can be a subset of $name or not). NULL if no common column names + # $any.id.row: logical. is there identical rows (not considering row names)? NULL if nrow(data1) * nrow(data2) > 1e10 + # $same.row.pos1: positions, in data1, of the rows identical in data2 (not considering row names). Return "TOO BIG FOR EVALUATION" if nrow(data1) * nrow(data2) > 1e10 + # $same.row.pos2: positions, in data2, of the rows identical in data1 (not considering row names). Return "TOO BIG FOR EVALUATION" if nrow(data1) * nrow(data2) > 1e10 + # $same.row.match1: positions, in data2, of the rows that match the rows in data1, as given by match(data1, data2) (NULL otherwise) + # $same.row.match2: positions, in data1, of the rows that match the rows in data2, as given by match(data1, data2) (NULL otherwise) + # $any.id.col: logical. is there identical columns (not considering column names)? NULL if ncol(data1) * ncol(data2) > 1e10 + # $same.col.pos1: position in data1 of the cols identical in data2 (not considering column names). Return "TOO BIG FOR EVALUATION" if ncol(data1) * ncol(data2) > 1e10 + # $same.col.pos2: position in data2 of the cols identical in data1 (not considering column names). Return "TOO BIG FOR EVALUATION" if ncol(data1) * ncol(data2) > 1e10 + # $same.col.match1: positions, in data2, of the columns that match the columns in data1, as given by match(data1, data2) (NULL otherwise) + # $same.row.match2: positions, in data1, of the columns that match the columns in data2, as given by match(data1, data2) (NULL otherwise) + # $identical.object: logical. Are objects identical (including row & column names)? + # $identical.content: logical. Are content objects identical (identical excluding row & column names)? + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # none + # EXAMPLES + # obs1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = as.data.frame(matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])), stringsAsFactors = TRUE) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) + # obs1 = matrix(101:110, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) + # large matrices + # obs1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; obs2 = matrix(as.integer((1:1e6)+1e6/5), ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; head(obs1) ; head(obs2) ; fun_comp_2d(obs1, obs2) + # WARNING: when comparing content (rows, columns, or total), double and integer data are considered as different -> double(1) != integer(1) + # obs1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; obs2 = matrix((1:1e6)+1e6/5, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; head(obs1) ; head(obs2) ; fun_comp_2d(obs1, obs2) + # Matrices: same row conten tand same row names + # obs1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4]))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) + # Matrices: same row content but not same row names -> works: same content is identified + # obs1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("x", "z", "y"), c(LETTERS[1:2], "k", LETTERS[5:4]))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) + # obs1 = t(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = t(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) + # Data frames: same row content and same row names, not same mode between columns + # obs1 = as.data.frame(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = as.data.frame(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1[, 5] <- as.character(obs1[, 5]) ; obs2[, 5] <- as.character(obs2[, 5]) ; obs1 ; obs2 ; str(obs1) ; str(obs2) ; fun_comp_2d(obs1, obs2) + # Data frames: same row content but not same row names -> works: same content is identified + # obs1 = as.data.frame(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = as.data.frame(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("x", "z", "y"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1[, 5] <- as.character(obs1[, 5]) ; obs2[, 5] <- as.character(obs2[, 5]) ; obs1 ; obs2 ; str(obs1) ; str(obs2) ; fun_comp_2d(obs1, obs2) + # DEBUGGING + # data1 = matrix(1:10, ncol = 5) ; data2 = matrix(1:10, ncol = 5) # for function debugging + # data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5) # for function debugging + # data1 = matrix(1:15, byrow = TRUE, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # data1 = matrix(1:15, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # data1 = matrix(1:15, ncol = 5, dimnames = list(paste0("A", letters[1:3]), LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # data1 = matrix(1:15, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:12, ncol = 4, dimnames = list(letters[1:3], LETTERS[1:4])) # for function debugging + # data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(101:110, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging + # data1 = data.frame(a = 1:3, b= letters[1:3], row.names = LETTERS[1:3], stringsAsFactors = TRUE) ; data2 = data.frame(A = 1:3, B= letters[1:3], stringsAsFactors = TRUE) # for function debugging + # data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = as.data.frame(matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])), stringsAsFactors = TRUE) # for function debugging + # data1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4]))) # for function debugging + # data1 = table(Exp1 = c("A", "A", "A", "B", "B", "B"), Exp2 = c("A1", "B1", "A1", "C1", "C1", "B1")) ; data2 = data.frame(A = 1:3, B= letters[1:3], stringsAsFactors = TRUE) # for function debugging + # data1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; data2 = matrix((1:1e6)+1e6/5, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # argument checking + if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A MATRIX, DATA FRAME OR TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (any(class(data2) %in% c("data.frame", "table")) | all(class(data2) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data2) %in% c("matrix", "data.frame", "table")) + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A MATRIX, DATA FRAME OR TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status + # end argument checking + # main code + same.class <- NULL + class <- NULL + same.dim <- NULL + dim <- NULL + same.row.nb <- NULL + row.nb <- NULL + same.col.nb <- NULL + col.nb <- NULL + same.row.name <- NULL + row.name <- NULL + any.id.row.name <- NULL + same.row.names.pos1 <- NULL + same.row.names.pos2 <- NULL + same.row.names.match1 <- NULL + same.row.names.match2 <- NULL + common.row.names <- NULL + same.col.name <- NULL + any.id.col.name <- NULL + same.col.names.pos1 <- NULL + same.col.names.pos2 <- NULL + same.col.names.match1 <- NULL + same.col.names.match2 <- NULL + common.col.names <- NULL + col.name <- NULL + any.id.row <- NULL + same.row.pos1 <- NULL + same.row.pos2 <- NULL + same.row.match1 <- NULL + same.row.match2 <- NULL + any.id.col <- NULL + same.col.pos1 <- NULL + same.col.pos2 <- NULL + same.col.match1 <- NULL + same.col.match2 <- NULL + identical.object <- NULL + identical.content <- NULL + if(identical(data1, data2) & (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) + same.class <- TRUE + class <- class(data1) + same.dim <- TRUE + dim <- dim(data1) + same.row.nb <- TRUE + row.nb <- nrow(data1) + same.col.nb <- TRUE + col.nb <- ncol(data1) + same.row.name <- TRUE + row.name <- dimnames(data1)[[1]] + any.id.row.name <- TRUE + same.row.names.pos1 <- 1:row.nb + same.row.names.pos2 <- 1:row.nb + same.row.names.match1 <- 1:row.nb + same.row.names.match2 <- 1:row.nb + common.row.names <- dimnames(data1)[[1]] + same.col.name <- TRUE + col.name <- dimnames(data1)[[2]] + any.id.col.name <- TRUE + same.col.names.pos1 <- 1:col.nb + same.col.names.pos2 <- 1:col.nb + same.col.names.match1 <- 1:col.nb + same.col.names.match2 <- 1:col.nb + common.col.names <- dimnames(data1)[[2]] + any.id.row <- TRUE + same.row.pos1 <- 1:row.nb + same.row.pos2 <- 1:row.nb + same.row.match1 <- 1:row.nb + same.row.match2 <- 1:row.nb + any.id.col <- TRUE + same.col.pos1 <- 1:col.nb + same.col.pos2 <- 1:col.nb + same.col.match1 <- 1:col.nb + same.col.match2 <- 1:col.nb + identical.object <- TRUE + identical.content <- TRUE + }else{ + identical.object <- FALSE + if(all(class(data1) == "table") & length(dim(data1)) == 1L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT IS A 1D TABLE. USE THE fun_comp_1d FUNCTION") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(all(class(data2) == "table") & length(dim(data2)) == 1L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT IS A 1D TABLE. USE THE fun_comp_1d FUNCTION") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! identical(class(data1), class(data2))){ + same.class <- FALSE + }else if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 AND data2 ARGUMENTS MUST BE EITHER MATRIX, DATA FRAME OR TABLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + same.class <- TRUE + class <- class(data1) + } + if( ! identical(dim(data1), dim(data2))){ + same.dim <- FALSE + }else{ + same.dim <- TRUE + dim <- dim(data1) + } + if( ! identical(nrow(data1), nrow(data2))){ + same.row.nb <- FALSE + }else{ + same.row.nb <- TRUE + row.nb <- nrow(data1) + } + if( ! identical(ncol(data1), ncol(data2))){ + same.col.nb <- FALSE + }else{ + same.col.nb <- TRUE + col.nb <- ncol(data1) + } + # row and col names + if(is.null(dimnames(data1)) & is.null(dimnames(data2))){ + same.row.name <- NULL # but already NULL + same.col.name <- NULL # but already NULL + # other row names param remain NULL + }else if((is.null(dimnames(data1)) & ! is.null(dimnames(data2))) | ( ! is.null(dimnames(data1)) & is.null(dimnames(data2)))){ + same.row.name <- FALSE + same.col.name <- FALSE + any.id.row.name <- FALSE + any.id.col.name <- FALSE + # other row names param remain NULL + }else{ + # row names + if(is.null(dimnames(data1)[[1]]) & is.null(dimnames(data2)[[1]])){ + same.row.name <- NULL # but already NULL + # other row names param remain NULL + }else if((is.null(dimnames(data1)[[1]]) & ! is.null(dimnames(data2)[[1]])) | ( ! is.null(dimnames(data1)[[1]]) & is.null(dimnames(data2)[[1]]))){ + same.row.name <- FALSE + any.id.row.name <- FALSE + # other row names param remain NULL + }else if(identical(dimnames(data1)[[1]], dimnames(data2)[[1]])){ + same.row.name <- TRUE + row.name <- dimnames(data1)[[1]] + any.id.row.name <- TRUE + same.row.names.pos1 <- 1:nrow(data1) + same.row.names.pos2 <- 1:nrow(data1) + same.row.names.match1 <- 1:nrow(data1) + same.row.names.match2 <- 1:nrow(data1) + common.row.names <- dimnames(data1)[[1]] + }else{ + same.row.name <- FALSE + any.id.row.name <- FALSE + if(any(dimnames(data1)[[1]] %in% dimnames(data2)[[1]])){ + any.id.row.name <- TRUE + same.row.names.pos1 <- which(dimnames(data1)[[1]] %in% dimnames(data2)[[1]]) + same.row.names.match1 <- match(dimnames(data1)[[1]], dimnames(data2)[[1]]) + } + if(any(dimnames(data2)[[1]] %in% dimnames(data1)[[1]])){ + any.id.row.name <- TRUE + same.row.names.pos2 <- which(dimnames(data2)[[1]] %in% dimnames(data1)[[1]]) + same.row.names.match2 <- match(dimnames(data2)[[1]], dimnames(data1)[[1]]) + } + if(any.id.row.name == TRUE){ + common.row.names <- unique(c(dimnames(data1)[[1]][same.row.names.pos1], dimnames(data2)[[1]][same.row.names.pos2])) + } + } + # col names + if(is.null(dimnames(data1)[[2]]) & is.null(dimnames(data2)[[2]])){ + same.col.name <- NULL # but already NULL + # other col names param remain NULL + }else if((is.null(dimnames(data1)[[2]]) & ! is.null(dimnames(data2)[[2]])) | ( ! is.null(dimnames(data1)[[2]]) & is.null(dimnames(data2)[[2]]))){ + same.col.name <- FALSE + any.id.col.name <- FALSE + # other col names param remain NULL + }else if(identical(dimnames(data1)[[2]], dimnames(data2)[[2]])){ + same.col.name <- TRUE + col.name <- dimnames(data1)[[2]] + any.id.col.name <- TRUE + same.col.names.pos1 <- 1:ncol(data1) + same.col.names.pos2 <- 1:ncol(data1) + same.col.names.match1 <- 1:ncol(data1) + same.col.names.match2 <- 1:ncol(data1) + common.col.names <- dimnames(data1)[[2]] + }else{ + same.col.name <- FALSE + any.id.col.name <- FALSE + if(any(dimnames(data1)[[2]] %in% dimnames(data2)[[2]])){ + any.id.col.name <- TRUE + same.col.names.pos1 <- which(dimnames(data1)[[2]] %in% dimnames(data2)[[2]]) + same.col.names.match1 <- match(dimnames(data1)[[2]], dimnames(data2)[[2]]) + } + if(any(dimnames(data2)[[2]] %in% dimnames(data1)[[2]])){ + any.id.col.name <- TRUE + same.col.names.pos2 <- which(dimnames(data2)[[2]] %in% dimnames(data1)[[2]]) + same.col.names.match2 <- match(dimnames(data2)[[2]], dimnames(data1)[[2]]) + } + if(any.id.col.name == TRUE){ + common.col.names <- unique(c(dimnames(data1)[[2]][same.col.names.pos1], dimnames(data2)[[2]][same.col.names.pos2])) + } + } + } + # identical row and col content + if(all(class(data1) == "table")){ + data1 <- as.data.frame(matrix(data1, ncol = ncol(data1)), stringsAsFactors = FALSE) # conversion of table into data frame to facilitate inter class comparison + }else if(all(class(data1) %in% c("matrix", "array"))){ + data1 <- as.data.frame(data1, stringsAsFactors = FALSE) # conversion of matrix into data frame to facilitate inter class comparison + }else if(all(class(data1) == "data.frame")){ + # data1 <- data.frame(lapply(data1, as.character), stringsAsFactors = FALSE) # conversion of columns into characters + } + if(all(class(data2) == "table")){ + data2 <- as.data.frame(matrix(data2, ncol = ncol(data2)), stringsAsFactors = FALSE) # conversion of table into data frame to facilitate inter class comparison + }else if(all(class(data2) %in% c("matrix", "array"))){ + data2 <- as.data.frame(data2, stringsAsFactors = FALSE) # conversion of matrix into data frame to facilitate inter class comparison + }else if(all(class(data2) == "data.frame")){ + # data2 <- data.frame(lapply(data2, as.character), stringsAsFactors = FALSE) # conversion of columns into characters + } + row.names(data1) <- paste0("A", 1:nrow(data1)) + row.names(data2) <- paste0("A", 1:nrow(data2)) + if(same.col.nb == TRUE){ # because if not the same col nb, the row cannot be identical + if(all(sapply(data1, FUN = typeof) == "integer") & all(sapply(data2, FUN = typeof) == "integer") & as.double(nrow(data1)) * nrow(data2) <= 1e10){ # fast method for integers (thus not data frames). as.double(nrow(data1)) to prevent integer overflow because R is 32 bits for integers + tempo1 <- c(as.data.frame(t(data1), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) + tempo2 <- c(as.data.frame(t(data2), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) + same.row.pos1 <- which(tempo1 %in% tempo2) + same.row.pos2 <- which(tempo2 %in% tempo1) + same.row.match1 <- match(tempo1, tempo2) + same.row.match2 <- match(tempo2, tempo1) + }else if(as.double(nrow(data1)) * nrow(data2) <= 1e6){ # as.double(nrow(data1)) to prevent integer overflow because R is 32 bits for integers + # inactivated because I would like to keep the mode during comparisons + # if(col.nb <= 10){ # if ncol is not to big, the t() should not be that long + # tempo1 <- c(as.data.frame(t(data1), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) + # tempo2 <- c(as.data.frame(t(data2), stringsAsFactors = FALSE)) # conversion into list. + # same.row.pos1 <- which(tempo1 %in% tempo2) + # same.row.pos2 <- which(tempo2 %in% tempo1) + # same.row.match1 <- match(tempo1, tempo2) + # same.row.match2 <- match(tempo2, tempo1) + # }else{ + # very long computation + same.row.pos1 <- logical(length = nrow(data1)) # FALSE by default + same.row.pos1[] <- FALSE # security + same.row.pos2 <- logical(length = nrow(data2)) # FALSE by default + same.row.pos2[] <- FALSE # security + same.row.match1 <- rep(NA, nrow(data1)) + same.row.match2 <- rep(NA, nrow(data2)) + for(i3 in 1:nrow(data1)){ + for(i4 in 1:nrow(data2)){ + tempo1 <- data1[i3, ] + tempo2 <- data2[i4, ] + rownames(tempo1) <- NULL # to have same row and column names + colnames(tempo1) <- NULL # to have same row and column names + rownames(tempo2) <- NULL # to have same row and column names + colnames(tempo2) <- NULL # to have same row and column names + if(identical(tempo1, tempo2)){ + same.row.pos1[i3] <- TRUE + same.row.pos2[i4] <- TRUE + same.row.match1[i3] <- i4 + same.row.match2[i4] <- i3 + } + } + } + same.row.pos1 <- which(same.row.pos1) + same.row.pos2 <- which(same.row.pos2) + # } + }else{ + same.row.pos1 <- "TOO BIG FOR EVALUATION" + same.row.pos2 <- "TOO BIG FOR EVALUATION" + same.row.match1 <- "TOO BIG FOR EVALUATION" + same.row.match2 <- "TOO BIG FOR EVALUATION" + } + + names(same.row.pos1) <- NULL + names(same.row.pos2) <- NULL + if(all(is.na(same.row.pos1))){ + same.row.pos1 <- NULL + }else{ + same.row.pos1 <- same.row.pos1[ ! is.na(same.row.pos1)] + any.id.row <- TRUE + } + if(all(is.na(same.row.pos2))){ + same.row.pos2 <- NULL + }else{ + same.row.pos2 <- same.row.pos2[ ! is.na(same.row.pos2)] + any.id.row <- TRUE + } + if(is.null(same.row.pos1) & is.null(same.row.pos2)){ + any.id.row <- FALSE + }else if(length(same.row.pos1) == 0L & length(same.row.pos2) == 0L){ + any.id.row <- FALSE + }else if(all(same.row.pos1 == "TOO BIG FOR EVALUATION") & all(same.row.pos2 == "TOO BIG FOR EVALUATION")){ + any.id.row <- NULL + } + }else{ + any.id.row <- FALSE + # same.row.pos1 and 2 remain NULL + } + if(same.row.nb == TRUE){ # because if not the same row nb, the col cannot be identical + if(as.double(ncol(data1)) * ncol(data2) <= 1e10){ # comparison of data frame columns is much easier than rows because no need to use t() before converting to list for fast comparison. as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers + # if(all(sapply(data1, FUN = typeof) == "integer") & all(sapply(data2, FUN = typeof) == "integer") & as.double(ncol(data1)) * ncol(data2) <= 1e10){ # fast method for integers (thus not data frames). as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers + tempo1 <- c(data1) + tempo2 <- c(data2) + same.col.pos1 <- which(tempo1 %in% tempo2) + same.col.pos2 <- which(tempo2 %in% tempo1) + same.col.match1 <- match(tempo1, tempo2) + same.col.match2 <- match(tempo2, tempo1) + # }else if(as.double(ncol(data1)) * ncol(data2) <= 1e6){ # as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers + # same.col.pos1 <- logical(length = ncol(data1)) # FALSE by default + # same.col.pos1[] <- FALSE # security + # same.col.pos2 <- logical(length = ncol(data2)) # FALSE by default + # same.col.pos2[] <- FALSE # security + # same.col.match1 <- rep(NA, ncol(data1)) + # same.col.match2 <- rep(NA, ncol(data2)) + # for(i3 in 1:ncol(data1)){ + # for(i4 in 1:ncol(data2)){ + # if(identical(data1[ , i3], data2[ , i4])){ + # same.col.pos1[i3] <- TRUE + # same.col.pos2[i4] <- TRUE + # same.col.match1[i3] <- i4 + # same.col.match2[i4] <- i3 + # } + # } + # } + # same.col.pos1 <- which(same.col.pos1) + # same.col.pos2 <- which(same.col.pos2) + }else{ + same.col.pos1 <- "TOO BIG FOR EVALUATION" + same.col.pos2 <- "TOO BIG FOR EVALUATION" + } + names(same.col.pos1) <- NULL + names(same.col.pos2) <- NULL + if(all(is.na(same.col.pos1))){ + same.col.pos1 <- NULL + }else{ + same.col.pos1 <- same.col.pos1[ ! is.na(same.col.pos1)] + any.id.col <- TRUE + } + if(all(is.na(same.col.pos2))){ + same.col.pos2 <- NULL + }else{ + same.col.pos2 <- same.col.pos2[ ! is.na(same.col.pos2)] + any.id.col <- TRUE + } + if(is.null(same.col.pos1) & is.null(same.col.pos2)){ + any.id.col <- FALSE + }else if(length(same.col.pos1) == 0L & length(same.col.pos2) == 0L){ + any.id.col <- FALSE + }else if(all(same.col.pos1 == "TOO BIG FOR EVALUATION") & all(same.col.pos2 == "TOO BIG FOR EVALUATION")){ + any.id.col <- NULL + } + }else{ + any.id.col <- FALSE + # same.col.pos1 and 2 remain NULL + } + if(same.dim == TRUE){ + # names(data1) <- NULL + # row.names(data1) <- NULL + # names(data2) <- NULL + # row.names(data2) <- NULL + # if(identical(data1, data2)){ + # identical.content <- TRUE + # }else{ + # identical.content <- FALSE + # } + # code above inactivated because integer dataset are sometimes imported into R as integer or as double, which is different for identical(data1, data2) + if(all(data1 == data2)){ + identical.content <- TRUE + }else{ + identical.content <- FALSE + } + }else{ + identical.content <- FALSE + } + } + output <- list(same.class = same.class, class = class, same.dim = same.dim, dim = dim, same.row.nb = same.row.nb, row.nb = row.nb, same.col.nb = same.col.nb , col.nb = col.nb, same.row.name = same.row.name, row.name = row.name, any.id.row.name = any.id.row.name, same.row.names.pos1 = same.row.names.pos1, same.row.names.pos2 = same.row.names.pos2, same.row.names.match1 = same.row.names.match1, same.row.names.match2 = same.row.names.match2, common.row.names = common.row.names, same.col.name = same.col.name, col.name = col.name,any.id.col.name = any.id.col.name, same.col.names.pos1 = same.col.names.pos1, same.col.names.pos2 = same.col.names.pos2, same.col.names.match1 = same.col.names.match1, same.col.names.match2 = same.col.names.match2, common.col.names = common.col.names, any.id.row = any.id.row, same.row.pos1 = same.row.pos1, same.row.pos2 = same.row.pos2, same.row.match1 = same.row.match1, same.row.match2 = same.row.match2, any.id.col = any.id.col, same.col.pos1 = same.col.pos1, same.col.pos2 = same.col.pos2, same.col.match1 = same.col.match1, same.col.match2 = same.col.match2, identical.object = identical.object, identical.content = identical.content) + return(output) } -if( ! any(class(data2) %in% c("logical", "integer", "numeric", "character", "factor", "table"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A NON NULL VECTOR, FACTOR OR 1D TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(all(class(data2) %in% "table")){ -if(length(dim(data2)) > 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A 1D TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + + +######## fun_comp_list() #### comparison of two lists + + +fun_comp_list <- function(data1, data2){ + # AIM + # compare two lists. Check and report in a list if the 2 datasets have: + # same length + # common names + # common compartments + # ARGUMENTS + # data1: list + # data2: list + # RETURN + # a list containing: + # $same.length: logical. Are number of elements identical? + # $length: number of elements in the 2 datasets (NULL otherwise) + # $same.names: logical. Are element names identical ? + # $name: name of elements of the 2 datasets if identical (NULL otherwise) + # $any.id.name: logical. Is there any element names identical ? + # $same.names.pos1: positions, in data1, of the element names identical in data2 + # $same.names.pos2: positions, in data2, of the compartment names identical in data1 + # $any.id.compartment: logical. is there any identical compartments ? + # $same.compartment.pos1: positions, in data1, of the compartments identical in data2 + # $same.compartment.pos2: positions, in data2, of the compartments identical in data1 + # $identical.object: logical. Are objects identical (kind of object, compartment names and content)? + # $identical.content: logical. Are content objects identical (identical compartments excluding compartment names)? + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # none + # EXAMPLES + # obs1 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; obs2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; fun_comp_list(obs1, obs2) + # obs1 = list(1:5, LETTERS[1:2]) ; obs2 = list(a = 1:5, b = LETTERS[1:2]) ; fun_comp_list(obs1, obs2) + # obs1 = list(b = 1:5, c = LETTERS[1:2]) ; obs2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; fun_comp_list(obs1, obs2) + # obs1 = list(b = 1:5, c = LETTERS[1:2]) ; obs2 = list(LETTERS[5:9], matrix(1:6), 1:5) ; fun_comp_list(obs1, obs2) + # DEBUGGING + # data1 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; data2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) # for function debugging + # data1 = list(a = 1:5, b = LETTERS[1:2]) ; data2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # argument checking + if( ! any(class(data1) %in% "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! any(class(data2) %in% "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status + # end argument checking + # main code + same.length <- NULL + length <- NULL + same.names <- NULL + name <- NULL + any.id.name <- NULL + same.names.pos1 <- NULL + same.names.pos2 <- NULL + any.id.compartment <- NULL + same.compartment.pos1 <- NULL + same.compartment.pos2 <- NULL + identical.object <- NULL + identical.content <- NULL + if(identical(data1, data2)){ + same.length <- TRUE + length <- length(data1) + if( ! is.null(names(data1))){ + same.names <- TRUE + name <- names(data1) + any.id.name <- TRUE + same.names.pos1 <- 1:length(data1) + same.names.pos2 <- 1:length(data2) + } + any.id.compartment <- TRUE + same.compartment.pos1 <- 1:length(data1) + same.compartment.pos2 <- 1:length(data2) + identical.object <- TRUE + identical.content <- TRUE + }else{ + identical.object <- FALSE + if( ! identical(length(data1), length(data2))){ + same.length<- FALSE + }else{ + same.length<- TRUE + length <- length(data1) + } + if( ! (is.null(names(data1)) & is.null(names(data2)))){ + if( ! identical(names(data1), names(data2))){ + same.names <- FALSE + }else{ + same.names <- TRUE + name <- names(data1) + } + any.id.name <- FALSE + if(any(names(data1) %in% names(data2))){ + any.id.name <- TRUE + same.names.pos1 <- which(names(data1) %in% names(data2)) + } + if(any(names(data2) %in% names(data1))){ + any.id.name <- TRUE + same.names.pos2 <- which(names(data2) %in% names(data1)) + } + } + names(data1) <- NULL + names(data2) <- NULL + any.id.compartment <- FALSE + if(any(data1 %in% data2)){ + any.id.compartment <- TRUE + same.compartment.pos1 <- which(data1 %in% data2) + } + if(any(data2 %in% data1)){ + any.id.compartment <- TRUE + same.compartment.pos2 <- which(data2 %in% data1) + } + if(same.length == TRUE & ! all(is.null(same.compartment.pos1), is.null(same.compartment.pos2))){ + if(identical(same.compartment.pos1, same.compartment.pos2)){ + identical.content <- TRUE + }else{ + identical.content <- FALSE + } + }else{ + identical.content <- FALSE + } + } + output <- list(same.length = same.length, length = length, same.names = same.names, name = name, any.id.name = any.id.name, same.names.pos1 = same.names.pos1, same.names.pos2 = same.names.pos2, any.id.compartment = any.id.compartment, same.compartment.pos1 = same.compartment.pos1, same.compartment.pos2 = same.compartment.pos2, identical.object = identical.object, identical.content = identical.content) + return(output) } + + +######## fun_test() #### test combinations of argument values of a function and return errors (and graphs) + + +# add traceback https://stackoverflow.com/questions/47414119/how-to-read-a-traceback-in-r + +fun_test <- function( + fun, + arg, + val, + expect.error = NULL, + parall = FALSE, + thread.nb = NULL, + print.count = 10, + plot.fun = FALSE, + export = FALSE, + res.path = NULL, + lib.path = NULL, + cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" +){ + # AIM + # test combinations of argument values of a function + # WARNINGS + # Limited to 43 arguments with at least 2 values each. The total number of arguments tested can be more if the additional arguments have a single value. The limit is due to nested "for" loops (https://stat.ethz.ch/pipermail/r-help/2008-March/157341.html), but it should not be a problem since the number of tests would be 2^43 > 8e12 + # ARGUMENTS + # fun: character string indicating the name of the function tested (without brackets) + # arg: vector of character strings of arguments of fun. At least arguments that do not have default values must be present in this vector + # val: list with number of compartments equal to length of arg, each compartment containing values of the corresponding argument in arg. Each different value must be in a list or in a vector. For instance, argument 3 in arg is a logical argument (values accepted TRUE, FALSE, NA). Thus, compartment 3 of val can be either list(TRUE, FALSE, NA), or c(TRUE, FALSE, NA). NULL value alone must be written list(NULL) + # expect.error: list of exactly the same structure as val argument, but containing FALSE or TRUE, depending on whether error is expected (TRUE) or not (FALSE) for each corresponding value of val. A message is returned depending on discrepancies between the expected and observed errors. BEWARE: not always possible to write the expected errors for all the combination of argument values. Ignored if NULL + # parall: logical. Force parallelization ? + # thread.nb: numeric value indicating the number of threads to use if ever parallelization is required. If NULL, all the available threads will be used. Ignored if parall is FALSE + # print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired + # plot.fun: logical. Plot the plotting function tested for each test? + # export: logical. Export the results into a .RData file and into a .txt file? If FALSE, return a list into the console (see below). BEWARE: will be automatically set to TRUE if parall is TRUE. This means that when using parallelization, the results are systematically exported, not returned into the console + # res.path: character string indicating the absolute pathway of folder where the txt results and pdfs, containing all the plots, will be saved. Several txt and pdf, one per thread, if parallelization. Ignored if export is FALSE. Must be specified if parall is TRUE or if export is TRUE + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # cute.path: character string indicating the absolute path of the cute.R file. Will be remove when cute will be a package. Ignored if parall is FALSE + # REQUIRED PACKAGES + # lubridate + # parallel if parall arguemtn is TRUE (included in the R installation packages but not automatically loaded) + # pdftools if parall arguemtn is TRUE (included in the R installation packages but not automatically loaded) + # If the tested function is in a package, this package must be imported first (no parallelization) or must be in the classical R package folder indicated by the lib.path argument (parallelization) + # RETURN + # if export is FALSE a list containing: + # $fun: the tested function + # $instruction: the initial instruction + # $sys.info: system and packages info + # $data: a data frame of all the combination tested, containing the following columns: + # the different values tested, named by arguments + # $kind: a vector of character strings indicating the kind of test result: either "ERROR", or "WARNING", or "OK" + # $problem: a logical vector indicating if error or not + # $expected.error: optional logical vector indicating the expected error specified in the expect.error argument + # $message: either NULL if $kind is always "OK", or the messages + # if export is TRUE 1) the same list object into a .RData file, 2) also the $data data frame into a .txt file, and 3) if expect.error is non NULL and if any discrepancy, the $data data frame into a .txt file but containing only the rows with discrepancies between expected and observed errors + # one or several pdf if a plotting function is tested and if the plot.fun argument is TRUE + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_get_message() + # fun_pack() + # EXAMPLES + # fun_test(fun = "unique", arg = c("x", "incomparables"), val = list(x = list(1:10, c(1,1,2,8), NA), incomparable = c(TRUE, FALSE, NA))) + # fun_test(fun = "fun_round", arg = c("data", "dec.nb", "after.lead.zero"), val = list(L1 = list(c(1, 1.0002256, 1.23568), "a", NA), L2 = list(2, c(1,3), NA), L3 = c(TRUE, FALSE, NA))) + # fun_test(fun = "plot", arg = c("x", "y"), val = list(x = list(1:10, 12:13, NA, (1:10)^2), y = list(1:10, NA, NA)), expect.error = list(x = list(FALSE, TRUE, TRUE, FALSE), y = list(FALSE, TRUE, TRUE)), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = NULL) + # fun_test(fun = "plot", arg = c("x", "y"), val = list(x = list(1:10, 12:13, NA, (1:10)^2), y = list(1:10, NA, NA)), parall = FALSE, thread.nb = 4, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_test(fun = "fun_gg_boxplot", arg = c("data1", "y", "categ"), val = list(L1 = list(L1 = obs1), L2 = list(L1 = "Time"), L3 = list(L1 = "Group1"))) + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_test(fun = "fun_gg_boxplot", arg = c("data1", "y", "categ"), val = list(L1 = list(obs1), L2 = "Time", L3 = "Group1"), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") + # library(ggplot2) ; fun_test(fun = "geom_histogram", arg = c("data", "mapping"), val = list(x = list(data.frame(X = "a", stringsAsFactors = TRUE)), y = list(ggplot2::aes(x = X))), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") # BEWARE: ggplot2::geom_histogram does not work + # DEBUGGING + # fun = "unique" ; arg = "x" ; val = list(x = list(1:10, c(1,1,2,8), NA)) ; expect.error = list(x = list(FALSE, FALSE, TRUE)) ; parall = FALSE ; thread.nb = NULL ; plot.fun = FALSE ; export = FALSE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 1 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging + # fun = "unique" ; arg = c("x", "incomparables") ; val = list(x = list(1:10, c(1,1,2,8), NA), incomparable = c(TRUE, FALSE, NA)) ; expect.error = NULL ; parall = FALSE ; thread.nb = 2 ; plot.fun = FALSE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 10 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging + # fun = "plot" ; arg = c("x", "y") ; val = list(x = list(1:10, 12:13, NA), y = list(1:10, NA, NA)) ; expect.error = list(x = list(FALSE, FALSE, TRUE, FALSE), y = list(FALSE, TRUE, TRUE)) ; print.count = 10 ; parall = FALSE ; thread.nb = NULL ; plot.fun = TRUE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL # for function debugging + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun = "fun_gg_boxplot" ; arg = c("data1", "y", "categ") ; val = list(L1 = list(L1 = obs1), L2 = list(L1 = "Time"), L3 = list(L1 = "Group1")) ; expect.error = NULL ; print.count = 10 ; parall = FALSE ; thread.nb = NULL ; plot.fun = TRUE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL # for function debugging + # fun = "unique" ; arg = "x" ; val = list(list(1:3, mean)) ; expect.error = list(TRUE, TRUE) ; parall = FALSE ; thread.nb = NULL ; plot.fun = FALSE ; export = FALSE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 1 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check", + "fun_get_message", + "fun_pack" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words + # end reserved words + # arg with no default values + mandat.args <- c( + "fun", + "arg", + "val" + ) + tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), "))"))) + print(tempo) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = fun, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = arg, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = val, class = "list", fun.name = function.name) ; eval(ee) + if( ! is.null(expect.error)){ + tempo <- fun_check(data = expect.error, class = "list", fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = parall, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(parall == TRUE){ + if( ! is.null(thread.nb)){ + tempo <- fun_check(data = thread.nb, typeof = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & thread.nb < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": thread.nb PARAMETER MUST EQUAL OR GREATER THAN 1: ", thread.nb) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = plot.fun, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = export, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(res.path)){ + tempo <- fun_check(data = res.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + } + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = cute.path, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # new environment + env.name <- paste0("env", as.numeric(Sys.time())) + if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + assign(env.name, new.env()) + assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test + } + # end new environment + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "fun", + "arg", + "val", + # "expect.erro", # because can be NULL + "parall", + # "thread.nb", # because can be NULL + "print.count", + "plot.fun", + "export", + # "res.path", # because can be NULL + # "lib.path", # because can be NULL + "cute.path" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + # end warning initiation + # other checkings + if(grepl(x = fun, pattern = "()$")){ # remove () + fun <- sub(x = fun, pattern = "()$", replacement = "") + } + if( ! exists(fun)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CHARACTER STRING IN fun ARGUMENT DOES NOT EXIST IN THE R WORKING ENVIRONMENT: ", paste(fun, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + }else if( ! all(base::class(get(fun)) == "function")){ # here no env = sys.nframe(), inherit = FALSE for get() because fun is a function in the classical scope + tempo.cat <- paste0("ERROR IN ", function.name, ": fun ARGUMENT IS NOT CLASS \"function\" BUT: ", paste(base::class(get(fun)), collapse = "\n"), "\nCHECK IF ANY CREATED OBJECT WOULD HAVE THE NAME OF THE TESTED FUNCTION") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(tempo$problem == FALSE & base::length(arg) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": arg ARGUMENT CANNOT BE LENGTH 0") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + for(i2 in 1:base::length(val)){ + tempo1 <- fun_check(data = val[[i2]], class = "vector", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = val[[i2]], class = "list", na.contain = TRUE, fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": COMPARTMENT ", i2, " OF val ARGUMENT MUST BE A VECTOR OR A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + }else if(tempo1$problem == FALSE){ # vector split into list compartments + val[[i2]] <- split(x = val[[i2]], f = 1:base::length(val[[i2]])) + } + } + if(base::length(arg) != base::length(val)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF arg ARGUMENT MUST BE IDENTICAL TO LENGTH OF val ARGUMENT:\nHERE IT IS: ", base::length(arg), " VERSUS ", base::length(val)) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + args <- names(formals(get(fun))) # here no env = sys.nframe(), inherit = FALSE for get() because fun is a function in the classical scope + if( ! all(arg %in% args)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": SOME OF THE STRINGS IN arg ARE NOT ARGUMENTS OF fun\nfun ARGUMENTS: ", paste(args, collapse = " "),"\nPROBLEMATIC STRINGS IN arg: ", paste(arg[ ! arg %in% args], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(sum(sapply(val, FUN = length) > 1) > 43){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CANNOT TEST MORE THAN 43 ARGUMENTS IF THEY ALL HAVE AT LEAST 2 VALUES EACH\nHERE THE NUMBER IS: ", sum(sapply(val, FUN = length) > 1)) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if( ! is.null(expect.error)){ + if(base::length(val) != base::length(expect.error)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF val ARGUMENT MUST BE IDENTICAL TO LENGTH OF expect.error ARGUMENT:\nHERE IT IS: ", base::length(val), " VERSUS ", base::length(expect.error)) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + for(i3 in 1:base::length(expect.error)){ + tempo1 <- fun_check(data = expect.error[[i3]], class = "vector", mode = "logical", fun.name = function.name) + tempo2 <- fun_check(data = expect.error[[i3]], class = "list", fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": COMPARTMENT ", i3, " OF expect.error ARGUMENT MUST BE TRUE OR FALSE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + }else if(tempo1$problem == FALSE){ # vector split into list compartments + expect.error[[i3]] <- split(x = expect.error[[i3]], f = 1:base::length(expect.error[[i3]])) + } + } + } + if( ! is.null(res.path)){ + if( ! all(dir.exists(res.path))){ # separation to avoid the problem of tempo$problem == FALSE and res.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE res.path ARGUMENT DOES NOT EXISTS:\n", paste(res.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + } + if(parall == TRUE & is.null(res.path)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": res.path ARGUMENT MUST BE SPECIFIED IF parall ARGUMENT IS TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(is.null(res.path) & export == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": res.path ARGUMENT MUST BE SPECIFIED IF export ARGUMENT TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(parall == TRUE & export == FALSE){ + export <- TRUE + tempo.cat <- paste0("WARNING FROM ", function.name, ": export ARGUMENT CONVERTED TO TRUE BECAUSE thread.nb ARGUMENT IS NOT NULL") + warning(paste0("\n", tempo.cat, "\n"), call. = FALSE) + } + if( ! is.null(lib.path)){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + } + if(parall == TRUE){ + if(grepl(x = cute.path, pattern = "^http")){ + tempo.error1 <- any(grepl(x = fun_get_message(data = "source(cute.path)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)), pattern = "^[Ee]rror")) + tempo.error2 <- FALSE + }else{ + tempo.error1 <- FALSE + tempo.error2 <- ! file.exists(cute.path) + } + if(tempo.error1 | tempo.error2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(grepl(x = cute.path, pattern = "^http"), "URL", "FILE"), " PATH INDICATED IN THE cute.path PARAMETER DOES NOT EXISTS:\n", cute.path) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + } + # end other checkings + # reserved word checking + # end reserved word checking + # end second round of checking and data preparation + # package checking + fun_pack(req.package = c("lubridate"), lib.path = lib.path) + if(parall == TRUE){ + fun_pack(req.package = c("parallel", "pdftools"), lib.path = lib.path) + } + # end package checking + # declaration of special plot functions + sp.plot.fun <- c("fun_gg_scatter", "fun_gg_bar", "fun_gg_boxplot") + # end declaration of special plot functions + # main code + ini.warning.length <- base::options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + cat("\nfun_test JOB IGNITION\n") + ini.date <- Sys.time() + ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds + if(export == TRUE){ + res.path <- paste0(res.path, "/fun_test_res_", trunc(ini.time)) + if(dir.exists(res.path)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": FOLDER ALREADY EXISTS\n", res.path, "\nPLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + dir.create(res.path) + } + } + total.comp.nb <- prod(sapply(val, FUN = "length")) + cat(paste0("\nTHE TOTAL NUMBER OF TESTS IS: ", total.comp.nb, "\n")) + # creation of the txt instruction that includes several loops + loop.string <- NULL + end.loop.string <- NULL + fun.args <- NULL + fun.args2 <- NULL + error.values <- NULL + arg.values <- "list(" + for(i1 in 1:base::length(arg)){ + if(parall == FALSE){ + if(base::length(val[[i1]]) > 1){ # loop only if more than one value in base::length(val[[i1]]) + loop.string <- paste0(loop.string, "for(i", i1, " in 1:", base::length(val[[i1]]), "){") + end.loop.string <- paste0(end.loop.string, "}") + } + }else{ + loop.string <- "for(i in x){" + end.loop.string <- "}" + } + fun.args <- paste0( + fun.args, + ifelse(i1 == 1L, "", ", "), + arg[i1], + " = val[[", + i1, + "]][[", + if(parall == FALSE){ + if(base::length(val[[i1]]) > 1){ + paste0("i", i1) + }else{ + "1" # a unique element in val[[i1]] + } + }else{ + paste0("i.list[[", i1, "]][i]") + }, + "]]" + ) + fun.args2 <- paste0( + fun.args2, + ifelse(i1 == 1L, "", ", "), + arg[i1], + " = val[[", + i1, + "]][[', ", + if(parall == FALSE){ + if(base::length(val[[i1]]) > 1){ + paste0("i", i1) + }else{ + "1" # a unique element in val[[i1]] + } + }else{ + paste0("i.list[[", i1, "]][i]") + }, + ", ']]" + ) + arg.values <- paste0( + arg.values, + "val[[", i1, "]][[", + if(parall == FALSE){ + if(base::length(val[[i1]]) > 1){ + paste0("i", i1) + }else{ + "1" # a unique element in val[[i1]] + } + }else{ + paste0("i.list[[", i1, "]][i]") + }, + "]]", + ifelse(i1 == base::length(arg), "", ", ") + ) + error.values <- paste0( + error.values, + ifelse(i1 == 1L, "", " | "), + "expect.error[[", i1, "]][[", + if(parall == FALSE){ + if(base::length(expect.error[[i1]]) > 1){ + paste0("i", i1) + }else{ + "1" # a unique element in expect.error[[i1]] + } + }else{ + paste0("i.list[[", i1, "]][i]") + }, + "]]" + ) + } + arg.values <- paste0(arg.values, ")") + fun.test <- paste0(fun, "(", fun.args, ")") + fun.test2 <- paste0("paste0('", fun, "(", fun.args2, ")')") + # plot title for special plot functions + if(plot.fun == TRUE){ + plot.kind <- "classic" + if(fun %in% sp.plot.fun){ + plot.kind <- "special" + if(any(arg %in% "title")){ # this is for the special functions + tempo.match <- regmatches(x = fun.test, m = regexpr(text = fun.test, pattern = "title = .+[,)]")) + tempo.match <- substring(tempo.match , 1, nchar(tempo.match) - 1) + fun.test <- sub(x = fun.test, pattern = tempo.match, replacement = paste0(tempo.match, "\ntempo.title")) + }else{ + fun.test <- sub(x = fun.test, pattern = ")$", replacement = ", title = tempo.title)") + } + } + } + # end plot title for special plot functions + kind <- character() + problem <- logical() + expected.error <- logical() + res <- character() + count <- 0 + print.count.loop <- 0 + plot.count <- 0 + if(base::length(arg) == 1L){ + data <- data.frame() + }else{ # base::length(arg) == 0L already tested above + data <- data.frame(t(vector("character", base::length(arg))), stringsAsFactors = FALSE)[-1, ] # -1 to remove the single row created and to have an empty data frame with base::length(arg) columns + } + code <- paste( + loop.string, ' +count <- count + 1 +print.count.loop <- print.count.loop + 1 +arg.values.print <- eval(parse(text = arg.values)) # recover the list of the i1 compartment +for(j3 in 1:base::length(arg.values.print)){ # WARNING: do not use i1, i2 etc., here because already in loop.string +tempo.capt <- capture.output(tempo.error <- fun_get_message(data = paste0("paste(arg.values.print[[", j3, "]])"), kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE))) # collapsing arg.values sometimes does not work (with function for instance) +if( ! is.null(tempo.error)){ +arg.values.print[[j3]] <- paste0("SPECIAL VALUE OF CLASS ", base::class(arg.values.print[[j3]]), " AND TYPE ", base::typeof(arg.values.print[[j3]])) } -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status -# end argument checking -# main code -same.class <- FALSE -class <- NULL -same.length <- FALSE -length <- NULL -same.levels <- NULL # not FALSE to deal with no factors -levels <- NULL -any.id.levels <- FALSE -same.levels.pos1 <- NULL -same.levels.pos2 <- NULL -same.levels.match1 <- NULL -same.levels.match2 <- NULL -common.levels <- NULL -same.names <- NULL # not FALSE to deal with absence of name -name <- NULL -any.id.name <- FALSE -same.names.pos1 <- NULL -same.names.pos2 <- NULL -same.names.match1 <- NULL -same.names.match2 <- NULL -common.names <- NULL -any.id.element <- FALSE -same.elements.pos1 <- NULL -same.elements.pos2 <- NULL -same.elements.match1 <- NULL -same.elements.match2 <- NULL -common.elements <- NULL -same.order <- NULL -order1 <- NULL -order2 <- NULL -identical.object <- FALSE -identical.content <- FALSE -if(identical(data1, data2)){ -same.class <- TRUE -class <- class(data1) -same.length <- TRUE -length <- length(data1) -if(any(class(data1) %in% "factor")){ -same.levels <- TRUE -levels <- levels(data1) -any.id.levels <- TRUE -same.levels.pos1 <- 1:length(levels(data1)) -same.levels.pos2 <- 1:length(levels(data2)) -same.levels.match1 <- 1:length(levels(data1)) -same.levels.match2 <- 1:length(levels(data2)) -common.levels <- levels(data1) } -if( ! is.null(names(data1))){ -same.names <- TRUE -name <- names(data1) -any.id.name <- TRUE -same.names.pos1 <- 1:length(data1) -same.names.pos2 <- 1:length(data2) -same.names.match1 <- 1:length(data1) -same.names.match2 <- 1:length(data2) -common.names <- names(data1) +data <- rbind(data, as.character(sapply(arg.values.print, FUN = "paste", collapse = " ")), stringsAsFactors = FALSE) # each colum is a test +tempo.capt <- capture.output(tempo.try.error <- fun_get_message(data = eval(parse(text = fun.test2)), kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE))) # data argument needs a character string but eval(parse(text = fun.test2)) provides it (eval parse replace the i1, i2, etc., by the correct values, meaning that only val is required in the env.name environment) +tempo.capt <- capture.output(tempo.try.warning <- fun_get_message(data = eval(parse(text = fun.test2)), kind = "warning", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE), print.no = TRUE)) # data argument needs a character string but eval(parse(text = fun.test2)) provides it (eval parse replace the i1, i2, etc., by the correct values, meaning that only val is required in the env.name environment) +if( ! is.null(expect.error)){ +expected.error <- c(expected.error, eval(parse(text = error.values))) } -any.id.element <- TRUE -same.elements.pos1 <- 1:length(data1) -same.elements.pos2 <- 1:length(data2) -same.elements.match1 <- 1:length(data1) -same.elements.match2 <- 1:length(data2) -common.elements <- data1 -same.order <- TRUE -order1 <- order(data1) -order2 <- order(data2) -identical.object <- TRUE -identical.content <- TRUE +if( ! is.null(tempo.try.error)){ +kind <- c(kind, "ERROR") +problem <- c(problem, TRUE) +res <- c(res, tempo.try.error) }else{ -if(identical(class(data1), class(data2))){ -same.class <- TRUE -class <- class(data1) -} -if(identical(length(data1), length(data2))){ -same.length<- TRUE -length <- length(data1) -} -if(any(class(data1) %in% "factor") & any(class(data2) %in% "factor")){ -if(identical(levels(data1), levels(data2))){ -same.levels <- TRUE -levels <- levels(data1) +if( ! is.null(tempo.try.warning)){ +kind <- c(kind, "WARNING") +problem <- c(problem, FALSE) +res <- c(res, tempo.try.warning) }else{ -same.levels <- FALSE -} -if(any(levels(data1) %in% levels(data2))){ -any.id.levels <- TRUE -same.levels.pos1 <- which(levels(data1) %in% levels(data2)) -same.levels.match1 <- match(levels(data1), levels(data2)) -} -if(any(levels(data2) %in% levels(data1))){ -any.id.levels <- TRUE -same.levels.pos2 <- which(levels(data2) %in% levels(data1)) -same.levels.match2 <- match(levels(data2), levels(data1)) -} -if(any.id.levels == TRUE){ -common.levels <- unique(c(levels(data1)[same.levels.pos1], levels(data2)[same.levels.pos2])) -} -} -if(any(class(data1) %in% "factor")){ # to compare content -data1 <- as.character(data1) -} -if(any(class(data2) %in% "factor")){ # to compare content -data2 <- as.character(data2) +kind <- c(kind, "OK") +problem <- c(problem, FALSE) +res <- c(res, "") } -if( ! (is.null(names(data1)) & is.null(names(data2)))){ -if(identical(names(data1), names(data2))){ -same.names <- TRUE -name <- names(data1) +if(plot.fun == TRUE){ +invisible(dev.set(window.nb)) +plot.count <- plot.count + 1 +tempo.title <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), ifelse(parall == FALSE, count, x[count]))) +if(plot.kind == "classic"){ +eval(parse(text = fun.test)) +tempo <- fun_post_plot(corner.text = tempo.title) +}else if(plot.kind == "special"){ +eval(parse(text = fun.test)) }else{ -same.names <- FALSE -} -if(any(names(data1) %in% names(data2))){ -any.id.name <- TRUE -same.names.pos1 <- which(names(data1) %in% names(data2)) -same.names.match1 <- match(names(data1), names(data2)) -} -if(any(names(data2) %in% names(data1))){ -any.id.name <- TRUE -same.names.pos2 <- which(names(data2) %in% names(data1)) -same.names.match2 <- match(names(data2), names(data1)) -} -if(any.id.name == TRUE){ -common.names <- unique(c(names(data1)[same.names.pos1], names(data2)[same.names.pos2])) +tempo.cat <- paste0("INTERNAL CODE ERROR 1 IN ", function.name, ": CODE HAS TO BE MODIFIED") +stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == } } -names(data1) <- NULL # names solved -> to do not be disturbed by names -names(data2) <- NULL # names solved -> to do not be disturbed by names -if(any(data1 %in% data2)){ -any.id.element <- TRUE -same.elements.pos1 <- which(data1 %in% data2) -same.elements.match1 <- match(data1, data2) } -if(any(data2 %in% data1)){ -any.id.element <- TRUE -same.elements.pos2 <- which(data2 %in% data1) -same.elements.match2 <- match(data2, data1) -} -if(any.id.element == TRUE){ -common.elements <- unique(c(data1[same.elements.pos1], data2[same.elements.pos2])) -} -if(identical(data1, data2)){ -identical.content <- TRUE -same.order <- TRUE -}else if(identical(sort(data1), sort(data2))){ -same.order <- FALSE -order1 <- order(data1) -order2 <- order(data2) +if(print.count.loop == print.count){ +print.count.loop <- 0 +tempo.time <- as.numeric(Sys.time()) +tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) +final.loop <- (tempo.time - ini.time) / count * ifelse(parall == FALSE, total.comp.nb, base::length(x)) # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining +final.exp <- as.POSIXct(final.loop, origin = ini.date) +cat(paste0(ifelse(parall == FALSE, "\n", paste0("\nIN PROCESS ", process.id, " | ")), "LOOP ", format(count, big.mark=","), " / ", format(ifelse(parall == FALSE, total.comp.nb, base::length(x)), big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) } +if(count == ifelse(parall == FALSE, total.comp.nb, base::length(x))){ +tempo.time <- as.numeric(Sys.time()) +tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) +cat(paste0(ifelse(parall == FALSE, "\nLOOP PROCESS ENDED | ", paste0("\nPROCESS ", process.id, " ENDED | ")), "LOOP ", format(count, big.mark=","), " / ", format(ifelse(parall == FALSE, total.comp.nb, base::length(x)), big.mark=","), " | TIME SPENT: ", tempo.lapse, "\n\n")) } -output <- list(same.class = same.class, class = class, same.length = same.length, length = length, same.levels = same.levels, levels = levels, any.id.levels = any.id.levels, same.levels.pos1 = same.levels.pos1, same.levels.pos2 = same.levels.pos2, same.levels.match1 = same.levels.match1, same.levels.match2 = same.levels.match2, common.levels = common.levels, same.names = same.names, name = name, any.id.name = any.id.name, same.names.pos1 = same.names.pos1, same.names.pos2 = same.names.pos2, same.names.match1 = same.names.match1, same.names.match2 = same.names.match2, common.names = common.names, any.id.element = any.id.element, same.elements.pos1 = same.elements.pos1, same.elements.pos2 = same.elements.pos2, same.elements.match1 = same.elements.match1, same.elements.match2 = same.elements.match2, common.elements = common.elements, same.order = same.order, order1 = order1, order2 = order2, identical.object = identical.object, identical.content = identical.content) -return(output) +', +end.loop.string + ) + # end creation of the txt instruction that includes several loops + if(parall == TRUE){ + # list of i numbers that will be split + i.list <- vector("list", base::length(val)) # positions to split in parallel jobs + for(i2 in 1:base::length(arg)){ + if(i2 == 1L){ + tempo.divisor <- total.comp.nb / base::length(val[[i2]]) + i.list[[i2]] <- rep(1:base::length(val[[i2]]), each = as.integer(tempo.divisor)) + tempo.multi <- base::length(val[[i2]]) + }else{ + tempo.divisor <- tempo.divisor / base::length(val[[i2]]) + i.list[[i2]] <- rep(rep(1:base::length(val[[i2]]), each = as.integer(tempo.divisor)), time = as.integer(tempo.multi)) + tempo.multi <- tempo.multi * base::length(val[[i2]]) + } + } + # end list of i numbers that will be split + tempo.cat <- paste0("PARALLELIZATION INITIATED AT: ", ini.date) + cat(paste0("\n", tempo.cat, "\n")) + tempo.thread.nb = parallel::detectCores(all.tests = FALSE, logical = TRUE) # detect the number of threads + if(tempo.thread.nb < thread.nb){ + thread.nb <- tempo.thread.nb + } + tempo.cat <- paste0("NUMBER OF THREADS USED: ", thread.nb) + cat(paste0("\n ", tempo.cat, "\n")) + Clust <- parallel::makeCluster(thread.nb, outfile = paste0(res.path, "/fun_test_parall_log.txt")) # outfile to print or cat during parallelization (only possible in a file, outfile = "" do not work on windows) + tempo.cat <- paste0("SPLIT OF TEST NUMBERS IN PARALLELISATION:") + cat(paste0("\n ", tempo.cat, "\n")) + cluster.list <- parallel::clusterSplit(Clust, 1:total.comp.nb) # split according to the number of cluster + str(cluster.list) # using print(str()) add a NULL below the result + cat("\n") + paral.output.list <- parallel::clusterApply( # paral.output.list is a list made of thread.nb compartments, each made of n / thread.nb (mat theo column number) compartment. Each compartment receive the corresponding results of fun_permut(), i.e., data (permuted mat1.perm), warning message, cor (final correlation) and count (number of permutations) + cl = Clust, + x = cluster.list, + function.name = function.name, + instruction = instruction, + thread.nb = thread.nb, + print.count = print.count, + total.comp.nb = total.comp.nb, + sp.plot.fun = sp.plot.fun, + i.list = i.list, + fun.tested = fun, + arg.values = arg.values, + fun.test = fun.test, + fun.test2 = fun.test2, + kind = kind, + problem = problem, + res = res, + count = count, + plot.count = plot.count, + data = data, + code = code, + plot.fun = plot.fun, + res.path = res.path, + lib.path = lib.path, + cute.path = cute.path, + fun = function( + x, + function.name, + instruction, + thread.nb, + print.count, + total.comp.nb, + sp.plot.fun, + i.list, + fun.tested, + arg.values, + fun.test, + fun.test2, + kind, + problem, + res, + count, + plot.count, + data, + code, + plot.fun, + res.path, + lib.path, + cute.path + ){ + # check again: very important because another R + process.id <- Sys.getpid() + cat(paste0("\nPROCESS ID ", process.id, " -> TESTS ", x[1], " TO ", x[base::length(x)], "\n")) + source(cute.path, local = .GlobalEnv) + fun_pack(req.package = "lubridate", lib.path = lib.path, load = TRUE) # load = TRUE to be sure that functions are present in the environment. And this prevent to use R.lib.path argument of fun_python_pack() + # end check again: very important because another R + # plot management + if(plot.fun == TRUE){ + pdf(file = paste0(res.path, "/plots_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".pdf", paste0("-", x[base::length(x)], ".pdf")))) + }else{ + pdf(file = NULL) # send plots into a NULL file, no pdf file created + } + window.nb <- dev.cur() + invisible(dev.set(window.nb)) + # end plot management + # new environment + ini.date <- Sys.time() + ini.time <- as.numeric(ini.date) # time of process begin, converted into + env.name <- paste0("env", ini.time) + if(exists(env.name, where = -1)){ # verify if still ok when fun_test() is inside a function + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + assign(env.name, new.env()) + assign("val", val, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # var replaced by val + } + # end new environment + print.count.loop <- 0 + suppressMessages(suppressWarnings(eval(parse(text = code)))) + colnames(data) <- arg + if( ! is.null(expect.error)){ + data <- data.frame(data, kind = kind, problem = problem, expected.error = expected.error, message = res, stringsAsFactors = FALSE) + }else{ + data <- data.frame(data, kind = kind, problem = problem, message = res, stringsAsFactors = FALSE) + } + row.names(data) <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), x)) + sys.info <- sessionInfo() + sys.info$loadedOnly <- sys.info$loadedOnly[order(names(sys.info$loadedOnly))] # sort the packages + invisible(dev.off(window.nb)) + rm(env.name) # optional, because should disappear at the end of the function execution + # output + output <- list(fun = fun, instruction = instruction, sys.info = sys.info) # data = data finally removed from the output list, because everything combined in a RData file at the end + save(output, file = paste0(res.path, "/fun_test_", x[1], ifelse(base::length(x) == 1L, ".RData", paste0("-", x[base::length(x)], ".RData")))) + if(plot.fun == TRUE & plot.count == 0L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN PROCESS ", process.id, ": NO PDF PLOT BECAUSE ONLY ERRORS REPORTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + file.remove(paste0(res.path, "/plots_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".pdf", paste0("-", x[base::length(x)], ".pdf")))) + } + table.out <- as.matrix(data) + # table.out[table.out == ""] <- " " # does not work # because otherwise read.table() converts "" into NA + table.out <- gsub(table.out, pattern = "\n", replacement = " ") + write.table(table.out, file = paste0(res.path, "/table_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".txt", paste0("-", x[base::length(x)], ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") + } + ) + parallel::stopCluster(Clust) + # files assembly + if(base::length(cluster.list) > 1){ + for(i2 in 1:base::length(cluster.list)){ + tempo.file <- paste0(res.path, "/table_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".txt", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".txt"))) # txt file + tempo <- read.table(file = tempo.file, header = TRUE, stringsAsFactors = FALSE, sep = "\t", row.names = 1, comment.char = "", colClasses = "character") # row.names = 1 (1st column) because now read.table() adds a NA in the header if the header starts by a tabulation, comment.char = "" because colors with #, colClasses = "character" otherwise convert "" (from NULL) into NA + if(file.exists(paste0(res.path, "/plots_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".pdf", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".pdf"))))){ + tempo.pdf <- paste0(res.path, "/plots_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".pdf", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".pdf"))) # pdf file + }else{ + tempo.pdf <- NULL + } + tempo.rdata <- paste0(res.path, "/fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".RData", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".RData"))) # RData file + if(i2 == 1L){ + final.file <- tempo + final.pdf <- tempo.pdf + # new env for RData combining + env.name <- paste0("env", ini.time) + if(exists(env.name, where = -1)){ # verify if still ok when fun_test() is inside a function + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + # end new env for RData combining + }else{ + assign(env.name, new.env()) + load(tempo.rdata, envir = get(env.name)) + tempo.rdata1 <- tempo.rdata + assign("final.output", get("output", envir = get(env.name)), envir = get(env.name)) + } + }else{ + final.file <- rbind(final.file, tempo, stringsAsFactors = TRUE) + final.pdf <- c(final.pdf, tempo.pdf) + load(tempo.rdata, envir = get(env.name)) + if( ! identical(get("final.output", envir = get(env.name))[c("R.version", "locale", "platform")], get("output", envir = get(env.name))[c("R.version", "locale", "platform")])){ + tempo.cat <- paste0("ERROR IN ", function.name, ": DIFFERENCE BETWEEN OUTPUTS WHILE THEY SHOULD BE IDENTICAL\nPLEASE CHECK\n", tempo.rdata1, "\n", tempo.rdata) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + # add the differences in RData $sysinfo into final.output + tempo.base1 <- sort(get("final.output", envir = get(env.name))$sys.info$basePkgs) + tempo.base2 <- sort(get("output", envir = get(env.name))$sys.info$basePkgs) + tempo.other1 <- names(get("final.output", envir = get(env.name))$sys.info$otherPkgs) + tempo.other2 <- names(get("output", envir = get(env.name))$sys.info$otherPkgs) + tempo.loaded1 <- names(get("final.output", envir = get(env.name))$sys.info$loadedOnly) + tempo.loaded2 <- names(get("output", envir = get(env.name))$sys.info$loadedOnly) + assign("final.output", { + x <- get("final.output", envir = get(env.name)) + y <- get("output", envir = get(env.name)) + x$sys.info$basePkgs <- sort(unique(tempo.base1, tempo.base2)) + if( ! all(tempo.other2 %in% tempo.other1)){ + x$sys.info$otherPkgs <- c(x$sys.info$otherPkgs, y$sys.info$otherPkgs[ ! (tempo.other2 %in% tempo.other1)]) + x$sys.info$otherPkgs <- x$sys.info$otherPkgs[order(names(x$sys.info$otherPkgs))] + } + if( ! all(tempo.loaded2 %in% tempo.loaded1)){ + x$sys.info$loadedOnly <- c(x$sys.info$loadedOnly, y$sys.info$loadedOnly[ ! (tempo.loaded2 %in% tempo.loaded1)]) + x$sys.info$loadedOnly <- x$sys.info$loadedOnly[order(names(x$sys.info$loadedOnly))] + } + x + }, envir = get(env.name)) + # add the differences in RData $sysinfo into final.output + } + } + file.remove(c(tempo.file, tempo.rdata)) + } + # combine pdf and save + if( ! is.null(final.pdf)){ + pdftools::pdf_combine( + input = final.pdf, + output = paste0(res.path, "/plots_from_fun_test_1-", total.comp.nb, ".pdf") + ) + file.remove(final.pdf) + } + # end combine pdf and save + # save RData + assign("output", c(get("final.output", envir = get(env.name)), data = list(final.file)), envir = get(env.name)) + save(output, file = paste0(res.path, "/fun_test__1-", total.comp.nb, ".RData"), envir = get(env.name)) + rm(env.name) # optional, because should disappear at the end of the function execution + # end save RData + # save txt + write.table(final.file, file = paste0(res.path, "/table_from_fun_test_1-", total.comp.nb, ".txt"), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") + # end save txt + if( ! is.null(expect.error)){ + final.file <- final.file[ ! final.file$problem == final.file$expected.error, ] + if(nrow(final.file) == 0L){ + cat(paste0("NO DISCREPANCY BETWEEN EXPECTED AND OBSERVED ERRORS\n\n")) + }else{ + cat(paste0("DISCREPANCIES BETWEEN EXPECTED AND OBSERVED ERRORS (SEE THE discrepancy_table_from_fun_test_1-", total.comp.nb, ".txt FILE)\n\n")) + write.table(final.file, file = paste0(res.path, "/discrepancy_table_from_fun_test_1-", total.comp.nb, ".txt"), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") + } + } + } + # end files assembly + }else{ + # plot management + if(plot.fun == TRUE){ + pdf(file = paste0(res.path, "/plots_from_fun_test_1", ifelse(total.comp.nb == 1L, ".pdf", paste0("-", total.comp.nb, ".pdf")))) + }else{ + pdf(file = NULL) # send plots into a NULL file, no pdf file created + } + window.nb <- dev.cur() + invisible(dev.set(window.nb)) + # end plot management + # new environment + env.name <- paste0("env", ini.time) + if(exists(env.name, where = -1)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + assign(env.name, new.env()) + assign("val", val, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # var replaced by val + } + # end new environment + suppressMessages(suppressWarnings(eval(parse(text = code)))) + colnames(data) <- arg + expect.data <- data.frame() + if( ! is.null(expect.error)){ + data <- data.frame(data, kind = kind, problem = problem, expected.error = expected.error, message = res, stringsAsFactors = FALSE) + }else{ + data <- data.frame(data, kind = kind, problem = problem, message = res, stringsAsFactors = FALSE) + } + row.names(data) <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), 1:total.comp.nb)) + sys.info <- sessionInfo() + sys.info$loadedOnly <- sys.info$loadedOnly[order(names(sys.info$loadedOnly))] # sort the packages + invisible(dev.off(window.nb)) + rm(env.name) # optional, because should disappear at the end of the function execution + # output + output <- list(fun = fun, instruction = instruction, sys.info = sys.info, data = data) + if(plot.fun == TRUE & plot.count == 0L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO PDF PLOT BECAUSE ONLY ERRORS REPORTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + file.remove(paste0(res.path, "/plots_from_fun_test_1", ifelse(total.comp.nb == 1L, ".pdf", paste0("-", total.comp.nb, ".pdf")))) + } + if( ! is.null(expect.error)){ + expect.data <- output$data[ ! output$data$problem == output$data$expected.error, ] + if(nrow(expect.data) == 0L){ + cat(paste0("NO DISCREPANCY BETWEEN EXPECTED AND OBSERVED ERRORS\n\n")) + }else{ + cat(paste0("DISCREPANCIES BETWEEN EXPECTED AND OBSERVED ERRORS (SEE THE ", if(export == TRUE){paste0("discrepancy_table_from_fun_test_1", ifelse(total.comp.nb == 1L, "", paste0("-", total.comp.nb)), ".txt FILE")}else{"$data RESULT"}, ")\n\n")) + if(export == TRUE){ + expect.data <- as.matrix(expect.data) + expect.data <- gsub(expect.data, pattern = "\n", replacement = " ") + write.table(expect.data, file = paste0(res.path, "/discrepancy_table_from_fun_test_1", ifelse(total.comp.nb == 1L, ".txt", paste0("-", total.comp.nb, ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") + } + } + } + if( ! is.null(warn)){ + base::options(warning.length = 8170) + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = base::options(warning.length = ini.warning.length), add = TRUE) + if(export == TRUE){ + save(output, file = paste0(res.path, "/fun_test_1", ifelse(total.comp.nb == 1L, ".RData", paste0("-", total.comp.nb, ".RData")))) + table.out <- as.matrix(output$data) + table.out <- gsub(table.out, pattern = "\n", replacement = " ") + write.table(table.out, file = paste0(res.path, "/table_from_fun_test_1", ifelse(total.comp.nb == 1L, ".txt", paste0("-", total.comp.nb, ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") + }else{ + return(output) + } + } + # after return() ? + end.date <- Sys.time() + end.time <- as.numeric(end.date) + total.lapse <- round(lubridate::seconds_to_period(end.time - ini.time)) + cat(paste0("fun_test JOB END\n\nTIME: ", end.date, "\n\nTOTAL TIME LAPSE: ", total.lapse, "\n\n\n")) } -######## fun_comp_2d() #### comparison of two 2D datasets (row & col names, dimensions, etc.) +################ Object modification -fun_comp_2d <- function(data1, data2){ -# AIM -# compare two 2D datasets of the same class or not. Check and report in a list if the 2 datasets have: -# same class -# common row names -# common column names -# same row number -# same column number -# potential identical rows between the 2 datasets -# potential identical columns between the 2 datasets -# WARNINGS -# For data frames: content are compared after conversion of content into characters. This means that the comparison of the content of data frame, either row to row, or column to column, does not take into account the mode in the different columns. This concern the results in $any.id.row, $same.row.pos1, $same.row.pos2, $same.row.match1, $same.row.match2, $any.id.col, $same.row.col1, $same.row.col2, $same.col.match1, $same.col.match2 and $identical.content result -# "TOO BIG FOR EVALUATION" returned in $same.row.pos1, $same.row.pos2, $same.row.match1 and $same.row.match2 when nrow(data1) * nrow(data2) > 1e6 and $any.id.row remains NULL -# "TOO BIG FOR EVALUATION" returned in $same.row.col1, $same.row.col2, $same.col.match1 and $same.col.match2 when ncol(data1) * ncol(data2) > 1e6 and $any.id.col remains NULL -# ARGUMENTS -# data1: matrix, data frame or table -# data2: matrix, data frame or table -# RETURN -# a list containing: -# $same.class: logical. Are class identical ? -# $class: classes of the 2 datasets (NULL otherwise) -# $same.dim: logical. Are dimension identical ? -# $dim: dimension of the 2 datasets (NULL otherwise) -# $same.row.nb: logical. Are number of rows identical ? -# $row.nb: nb of rows of the 2 datasets if identical (NULL otherwise) -# $same.col.nb: logical. Are number of columns identical ? -# $col.nb: nb of columns of the 2 datasets if identical (NULL otherwise) -# $same.row.name: logical. Are row names identical ? NULL if no row names in the two 2D datasets -# $row.name: name of rows of the 2 datasets if identical (NULL otherwise) -# $any.id.row.name: logical. Is there any row names identical ? NULL if no row names in the two 2D datasets -# $same.row.names.pos1: positions, in data1, of the row names identical in data2 -# $same.row.names.pos2: positions, in data2, of the row names identical in data1 -# $same.row.names.match1: positions, in data2, of the row names that match the row names in data1, as given by match(data1, data2) (NULL otherwise) -# $same.row.names.match2: positions, in data1, of the row names that match the row names in data2, as given by match(data1, data2) (NULL otherwise) -# $common.row.names: common row names between data1 and data2 (can be a subset of $name or not). NULL if no common row names -# $same.col.name: logical. Are column names identical ? NULL if no col names in the two 2D datasets -# $col.name: name of columns of the 2 datasets if identical (NULL otherwise) -# $any.id.col.name: logical. Is there any column names identical ? NULL if no col names in the two 2D datasets -# $same.col.names.pos1: positions, in data1, of the column names identical in data2 -# $same.col.names.pos2: positions, in data2, of the column names identical in data1 -# $same.col.names.match1: positions, in data2, of the column names that match the column names in data1, as given by match(data1, data2) (NULL otherwise) -# $same.col.names.match2: positions, in data1, of the column names that match the column names in data2, as given by match(data1, data2) (NULL otherwise) -# $common.col.names: common column names between data1 and data2 (can be a subset of $name or not). NULL if no common column names -# $any.id.row: logical. is there identical rows (not considering row names)? NULL if nrow(data1) * nrow(data2) > 1e10 -# $same.row.pos1: positions, in data1, of the rows identical in data2 (not considering row names). Return "TOO BIG FOR EVALUATION" if nrow(data1) * nrow(data2) > 1e10 -# $same.row.pos2: positions, in data2, of the rows identical in data1 (not considering row names). Return "TOO BIG FOR EVALUATION" if nrow(data1) * nrow(data2) > 1e10 -# $same.row.match1: positions, in data2, of the rows that match the rows in data1, as given by match(data1, data2) (NULL otherwise) -# $same.row.match2: positions, in data1, of the rows that match the rows in data2, as given by match(data1, data2) (NULL otherwise) -# $any.id.col: logical. is there identical columns (not considering column names)? NULL if ncol(data1) * ncol(data2) > 1e10 -# $same.col.pos1: position in data1 of the cols identical in data2 (not considering column names). Return "TOO BIG FOR EVALUATION" if ncol(data1) * ncol(data2) > 1e10 -# $same.col.pos2: position in data2 of the cols identical in data1 (not considering column names). Return "TOO BIG FOR EVALUATION" if ncol(data1) * ncol(data2) > 1e10 -# $same.col.match1: positions, in data2, of the columns that match the columns in data1, as given by match(data1, data2) (NULL otherwise) -# $same.row.match2: positions, in data1, of the columns that match the columns in data2, as given by match(data1, data2) (NULL otherwise) -# $identical.object: logical. Are objects identical (including row & column names)? -# $identical.content: logical. Are content objects identical (identical excluding row & column names)? -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# none -# EXAMPLES -# obs1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = as.data.frame(matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])), stringsAsFactors = TRUE) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) -# obs1 = matrix(101:110, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) -# large matrices -# obs1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; obs2 = matrix(as.integer((1:1e6)+1e6/5), ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; head(obs1) ; head(obs2) ; fun_comp_2d(obs1, obs2) -# WARNING: when comparing content (rows, columns, or total), double and integer data are considered as different -> double(1) != integer(1) -# obs1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; obs2 = matrix((1:1e6)+1e6/5, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; head(obs1) ; head(obs2) ; fun_comp_2d(obs1, obs2) -# Matrices: same row conten tand same row names -# obs1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4]))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) -# Matrices: same row content but not same row names -> works: same content is identified -# obs1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; obs2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("x", "z", "y"), c(LETTERS[1:2], "k", LETTERS[5:4]))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) -# obs1 = t(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = t(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1 ; obs2 ; fun_comp_2d(obs1, obs2) -# Data frames: same row content and same row names, not same mode between columns -# obs1 = as.data.frame(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = as.data.frame(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1[, 5] <- as.character(obs1[, 5]) ; obs2[, 5] <- as.character(obs2[, 5]) ; obs1 ; obs2 ; str(obs1) ; str(obs2) ; fun_comp_2d(obs1, obs2) -# Data frames: same row content but not same row names -> works: same content is identified -# obs1 = as.data.frame(matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5]))) ; obs2 = as.data.frame(matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("x", "z", "y"), c(LETTERS[1:2], "k", LETTERS[5:4])))) ; obs1[, 5] <- as.character(obs1[, 5]) ; obs2[, 5] <- as.character(obs2[, 5]) ; obs1 ; obs2 ; str(obs1) ; str(obs2) ; fun_comp_2d(obs1, obs2) -# DEBUGGING -# data1 = matrix(1:10, ncol = 5) ; data2 = matrix(1:10, ncol = 5) # for function debugging -# data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5) # for function debugging -# data1 = matrix(1:15, byrow = TRUE, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# data1 = matrix(1:15, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# data1 = matrix(1:15, ncol = 5, dimnames = list(paste0("A", letters[1:3]), LETTERS[1:5])) ; data2 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# data1 = matrix(1:15, ncol = 5, dimnames = list(letters[1:3], LETTERS[1:5])) ; data2 = matrix(1:12, ncol = 4, dimnames = list(letters[1:3], LETTERS[1:4])) # for function debugging -# data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(101:110, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) # for function debugging -# data1 = data.frame(a = 1:3, b= letters[1:3], row.names = LETTERS[1:3], stringsAsFactors = TRUE) ; data2 = data.frame(A = 1:3, B= letters[1:3], stringsAsFactors = TRUE) # for function debugging -# data1 = matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = as.data.frame(matrix(1:10, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])), stringsAsFactors = TRUE) # for function debugging -# data1 = matrix(1:10, byrow = TRUE, ncol = 5, dimnames = list(letters[1:2], LETTERS[1:5])) ; data2 = matrix(c(1:5, 101:105, 6:10), byrow = TRUE, ncol = 5, dimnames = list(c("a", "z", "b"), c(LETTERS[1:2], "k", LETTERS[5:4]))) # for function debugging -# data1 = table(Exp1 = c("A", "A", "A", "B", "B", "B"), Exp2 = c("A1", "B1", "A1", "C1", "C1", "B1")) ; data2 = data.frame(A = 1:3, B= letters[1:3], stringsAsFactors = TRUE) # for function debugging -# data1 = matrix(1:1e6, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) ; data2 = matrix((1:1e6)+1e6/5, ncol = 5, dimnames = list(NULL, LETTERS[1:5])) -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# argument checking -if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A MATRIX, DATA FRAME OR TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (any(class(data2) %in% c("data.frame", "table")) | all(class(data2) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data2) %in% c("matrix", "data.frame", "table")) -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A MATRIX, DATA FRAME OR TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status -# end argument checking -# main code -same.class <- NULL -class <- NULL -same.dim <- NULL -dim <- NULL -same.row.nb <- NULL -row.nb <- NULL -same.col.nb <- NULL -col.nb <- NULL -same.row.name <- NULL -row.name <- NULL -any.id.row.name <- NULL -same.row.names.pos1 <- NULL -same.row.names.pos2 <- NULL -same.row.names.match1 <- NULL -same.row.names.match2 <- NULL -common.row.names <- NULL -same.col.name <- NULL -any.id.col.name <- NULL -same.col.names.pos1 <- NULL -same.col.names.pos2 <- NULL -same.col.names.match1 <- NULL -same.col.names.match2 <- NULL -common.col.names <- NULL -col.name <- NULL -any.id.row <- NULL -same.row.pos1 <- NULL -same.row.pos2 <- NULL -same.row.match1 <- NULL -same.row.match2 <- NULL -any.id.col <- NULL -same.col.pos1 <- NULL -same.col.pos2 <- NULL -same.col.match1 <- NULL -same.col.match2 <- NULL -identical.object <- NULL -identical.content <- NULL -if(identical(data1, data2) & (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) -same.class <- TRUE -class <- class(data1) -same.dim <- TRUE -dim <- dim(data1) -same.row.nb <- TRUE -row.nb <- nrow(data1) -same.col.nb <- TRUE -col.nb <- ncol(data1) -same.row.name <- TRUE -row.name <- dimnames(data1)[[1]] -any.id.row.name <- TRUE -same.row.names.pos1 <- 1:row.nb -same.row.names.pos2 <- 1:row.nb -same.row.names.match1 <- 1:row.nb -same.row.names.match2 <- 1:row.nb -common.row.names <- dimnames(data1)[[1]] -same.col.name <- TRUE -col.name <- dimnames(data1)[[2]] -any.id.col.name <- TRUE -same.col.names.pos1 <- 1:col.nb -same.col.names.pos2 <- 1:col.nb -same.col.names.match1 <- 1:col.nb -same.col.names.match2 <- 1:col.nb -common.col.names <- dimnames(data1)[[2]] -any.id.row <- TRUE -same.row.pos1 <- 1:row.nb -same.row.pos2 <- 1:row.nb -same.row.match1 <- 1:row.nb -same.row.match2 <- 1:row.nb -any.id.col <- TRUE -same.col.pos1 <- 1:col.nb -same.col.pos2 <- 1:col.nb -same.col.match1 <- 1:col.nb -same.col.match2 <- 1:col.nb -identical.object <- TRUE -identical.content <- TRUE -}else{ -identical.object <- FALSE -if(all(class(data1) == "table") & length(dim(data1)) == 1L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT IS A 1D TABLE. USE THE fun_comp_1d FUNCTION") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(all(class(data2) == "table") & length(dim(data2)) == 1L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT IS A 1D TABLE. USE THE fun_comp_1d FUNCTION") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! identical(class(data1), class(data2))){ -same.class <- FALSE -}else if( ! (any(class(data1) %in% c("data.frame", "table")) | all(class(data1) %in% c("matrix", "array")))){ # before R4.0.0, it was ! any(class(data1) %in% c("matrix", "data.frame", "table")) -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 AND data2 ARGUMENTS MUST BE EITHER MATRIX, DATA FRAME OR TABLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -same.class <- TRUE -class <- class(data1) -} -if( ! identical(dim(data1), dim(data2))){ -same.dim <- FALSE -}else{ -same.dim <- TRUE -dim <- dim(data1) -} -if( ! identical(nrow(data1), nrow(data2))){ -same.row.nb <- FALSE -}else{ -same.row.nb <- TRUE -row.nb <- nrow(data1) -} -if( ! identical(ncol(data1), ncol(data2))){ -same.col.nb <- FALSE -}else{ -same.col.nb <- TRUE -col.nb <- ncol(data1) -} -# row and col names -if(is.null(dimnames(data1)) & is.null(dimnames(data2))){ -same.row.name <- NULL # but already NULL -same.col.name <- NULL # but already NULL -# other row names param remain NULL -}else if((is.null(dimnames(data1)) & ! is.null(dimnames(data2))) | ( ! is.null(dimnames(data1)) & is.null(dimnames(data2)))){ -same.row.name <- FALSE -same.col.name <- FALSE -any.id.row.name <- FALSE -any.id.col.name <- FALSE -# other row names param remain NULL -}else{ -# row names -if(is.null(dimnames(data1)[[1]]) & is.null(dimnames(data2)[[1]])){ -same.row.name <- NULL # but already NULL -# other row names param remain NULL -}else if((is.null(dimnames(data1)[[1]]) & ! is.null(dimnames(data2)[[1]])) | ( ! is.null(dimnames(data1)[[1]]) & is.null(dimnames(data2)[[1]]))){ -same.row.name <- FALSE -any.id.row.name <- FALSE -# other row names param remain NULL -}else if(identical(dimnames(data1)[[1]], dimnames(data2)[[1]])){ -same.row.name <- TRUE -row.name <- dimnames(data1)[[1]] -any.id.row.name <- TRUE -same.row.names.pos1 <- 1:nrow(data1) -same.row.names.pos2 <- 1:nrow(data1) -same.row.names.match1 <- 1:nrow(data1) -same.row.names.match2 <- 1:nrow(data1) -common.row.names <- dimnames(data1)[[1]] -}else{ -same.row.name <- FALSE -any.id.row.name <- FALSE -if(any(dimnames(data1)[[1]] %in% dimnames(data2)[[1]])){ -any.id.row.name <- TRUE -same.row.names.pos1 <- which(dimnames(data1)[[1]] %in% dimnames(data2)[[1]]) -same.row.names.match1 <- match(dimnames(data1)[[1]], dimnames(data2)[[1]]) -} -if(any(dimnames(data2)[[1]] %in% dimnames(data1)[[1]])){ -any.id.row.name <- TRUE -same.row.names.pos2 <- which(dimnames(data2)[[1]] %in% dimnames(data1)[[1]]) -same.row.names.match2 <- match(dimnames(data2)[[1]], dimnames(data1)[[1]]) -} -if(any.id.row.name == TRUE){ -common.row.names <- unique(c(dimnames(data1)[[1]][same.row.names.pos1], dimnames(data2)[[1]][same.row.names.pos2])) -} -} -# col names -if(is.null(dimnames(data1)[[2]]) & is.null(dimnames(data2)[[2]])){ -same.col.name <- NULL # but already NULL -# other col names param remain NULL -}else if((is.null(dimnames(data1)[[2]]) & ! is.null(dimnames(data2)[[2]])) | ( ! is.null(dimnames(data1)[[2]]) & is.null(dimnames(data2)[[2]]))){ -same.col.name <- FALSE -any.id.col.name <- FALSE -# other col names param remain NULL -}else if(identical(dimnames(data1)[[2]], dimnames(data2)[[2]])){ -same.col.name <- TRUE -col.name <- dimnames(data1)[[2]] -any.id.col.name <- TRUE -same.col.names.pos1 <- 1:ncol(data1) -same.col.names.pos2 <- 1:ncol(data1) -same.col.names.match1 <- 1:ncol(data1) -same.col.names.match2 <- 1:ncol(data1) -common.col.names <- dimnames(data1)[[2]] -}else{ -same.col.name <- FALSE -any.id.col.name <- FALSE -if(any(dimnames(data1)[[2]] %in% dimnames(data2)[[2]])){ -any.id.col.name <- TRUE -same.col.names.pos1 <- which(dimnames(data1)[[2]] %in% dimnames(data2)[[2]]) -same.col.names.match1 <- match(dimnames(data1)[[2]], dimnames(data2)[[2]]) -} -if(any(dimnames(data2)[[2]] %in% dimnames(data1)[[2]])){ -any.id.col.name <- TRUE -same.col.names.pos2 <- which(dimnames(data2)[[2]] %in% dimnames(data1)[[2]]) -same.col.names.match2 <- match(dimnames(data2)[[2]], dimnames(data1)[[2]]) -} -if(any.id.col.name == TRUE){ -common.col.names <- unique(c(dimnames(data1)[[2]][same.col.names.pos1], dimnames(data2)[[2]][same.col.names.pos2])) -} -} -} -# identical row and col content -if(all(class(data1) == "table")){ -data1 <- as.data.frame(matrix(data1, ncol = ncol(data1)), stringsAsFactors = FALSE) # conversion of table into data frame to facilitate inter class comparison -}else if(all(class(data1) %in% c("matrix", "array"))){ -data1 <- as.data.frame(data1, stringsAsFactors = FALSE) # conversion of matrix into data frame to facilitate inter class comparison -}else if(all(class(data1) == "data.frame")){ -# data1 <- data.frame(lapply(data1, as.character), stringsAsFactors = FALSE) # conversion of columns into characters -} -if(all(class(data2) == "table")){ -data2 <- as.data.frame(matrix(data2, ncol = ncol(data2)), stringsAsFactors = FALSE) # conversion of table into data frame to facilitate inter class comparison -}else if(all(class(data2) %in% c("matrix", "array"))){ -data2 <- as.data.frame(data2, stringsAsFactors = FALSE) # conversion of matrix into data frame to facilitate inter class comparison -}else if(all(class(data2) == "data.frame")){ -# data2 <- data.frame(lapply(data2, as.character), stringsAsFactors = FALSE) # conversion of columns into characters -} -row.names(data1) <- paste0("A", 1:nrow(data1)) -row.names(data2) <- paste0("A", 1:nrow(data2)) -if(same.col.nb == TRUE){ # because if not the same col nb, the row cannot be identical -if(all(sapply(data1, FUN = typeof) == "integer") & all(sapply(data2, FUN = typeof) == "integer") & as.double(nrow(data1)) * nrow(data2) <= 1e10){ # fast method for integers (thus not data frames). as.double(nrow(data1)) to prevent integer overflow because R is 32 bits for integers -tempo1 <- c(as.data.frame(t(data1), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) -tempo2 <- c(as.data.frame(t(data2), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) -same.row.pos1 <- which(tempo1 %in% tempo2) -same.row.pos2 <- which(tempo2 %in% tempo1) -same.row.match1 <- match(tempo1, tempo2) -same.row.match2 <- match(tempo2, tempo1) -}else if(as.double(nrow(data1)) * nrow(data2) <= 1e6){ # as.double(nrow(data1)) to prevent integer overflow because R is 32 bits for integers -# inactivated because I would like to keep the mode during comparisons -# if(col.nb <= 10){ # if ncol is not to big, the t() should not be that long -# tempo1 <- c(as.data.frame(t(data1), stringsAsFactors = FALSE)) # conversion into list. This work fast with only integers (because 32 bits) -# tempo2 <- c(as.data.frame(t(data2), stringsAsFactors = FALSE)) # conversion into list. -# same.row.pos1 <- which(tempo1 %in% tempo2) -# same.row.pos2 <- which(tempo2 %in% tempo1) -# same.row.match1 <- match(tempo1, tempo2) -# same.row.match2 <- match(tempo2, tempo1) -# }else{ -# very long computation -same.row.pos1 <- logical(length = nrow(data1)) # FALSE by default -same.row.pos1[] <- FALSE # security -same.row.pos2 <- logical(length = nrow(data2)) # FALSE by default -same.row.pos2[] <- FALSE # security -same.row.match1 <- rep(NA, nrow(data1)) -same.row.match2 <- rep(NA, nrow(data2)) -for(i3 in 1:nrow(data1)){ -for(i4 in 1:nrow(data2)){ -tempo1 <- data1[i3, ] -tempo2 <- data2[i4, ] -rownames(tempo1) <- NULL # to have same row and column names -colnames(tempo1) <- NULL # to have same row and column names -rownames(tempo2) <- NULL # to have same row and column names -colnames(tempo2) <- NULL # to have same row and column names -if(identical(tempo1, tempo2)){ -same.row.pos1[i3] <- TRUE -same.row.pos2[i4] <- TRUE -same.row.match1[i3] <- i4 -same.row.match2[i4] <- i3 -} -} -} -same.row.pos1 <- which(same.row.pos1) -same.row.pos2 <- which(same.row.pos2) -# } -}else{ -same.row.pos1 <- "TOO BIG FOR EVALUATION" -same.row.pos2 <- "TOO BIG FOR EVALUATION" -same.row.match1 <- "TOO BIG FOR EVALUATION" -same.row.match2 <- "TOO BIG FOR EVALUATION" -} +######## fun_name_change() #### check a vector of character strings and modify any string if present in another vector -names(same.row.pos1) <- NULL -names(same.row.pos2) <- NULL -if(all(is.na(same.row.pos1))){ -same.row.pos1 <- NULL -}else{ -same.row.pos1 <- same.row.pos1[ ! is.na(same.row.pos1)] -any.id.row <- TRUE -} -if(all(is.na(same.row.pos2))){ -same.row.pos2 <- NULL -}else{ -same.row.pos2 <- same.row.pos2[ ! is.na(same.row.pos2)] -any.id.row <- TRUE -} -if(is.null(same.row.pos1) & is.null(same.row.pos2)){ -any.id.row <- FALSE -}else if(length(same.row.pos1) == 0L & length(same.row.pos2) == 0L){ -any.id.row <- FALSE -}else if(all(same.row.pos1 == "TOO BIG FOR EVALUATION") & all(same.row.pos2 == "TOO BIG FOR EVALUATION")){ -any.id.row <- NULL -} -}else{ -any.id.row <- FALSE -# same.row.pos1 and 2 remain NULL -} -if(same.row.nb == TRUE){ # because if not the same row nb, the col cannot be identical -if(as.double(ncol(data1)) * ncol(data2) <= 1e10){ # comparison of data frame columns is much easier than rows because no need to use t() before converting to list for fast comparison. as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers -# if(all(sapply(data1, FUN = typeof) == "integer") & all(sapply(data2, FUN = typeof) == "integer") & as.double(ncol(data1)) * ncol(data2) <= 1e10){ # fast method for integers (thus not data frames). as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers -tempo1 <- c(data1) -tempo2 <- c(data2) -same.col.pos1 <- which(tempo1 %in% tempo2) -same.col.pos2 <- which(tempo2 %in% tempo1) -same.col.match1 <- match(tempo1, tempo2) -same.col.match2 <- match(tempo2, tempo1) -# }else if(as.double(ncol(data1)) * ncol(data2) <= 1e6){ # as.double(ncol(data1)) to prevent integer overflow because R is 32 bits for integers -# same.col.pos1 <- logical(length = ncol(data1)) # FALSE by default -# same.col.pos1[] <- FALSE # security -# same.col.pos2 <- logical(length = ncol(data2)) # FALSE by default -# same.col.pos2[] <- FALSE # security -# same.col.match1 <- rep(NA, ncol(data1)) -# same.col.match2 <- rep(NA, ncol(data2)) -# for(i3 in 1:ncol(data1)){ -# for(i4 in 1:ncol(data2)){ -# if(identical(data1[ , i3], data2[ , i4])){ -# same.col.pos1[i3] <- TRUE -# same.col.pos2[i4] <- TRUE -# same.col.match1[i3] <- i4 -# same.col.match2[i4] <- i3 -# } -# } -# } -# same.col.pos1 <- which(same.col.pos1) -# same.col.pos2 <- which(same.col.pos2) -}else{ -same.col.pos1 <- "TOO BIG FOR EVALUATION" -same.col.pos2 <- "TOO BIG FOR EVALUATION" -} -names(same.col.pos1) <- NULL -names(same.col.pos2) <- NULL -if(all(is.na(same.col.pos1))){ -same.col.pos1 <- NULL -}else{ -same.col.pos1 <- same.col.pos1[ ! is.na(same.col.pos1)] -any.id.col <- TRUE -} -if(all(is.na(same.col.pos2))){ -same.col.pos2 <- NULL -}else{ -same.col.pos2 <- same.col.pos2[ ! is.na(same.col.pos2)] -any.id.col <- TRUE -} -if(is.null(same.col.pos1) & is.null(same.col.pos2)){ -any.id.col <- FALSE -}else if(length(same.col.pos1) == 0L & length(same.col.pos2) == 0L){ -any.id.col <- FALSE -}else if(all(same.col.pos1 == "TOO BIG FOR EVALUATION") & all(same.col.pos2 == "TOO BIG FOR EVALUATION")){ -any.id.col <- NULL -} -}else{ -any.id.col <- FALSE -# same.col.pos1 and 2 remain NULL -} -if(same.dim == TRUE){ -names(data1) <- NULL -row.names(data1) <- NULL -names(data2) <- NULL -row.names(data2) <- NULL -if(identical(data1, data2)){ -identical.content <- TRUE -}else{ -identical.content <- FALSE -} -}else{ -identical.content <- FALSE -} -} -output <- list(same.class = same.class, class = class, same.dim = same.dim, dim = dim, same.row.nb = same.row.nb, row.nb = row.nb, same.col.nb = same.col.nb , col.nb = col.nb, same.row.name = same.row.name, row.name = row.name, any.id.row.name = any.id.row.name, same.row.names.pos1 = same.row.names.pos1, same.row.names.pos2 = same.row.names.pos2, same.row.names.match1 = same.row.names.match1, same.row.names.match2 = same.row.names.match2, common.row.names = common.row.names, same.col.name = same.col.name, col.name = col.name,any.id.col.name = any.id.col.name, same.col.names.pos1 = same.col.names.pos1, same.col.names.pos2 = same.col.names.pos2, same.col.names.match1 = same.col.names.match1, same.col.names.match2 = same.col.names.match2, common.col.names = common.col.names, any.id.row = any.id.row, same.row.pos1 = same.row.pos1, same.row.pos2 = same.row.pos2, same.row.match1 = same.row.match1, same.row.match2 = same.row.match2, any.id.col = any.id.col, same.col.pos1 = same.col.pos1, same.col.pos2 = same.col.pos2, same.col.match1 = same.col.match1, same.col.match2 = same.col.match2, identical.object = identical.object, identical.content = identical.content) -return(output) + +fun_name_change <- function(data1, data2, added.string = "_modif"){ + # AIM + # this function allow to check if a vector of character strings, like column names of a data frame, has elements present in another vector (vector of reserved words or column names of another data frame before merging) + # ARGUMENTS + # data1: vector of character strings to check and modify + # data2: reference vector of character strings + # added.string: string added at the end of the modified string in data1 if present in data2 + # RETURN + # a list containing + # $data: the modified data1 (in the same order as in the initial data1) + # $ini: the initial elements before modification. NULL if no modification + # $post: the modified elements in the same order as in ini. NULL if no modification + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # obs1 <- c("A", "B", "C", "D") ; obs2 <- c("A", "C") ; fun_name_change(obs1, obs2) + # obs1 <- c("A", "B", "C", "C_modif1", "D") ; obs2 <- c("A", "A_modif1", "C") ; fun_name_change(obs1, obs2) # the function checks that the new names are neither in obs1 nor in obs2 (increment the number after the added string) + # DEBUGGING + # data1 = c("A", "B", "C", "D") ; data2 <- c("A", "C") ; added.string = "_modif" # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data1, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = data2, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = added.string, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + ini <- NULL + post <- NULL + if(any(data1 %in% data2)){ + tempo.names <- data1[data1 %in% data2] + ini <- NULL + post <- NULL + for(i2 in 1:length(tempo.names)){ + count <- 0 + tempo <- tempo.names[i2] + while(any(tempo %in% data2) | any(tempo %in% data1)){ + count <- count + 1 + tempo <- paste0(tempo.names[i2], "_modif", count) + } + data1[data1 %in% tempo.names[i2]] <- paste0(tempo.names[i2], "_modif", count) + if(count != 0){ + ini <- c(ini, tempo.names[i2]) + post <- c(post, paste0(tempo.names[i2], "_modif", count)) + } + } + data <- data1 + }else{ + data <- data1 + } + output <- list(data = data, ini = ini, post = post) + return(output) } -######## fun_comp_list() #### comparison of two lists +######## fun_df_remod() #### remodeling a data frame to have column name as a qualitative values and vice-versa -fun_comp_list <- function(data1, data2){ -# AIM -# compare two lists. Check and report in a list if the 2 datasets have: -# same length -# common names -# common compartments -# ARGUMENTS -# data1: list -# data2: list -# RETURN -# a list containing: -# $same.length: logical. Are number of elements identical? -# $length: number of elements in the 2 datasets (NULL otherwise) -# $same.names: logical. Are element names identical ? -# $name: name of elements of the 2 datasets if identical (NULL otherwise) -# $any.id.name: logical. Is there any element names identical ? -# $same.names.pos1: positions, in data1, of the element names identical in data2 -# $same.names.pos2: positions, in data2, of the compartment names identical in data1 -# $any.id.compartment: logical. is there any identical compartments ? -# $same.compartment.pos1: positions, in data1, of the compartments identical in data2 -# $same.compartment.pos2: positions, in data2, of the compartments identical in data1 -# $identical.object: logical. Are objects identical (kind of object, compartment names and content)? -# $identical.content: logical. Are content objects identical (identical compartments excluding compartment names)? -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# none -# EXAMPLES -# obs1 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; obs2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; fun_comp_list(obs1, obs2) -# obs1 = list(1:5, LETTERS[1:2]) ; obs2 = list(a = 1:5, b = LETTERS[1:2]) ; fun_comp_list(obs1, obs2) -# obs1 = list(b = 1:5, c = LETTERS[1:2]) ; obs2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; fun_comp_list(obs1, obs2) -# obs1 = list(b = 1:5, c = LETTERS[1:2]) ; obs2 = list(LETTERS[5:9], matrix(1:6), 1:5) ; fun_comp_list(obs1, obs2) -# DEBUGGING -# data1 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) ; data2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) # for function debugging -# data1 = list(a = 1:5, b = LETTERS[1:2]) ; data2 = list(a = 1:5, b = LETTERS[1:2], d = matrix(1:6)) # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# argument checking -if( ! any(class(data1) %in% "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! any(class(data2) %in% "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) # activate this line and use the function to check arguments status -# end argument checking -# main code -same.length <- NULL -length <- NULL -same.names <- NULL -name <- NULL -any.id.name <- NULL -same.names.pos1 <- NULL -same.names.pos2 <- NULL -any.id.compartment <- NULL -same.compartment.pos1 <- NULL -same.compartment.pos2 <- NULL -identical.object <- NULL -identical.content <- NULL -if(identical(data1, data2)){ -same.length <- TRUE -length <- length(data1) -if( ! is.null(names(data1))){ -same.names <- TRUE -name <- names(data1) -any.id.name <- TRUE -same.names.pos1 <- 1:length(data1) -same.names.pos2 <- 1:length(data2) -} -any.id.compartment <- TRUE -same.compartment.pos1 <- 1:length(data1) -same.compartment.pos2 <- 1:length(data2) -identical.object <- TRUE -identical.content <- TRUE -}else{ -identical.object <- FALSE -if( ! identical(length(data1), length(data2))){ -same.length<- FALSE -}else{ -same.length<- TRUE -length <- length(data1) -} -if( ! (is.null(names(data1)) & is.null(names(data2)))){ -if( ! identical(names(data1), names(data2))){ -same.names <- FALSE -}else{ -same.names <- TRUE -name <- names(data1) -} -any.id.name <- FALSE -if(any(names(data1) %in% names(data2))){ -any.id.name <- TRUE -same.names.pos1 <- which(names(data1) %in% names(data2)) +fun_df_remod <- function( + data, + quanti.col.name = "quanti", + quali.col.name = "quali" +){ + # AIM + # if the data frame is made of n numeric columns, a new data frame is created, with the 1st column gathering all the numeric values, and the 2nd column being the name of the columns of the initial data frame. If row names were present in the initial data frame, then a new ini_rowname column is added with the names of the rows + + + # If the data frame is made of one numeric column and one character or factor column, a new data frame is created, with the new columns corresponding to the split numeric values (according to the character column). NA are added a the end of each column to have the same number of rows. BEWARE: in such data frame, rows are not individuals. This means that in the example below, values 10 and 20 are associated on the same row but that means nothing in term of association + + + + # ARGUMENTS + # data: data frame to convert + # quanti.col.name: optional name for the quanti column of the new data frame + # quali.col.name: optional name for the quali column of the new data frame + # RETURN + # the modified data frame + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # obs <- data.frame(col1 = (1:4)*10, col2 = c("A", "B", "A", "A"), stringsAsFactors = TRUE) ; obs ; fun_df_remod(obs) + # obs <- data.frame(col1 = (1:4)*10, col2 = 5:8, stringsAsFactors = TRUE) ; obs ; fun_df_remod(obs, quanti.col.name = "quanti", quali.col.name = "quali") + # obs <- data.frame(col1 = (1:4)*10, col2 = 5:8, stringsAsFactors = TRUE) ; rownames(obs) <- paste0("row", 1:4) ; obs ; fun_df_remod(obs, quanti.col.name = "quanti", quali.col.name = "quali") + # DEBUGGING + # data = data.frame(a = 1:3, b = 4:6, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging + # data = data.frame(a = 1:3, b = 4:6, c = 11:13, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging + # data = data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging + # data = data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = TRUE) ; quanti.col.name = "TEST" ; quali.col.name = "quali" # for function debugging + # data = data.frame(b = letters[1:3], a = 1:3, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging + # data = data.frame(b = c("e", "e", "h"), a = 1:3, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking without fun_check() + if( ! any(class(data) %in% "data.frame")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data ARGUMENT MUST BE A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking without fun_check() + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = quanti.col.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = quali.col.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + tempo.factor <- unlist(lapply(data, class)) + for(i in 1:length(tempo.factor)){ # convert factor columns as character + if(all(tempo.factor[i] == "factor")){ + data[, i] <- as.character(data[, i]) + } + } + tempo.factor <- unlist(lapply(data, mode)) + if(length(data) == 2L){ + if( ! ((base::mode(data[, 1]) == "character" & base::mode(data[, 2]) == "numeric") | base::mode(data[, 2]) == "character" & base::mode(data[, 1]) == "numeric" | base::mode(data[, 2]) == "numeric" & base::mode(data[, 1]) == "numeric") ){ + tempo.cat <- paste0("ERROR IN ", function.name, ": IF data ARGUMENT IS A DATA FRAME MADE OF 2 COLUMNS, EITHER A COLUMN MUST BE NUMERIC AND THE OTHER CHARACTER, OR THE TWO COLUMNS MUST BE NUMERIC") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if((base::mode(data[, 1]) == "character" | base::mode(data[, 2]) == "character") & (quanti.col.name != "quanti" | quali.col.name != "quali")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": IMPROPER quanti.col.name OR quali.col.name RESETTINGS. THESE ARGUMENTS ARE RESERVED FOR DATA FRAMES MADE OF n NUMERIC COLUMNS ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + if( ! all(tempo.factor %in% "numeric")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": IF data ARGUMENT IS A DATA FRAME MADE OF ONE COLUMN, OR MORE THAN 2 COLUMNS, THESE COLUMNS MUST BE NUMERIC") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(( ! any(tempo.factor %in% "character")) & is.null(names(data))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": NUMERIC DATA FRAME in the data ARGUMENT MUST HAVE COLUMN NAMES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(all(tempo.factor %in% "numeric")){ # transfo 1 + quanti <- NULL + for(i in 1:length(data)){ + quanti <-c(quanti, data[, i]) + } + quali <- rep(names(data), each = nrow(data)) + output.data <- data.frame(quanti, quali, stringsAsFactors = TRUE, check.names = FALSE) + names(output.data) <- c(quanti.col.name, quali.col.name) + # add the ini_rowname column + ini.rownames <- rownames(data) + tempo.data <- data + rownames(tempo.data) <- NULL + null.rownames <- (tempo.data) + if( ! identical(ini.rownames, null.rownames)){ + ini_rowname <- rep(ini.rownames, times = ncol(data)) + output.data <- cbind(output.data, ini_rowname, stringsAsFactors = TRUE) + } + }else{ # transfo 2 + if(class(data[, 1]) == "character"){ + data <- cbind(data[2], data[1], stringsAsFactors = TRUE) + } + nc.max <- max(table(data[, 2])) # effectif maximum des classes + nb.na <- nc.max - table(data[,2]) # nombre de NA à ajouter pour réaliser la data frame + tempo<-split(data[, 1], data[, 2]) + for(i in 1:length(tempo)){tempo[[i]] <- append(tempo[[i]], rep(NA, nb.na[i]))} # des NA doivent être ajoutés lorsque les effectifs sont différents entre les classes. C'est uniquement pour que chaque colonne ait le même nombre de lignes + output.data<-data.frame(tempo, stringsAsFactors = TRUE, check.names = FALSE) + } + return(output.data) } -if(any(names(data2) %in% names(data1))){ -any.id.name <- TRUE -same.names.pos2 <- which(names(data2) %in% names(data1)) + + + + +######## fun_round() #### rounding number if decimal present + + +fun_round <- function(data, dec.nb = 2, after.lead.zero = TRUE){ + # AIM + # round a vector of values, if decimal, with the desired number of decimal digits after the decimal leading zeros + # WARNINGS + # Work well with numbers as character strings, but not always with numerical numbers because of the floating point + # Numeric values are really truncated from a part of their decimal digits, whatever options(digits) settings + # See ?.Machine or https://stackoverflow.com/questions/5173692/how-to-return-number-of-decimal-places-in-r, with the interexting formula: abs(x - round(x)) > .Machine$double.eps^0.5 + # ARGUMENTS + # data: a vector of numbers (numeric or character mode) + # dec.nb: number of required decimal digits + # after.lead.zero: logical. If FALSE, rounding is performed for all the decimal numbers, whatever the leading zeros (e.g., 0.123 -> 0.12 and 0.00128 -> 0.00). If TRUE, dec.nb are taken after the leading zeros (e.g., 0.123 -> 0.12 and 0.00128 -> 0.0013) + # RETURN + # the modified vector + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, 10, 100.001, 333.0001254, 12312.1235), dec.nb = 2, after.lead.zero = FALSE), "\n\n") ; options(digits = ini.options) + # ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, 10, 100.001, 333.0001254, 12312.1235), dec.nb = 2, after.lead.zero = TRUE), "\n\n") ; options(digits = ini.options) + # ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, "10", "100.001", "333.0001254", "12312.1235"), dec.nb = 2, after.lead.zero = FALSE), "\n\n") ; options(digits = ini.options) + # ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, "10", "100.001", "333.0001254", "12312.1235"), dec.nb = 2, after.lead.zero = TRUE), "\n\n") ; options(digits = ini.options) + # DEBUGGING + # data = data = c(10, 100.001, 333.0001254, 12312.1235) ; dec.nb = 2 ; after.lead.zero = FALSE # # for function debugging + # data = data = c("10", "100.001", "333.0001254", "12312.1235") ; dec.nb = 2 ; after.lead.zero = TRUE # # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking without fun_check() + if( ! (all(typeof(data) == "character") | all(typeof(data) == "double") | all(typeof(data) == "integer"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A VECTOR OF NUMBERS (IN NUMERIC OR CHARACTER MODE)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking without fun_check() + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data, class = "vector", na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dec.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = after.lead.zero, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + tempo <- grepl(x = data, pattern = "\\.") # detection of decimal numbers + ini.mode <- base::mode(data) + data <- as.character(data) # to really truncate decimal digits + for(i in 1:length(data)){ # scan all the numbers of the vector + if(tempo[i] == TRUE){ # means decimal number + if(after.lead.zero == TRUE){ + zero.pos <- unlist(gregexpr(text=data[i], pattern = 0)) # recover all the position of the zeros in the number. -1 if no zeros (do not record the leading and trailing zeros) + }else{ + zero.pos <- -1 # -1 as if no zero + } + dot.pos <- unlist(gregexpr(text=data[i], pattern = "\\.")) # recover all the position of the zeros in the number + digit.pos <- unlist(gregexpr(text=data[i], pattern = "[[:digit:]]")) # recover all the position of the digits in the number + dec.pos <- digit.pos[digit.pos > dot.pos] + count <- 0 + while((dot.pos + count + 1) %in% zero.pos & (dot.pos + count + 1) <= max(dec.pos) & (count + dec.nb) < length(dec.pos)){ # count the number of leading zeros in the decimal part + count <- count + 1 + } + data[i] <- formatC(as.numeric(data[i]), digits = (count + dec.nb), format = "f") + } + } + if(ini.mode != "character"){ + data <- as.numeric(data) + } + return(data) } + + +######## fun_mat_rotate() #### 90° clockwise matrix rotation + + +fun_mat_rotate <- function(data){ + # AIM + # 90° clockwise matrix rotation + # applied twice, the function provide the mirror matrix, according to vertical and horizontal symmetry + # ARGUMENTS + # data: matrix (matrix class) + # RETURN + # the modified matrix + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # obs <- matrix(1:10, ncol = 1) ; obs ; fun_mat_rotate(obs) + # obs <- matrix(LETTERS[1:10], ncol = 5) ; obs ; fun_mat_rotate(obs) + # DEBUGGING + # data = matrix(1:10, ncol = 1) + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data, class = "matrix", fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + for (i in 1:ncol(data)){data[,i] <- rev(data[,i])} + data <- t(data) + return(data) } -names(data1) <- NULL -names(data2) <- NULL -any.id.compartment <- FALSE -if(any(data1 %in% data2)){ -any.id.compartment <- TRUE -same.compartment.pos1 <- which(data1 %in% data2) + + +######## fun_mat_num2color() #### convert a numeric matrix into hexadecimal color matrix + + +fun_mat_num2color <- function( + mat1, + mat.hsv.h = TRUE, + notch = 1, + s = 1, + v = 1, + forced.color = NULL +){ + # AIM + # convert a matrix made of numbers into a hexadecimal matrix for rgb colorization + # ARGUMENTS: + # mat1: matrix 1 of non negative numerical values that has to be colored (matrix class). NA allowed + # mat.hsv.h: logical. Is mat1 the h of hsv colors ? (if TRUE, mat1 must be between zero and 1). If FALSE, mat1 must be made of positive integer values without 0 + # notch: single value between 0 and 1 to shift the successive colors on the hsv circle by + notch + # s: s argument of hsv(). Must be between 0 and 1 + # v: v argument of hsv(). Must be between 0 and 1 + # forced.color: Must be NULL or hexadecimal color code or name given by colors(). The first minimal values of mat1 will be these colors. All the color of mat1 can be forced using this argument + # RETURN + # a list containing: + # $mat1.name: name of mat1 + # $colored.mat: colors of mat1 in hexa + # $problem: logical. Is any colors of forced.color overlap the colors designed by the function. NULL if forced.color = NULL + # $text.problem: text when overlapping colors. NULL if forced.color = NULL or problem == FALSE + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # mat1 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; dimnames(mat1) <- list(LETTERS[1:4], letters[1:2]) ; fun_mat_num2color(mat1, mat.hsv.h = FALSE, notch = 1, s = 1, v = 1, forced.color = NULL) + # DEBUGGING + # mat1 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; dimnames(mat1) <- list(LETTERS[1:4], letters[1:2]); mat.hsv.h = FALSE ; notch = 1 ; s = 1 ; v = 1 ; forced.color = c(hsv(1,1,1), hsv(0,0,0)) # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = mat1, mode = "numeric", class = "matrix", na.contain = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = mat.hsv.h, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = notch, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = s, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = v, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # argument checking without fun_check() + if(mat.hsv.h == TRUE & fun_check(data = mat1, mode = "numeric", prop = TRUE)$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 ARGUMENT MUST BE A MATRIX OF PROPORTIONS SINCE THE mat.hsv.h ARGUMENT IS SET TO TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(forced.color)){ + tempo <- fun_check(data = forced.color, class = "character") + if(any(tempo$problem == TRUE)){ + paste0("\n\n================\n\n", paste(tempo$text[tempo$problem], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! all(forced.color %in% colors() | grepl(pattern = "^#", forced.color))){ # check that all strings of forced.color start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": forced.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end argument checking without fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + problem <- NULL + text.problem <- NULL + mat1.name <- deparse(substitute(mat1)) + # change the scale of the plotted matrix + if(mat.hsv.h == TRUE){ + if(any(min(mat1, na.rm = TRUE) < 0 | max(mat1, na.rm = TRUE) > 1, na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 MUST BE MADE OF VALUES BETWEEN 0 AND 1 BECAUSE mat.hsv.h ARGUMENT SET TO TRUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + if(any(mat1 - floor(mat1) > 0, na.rm = TRUE) | any(mat1 == 0L, na.rm = TRUE)){ # no need of isTRUE(all.equal()) because we do not require approx here but strictly 0, thus == is ok + tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 MUST BE MADE OF INTEGER VALUES WITHOUT 0 BECAUSE mat.hsv.h ARGUMENT SET TO FALSE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + mat1 <- mat1 / max(mat1, na.rm = TRUE) + } + } + if(notch != 1){ + different.color <- unique(as.vector(mat1)) + different.color <- different.color[ ! is.na(different.color)] + tempo.different.color <- different.color + c(0, cumsum(rep(notch, length(different.color) - 1))) + tempo.different.color <- tempo.different.color - floor(tempo.different.color) + if(any(duplicated(tempo.different.color) == TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": DUPLICATED VALUES AFTER USING notch (", paste(tempo.different.color[duplicated(tempo.different.color)], collapse = " "), "). TRY ANOTHER notch VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(length(different.color) != length(tempo.different.color)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF different.color (", paste(different.color, collapse = " "), ") DIFFERENT FROM LENGTH OF tempo.different.color (", paste(tempo.different.color, collapse = " "), ")") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + for(i in 1:length(different.color)){ + mat1[mat1 == different.color[i]] <- tempo.different.color[i] # no need of isTRUE(all.equal()) because different.color comes from mat1 + } + } + } + if( ! is.null(forced.color)){ + hexa.values.to.change <- hsv(unique(sort(mat1))[1:length(forced.color)], s, v) + } + mat1[ ! is.na(mat1)] <- hsv(mat1[ ! is.na(mat1)], s, v) + if( ! is.null(forced.color)){ + if(any(forced.color %in% mat1, na.rm = TRUE)){ + problem <- TRUE + text.problem <- paste0("THE FOLLOWING COLORS WHERE INTRODUCED USING forced.color BUT WHERE ALREADY PRESENT IN THE COLORED MATRIX :", paste(forced.color[forced.color %in% mat1], collapse = " ")) + }else{ + problem <- FALSE + } + for(i in 1:length(hexa.values.to.change)){ + if( ! any(mat1 == hexa.values.to.change[i], na.rm = TRUE)){# no need of isTRUE(all.equal()) because character + tempo.cat <- paste0("ERROR IN ", function.name, ": THE ", hexa.values.to.change[i], " VALUE FROM hexa.values.to.change IS NOT REPRESENTED IN mat1 : ", paste(unique(as.vector(mat1)), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + mat1[which(mat1 == hexa.values.to.change[i])] <- forced.color[i] # no need of isTRUE(all.equal()) because character + } + } + } + output <- list(mat1.name = mat1.name, colored.mat = mat1, problem = problem, text.problem = text.problem) + return(output) } -if(any(data2 %in% data1)){ -any.id.compartment <- TRUE -same.compartment.pos2 <- which(data2 %in% data1) + + +######## fun_mat_op() #### assemble several matrices with operation + + +fun_mat_op <- function(mat.list, kind.of.operation = "+"){ + # AIM + # assemble several matrices of same dimensions by performing by case operation. For instance add the value of all the case 1 (row1 & column1) of the matrices and put it in the case 1 of a new matrix M, add the value of all the case 2 (row2 & column1) of the matrices and put it in the case 2 of a new matrix M, etc. + + # c: case + # i: row number + # j: column number + # k: matrix number + # z: number of matrices + # ARGUMENTS: + # mat.list: list of matrices + # kind.of.operation: either "+" (by case addition), "-" (by case subtraction) or "*" (by case multiplication) + # RETURN + # the assembled matrix, with row and/or column names only if all the matrices have identical row/column names + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_comp_2d() + # EXAMPLES + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "+") + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "*") + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], c(NA, NA))) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "-") + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(c("A1", "A2", "A3", "A4"), letters[1:2])) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat3 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2, mat3), kind.of.operation = "+") + # DEBUGGING + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; mat.list = list(mat1, mat2) ; kind.of.operation = "+" # for function debugging + # mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], c(NA, NA))) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat.list = list(mat1, mat2) ; kind.of.operation = "*" # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_comp_2d() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = mat.list, class = "list", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = kind.of.operation, options = c("+", "-", "*"), length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # argument checking without fun_check() + if(length(mat.list) < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat.list ARGUMENT MUST BE A LIST CONTAINING AT LEAST 2 MATRICES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + for(i1 in 1:length(mat.list)){ + tempo <- fun_check(data = mat.list[[i1]], class = "matrix", mode = "numeric", na.contain = TRUE) + if(tempo$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ELEMENT ", i1, " OF mat.list ARGUMENT MUST BE A NUMERIC MATRIX") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + ident.row.names <- TRUE + ident.col.names <- TRUE + for(i1 in 2:length(mat.list)){ + tempo <- fun_comp_2d(data1 = mat.list[[1]], data2 = mat.list[[i1]]) + if(tempo$same.dim == FALSE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX ", i1, " OF mat.list ARGUMENT MUST HAVE THE SAME DIMENSION (", paste(dim(mat.list[[i1]]), collapse = " "), ") THAN THE MATRIX 1 IN mat.list (", paste(dim(mat.list[[1]]), collapse = " "), ")") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(tempo$same.row.name)){ + if(tempo$same.row.name != TRUE){ # != TRUE to deal with NA + ident.row.names <- FALSE + } + } + if( ! is.null(tempo$same.col.name)){ + if(tempo$same.col.name != TRUE){ # != TRUE to deal with NA + ident.col.names <- FALSE + } + } + } + # end argument checking without fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + output <- mat.list[[1]] + for(i1 in 2:length(mat.list)){ + output <- get(kind.of.operation)(output, mat.list[[i1]]) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + } + dimnames(output) <- NULL + if(ident.row.names == TRUE){ + rownames(output) <- rownames(mat.list[[1]]) + } + if(ident.col.names == TRUE){ + colnames(output) <- colnames(mat.list[[1]]) + } + return(output) } -if(same.length == TRUE & ! all(is.null(same.compartment.pos1), is.null(same.compartment.pos2))){ -if(identical(same.compartment.pos1, same.compartment.pos2)){ -identical.content <- TRUE -}else{ -identical.content <- FALSE + + +######## fun_mat_inv() #### return the inverse of a square matrix + + +fun_mat_inv <- function(mat){ + # AIM + # return the inverse of a square matrix when solve() cannot + # ARGUMENTS: + # mat: a square numeric matrix without NULL, NA, Inf or single case (dimension 1, 1) of 0 + # RETURN + # the inversed matrix + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # mat1 = matrix(c(1,1,1,2,1,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) # use solve() + # mat1 = matrix(c(0,0,0,0,0,0,0,0,0), ncol = 3) ; fun_mat_inv(mat = mat1) # use the trick + # mat1 = matrix(c(1,1,1,2,Inf,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) + # mat1 = matrix(c(1,1,1,2,NA,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) + # mat1 = matrix(c(1,2), ncol = 1) ; fun_mat_inv(mat = mat1) + # mat1 = matrix(0, ncol = 1) ; fun_mat_inv(mat = mat1) + # mat1 = matrix(2, ncol = 1) ; fun_mat_inv(mat = mat1) + # DEBUGGING + # mat = matrix(c(1,1,1,2,1,5,9,8,9), ncol = 3) # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = mat, class = "matrix", mode = "numeric", fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # argument checking without fun_check() + if(ncol(mat) != nrow(mat)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A SQUARE MATRIX") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(mat %in% c(Inf, -Inf, NA))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A MATRIX WITHOUT Inf, -Inf OR NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(all(mat == 0L) & ncol(mat) == 1L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT CANNOT BE A SQUARE MATRIX MADE OF A SINGLE CASE OF 0") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking without fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if(any(grepl(x = try(solve(mat), silent = TRUE)[], pattern = "[Ee]rror"))){ + tempo <- svd(mat) + val.critique <- which(tempo$d < 10^-8) + Diag.mod <- diag(1 / tempo$d) + for(i in val.critique){ + Diag.mod[i, i] <- 0 + } + return(tempo$v %*% Diag.mod %*% t(tempo$u)) + }else{ + return(solve(mat)) + } } -}else{ -identical.content <- FALSE + + +######## fun_mat_fill() #### fill the empty half part of a symmetric square matrix + + +fun_mat_fill <- function(mat, empty.cell.string = 0, warn.print = FALSE){ + # AIM + # detect the empty half part of a symmetric square matrix (either topleft, topright, bottomleft or bottomright) + # fill this empty half part using the other symmetric half part of the matrix + # WARNINGS + # a plot verification using fun_gg_heatmap() is recommanded + # ARGUMENTS: + # mat: a numeric or character square matrix with the half part (according to the grand diagonal) filled with NA (any kind of matrix), "0" (character matrix) or 0 (numeric matrix) exclusively (not a mix of 0 and NA in the empty part) + # empty.cell.string: a numeric, character or NA (no quotes) indicating what empty cells are filled with + # warn.print: logical. Print warnings at the end of the execution? No print if no warning messages + # RETURN + # a list containing: + # $mat: the filled matrix + # $warn: the warning messages. Use cat() for proper display. NULL if no warning + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # mat1 = matrix(c(1,NA,NA,NA, 0,2,NA,NA, NA,3,4,NA, 5,6,7,8), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = NA, warn.print = TRUE) # bottomleft example + # mat1 = matrix(c(1,1,1,2, 0,2,3,0, NA,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = NA, warn.print = TRUE) # error example + # mat1 = matrix(c(1,1,1,2, 0,2,3,0, NA,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # bottomright example + # mat1 = matrix(c(1,1,1,2, "a",2,3,NA, "a","a",0,0, "a","a","a",0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = "a", warn.print = TRUE) # topright example + # mat1 = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # topleft example + # mat1 = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # error example + # DEBUGGING + # mat = matrix(c(1,NA,NA,NA, 0,2,NA,NA, NA,3,4,NA, 5,6,7,8), ncol = 4) ; empty.cell.string = NA ; warn.print = TRUE # for function debugging + # mat = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; empty.cell.string = 0 ; warn.print = TRUE # for function debugging # topleft example + # mat = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; empty.cell.string = NA ; warn.print = TRUE # for function debugging # topleft example + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = mat, class = "matrix", na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = empty.cell.string, class = "vector", na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # argument checking without fun_check() + if(ncol(mat) != nrow(mat)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A SQUARE MATRIX") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (base::mode(mat) %in% c("numeric", "character"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A NUMERIC OR CHARACTER MATRIX") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(nrow(mat) == 1L & ncol(mat) == 1L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT CANNOT BE A SQUARE MATRIX MADE OF A SINGLE CASE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(ifelse(is.na(empty.cell.string), ! any(is.na(mat)), ! any(mat == empty.cell.string, na.rm = TRUE))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MATRIX MUST HAVE CELLS WITH THE EMPTY STRING SPECIFIED IN empty.cell.string ARGUMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking without fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + list.diag <- vector("list", length = nrow(mat) - 1) + for(i1 in 1:(nrow(mat) - 1)){ + list.diag[[i1]] <- numeric(length = nrow(mat) - i1) # list made of zero + } + sector <- c("topleft", "topright", "bottomright", "bottomleft") + diag.scan <-c( # same order as sector. Recover each diag from center to corner + "mat[as.matrix(as.data.frame(list(1:(nrow(mat) - i2), (ncol(mat) -i2):1), stringsAsFactors = TRUE))]", # topleft part + "mat[as.matrix(as.data.frame(list(1:(nrow(mat) - i2), (1:ncol(mat))[-(1:i2)]), stringsAsFactors = TRUE))]", # topright part + "mat[as.matrix(as.data.frame(list((1 + i2):nrow(mat), ncol(mat):(1 + i2)), stringsAsFactors = TRUE))]", # bottomright part + "mat[as.matrix(as.data.frame(list((1 + i2):nrow(mat), 1:(ncol(mat) -i2)), stringsAsFactors = TRUE))]" # bottomleft part + ) + # empty part detection + empty.sector <- NULL + full.sector <- NULL + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + for(i1 in 1:length(sector)){ + tempo.list.diag <- list.diag + for(i2 in 1:(nrow(mat) - 1)){ + tempo.list.diag[[i2]] <- eval(parse(text = diag.scan[i1])) + if(ifelse(is.na(empty.cell.string), ! all(is.na(tempo.list.diag[[i2]])), ! (all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = TRUE) & ! (is.na(all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE)))))){ # I had to add this ! (is.na(all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE))) because all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE) gives NA and not FALSE if one NA in tempo.list.diag[[i2]] -> not good for if() + full.sector <- c(full.sector, sector[i1]) + break + } + } + if(i2 == nrow(mat) - 1){ + if(all(unlist(lapply(tempo.list.diag, FUN = function(x){if(is.na(empty.cell.string)){is.na(x)}else{x == empty.cell.string}})), na.rm = TRUE)){ + empty.sector <- c(empty.sector, sector[i1]) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") EMPTY SECTOR DETECTED ON THE ", toupper(sector[i1]), " CORNER, FULL OF ", empty.cell.string) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE ", toupper(sector[i1]), " SECTOR, DETECTED AS EMPTY, IS NOT? DIFFERENT VALUES IN THIS SECTOR:\n", paste(names(table(unlist(tempo.list.diag), useNA = "ifany")), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + } + # end empty part detection + if(length(empty.sector) == 0L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") ACCORDING TO empty.cell.string ARGUMENT (", empty.cell.string, "), mat ARGUMENT MATRIX HAS ZERO EMPTY HALF PART") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + if(length(empty.sector) > 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ACCORDING TO empty.cell.string ARGUMENT (", empty.cell.string, "), mat ARGUMENT MATRIX HAS MORE THAN ONE EMPTY HALF PART (ACCORDING TO THE GRAND DIAGONAL): ", paste(empty.sector, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(any(full.sector %in% empty.sector, na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE FUNCTION HAS DETECTED EMPTY AND NON EMPTY HALF PART IN THE SAME SECTOR: ", paste(full.sector[full.sector %in% empty.sector], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(length(empty.sector) + length(full.sector)!= 4){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE FUNCTION HAS DETECTED MORE OR LESS SECTORS THAN 4:\nHALF SECTORS:", paste(empty.sector, collapse = " "), "\nFULL SECTORS:", paste(full.sector, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") ", toupper(empty.sector), " SECTOR HAS BEEN COMPLETED TO BECOME SYMMETRICAL") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # matrix filling + for(i2 in 1:(nrow(mat) - 1)){ + if(empty.sector == "topleft"){ + eval(parse(text = paste0(diag.scan[1], " <- ", diag.scan[3]))) + }else if(empty.sector == "topright"){ + eval(parse(text = paste0(diag.scan[2], " <- ", diag.scan[4]))) + }else if(empty.sector == "bottomright"){ + eval(parse(text = paste0(diag.scan[3], " <- ", diag.scan[1]))) + }else if(empty.sector == "bottomleft"){ + eval(parse(text = paste0(diag.scan[4], " <- ", diag.scan[2]))) + } + } + # end matrix filling + } + if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + return(list(mat = mat, warn = warn)) } + + +######## fun_permut() #### progressively breaks a vector order + + +fun_permut <- function( + data1, + data2 = NULL, + n = NULL, + seed = NULL, + print.count = 10, + text.print = "", + cor.method = "spearman", + cor.limit = 0.2, + warn.print = FALSE, + lib.path = NULL +){ + # AIM + # reorder the elements of the data1 vector by flipping 2 randomly selected consecutive positions either: + # 1) n times (when n is precised) or + # 2) until the correlation between data1 and data2 decreases down to the cor.limit (0.2 by default). See cor.limit below to deal with negative correlations + # Example of consecutive position flipping: ABCD -> BACD -> BADC, etc. + # designed for discrete values, but worls also for continuous values + # WARNINGS + # see # https://www.r-bloggers.com/strategies-to-speedup-r-code/ for code speedup + # the random switch of non consecutive positions (ABCD -> DBCA for instance) does not work very well as the correlation is quickly obtained but the initial vector structure is mainly kept (no much order). Ths code would be: pos <- ini.pos[1:2] ; pos <- sample.int(n = n , size = 2, replace = FALSE) ; tempo.pos[pos] <- tempo.pos[rev(pos)] + # ARGUMENTS + # data1: a vector of at least 2 elements. Must be numeric if data2 is specified + # data2: a numeric vector of same length as data1 + # n: number of times "flipping 2 randomly selected consecutive positions". Ignored if data2 is specified + # seed: integer number used by set.seed(). Write NULL if random result is required, an integer otherwise. BEWARE: if not NULL, fun_permut() will systematically return the same result when the other parameters keep the same settings + # print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired + # text.print: optional message to add to the working progress message every print.count loop + # cor.method: correlation method. Either "pearson", "kendall" or "spearman". Ignored if data2 is not specified + # cor.limit: a correlation limit (between 0 and 1). Ignored if data2 is not specified. Compute the correlation between data1 and data2, permute the data1 values, and stop the permutation process when the correlation between data1 and data2 decreases down below the cor limit value (0.2 by default). If cor(data1, data2) is negative, then -cor.limit is used and the process stops until the correlation between data1 and data2 increases up over cor.limit (-0.2 by default). BEWARE: write a positive cor.limit even if cor(data1, data2) is known to be negative. The function will automatically uses -cor.limit. If the initial correlation is already below cor.limit (positive correlation) or over -cor.limit (negative correlation), then the data1 value positions are completely randomized (correlation between data1 and data2 is expected to be 0) + # warn.print: logical. Print warnings at the end of the execution? No print if no warning messages + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # RETURN + # a list containing: + # $data: the modified vector + # $warn: potential warning messages (in case of negative correlation when data2 is specified). NULL if non warning message + # $cor: a spearman correlation between the initial positions (1:length(data1) and the final positions if data2 is not specified and the final correlation between data1 and data2 otherwise, according to cor.method + # $count: the number of loops used + # REQUIRED PACKAGES + # lubridate + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # fun_round() + # EXAMPLES + # example (1) showing that for loop, used in fun_permut(), is faster than while loop + # ini.time <- as.numeric(Sys.time()) ; count <- 0 ; for(i0 in 1:1e9){count <- count + 1} ; tempo.time <- as.numeric(Sys.time()) ; tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) ; tempo.lapse + # example (2) showing that for loop, used in fun_permut(), is faster than while loop + # ini.time <- as.numeric(Sys.time()) ; count <- 0 ; while(count < 1e9){count <- count + 1} ; tempo.time <- as.numeric(Sys.time()) ; tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) ; tempo.lapse + # fun_permut(data1 = LETTERS[1:5], data2 = NULL, n = 100, seed = 1, print.count = 10, text.print = "CPU NB 4") + # fun_permut(data1 = 101:110, data2 = 21:30, seed = 1, print.count = 1e4, text.print = "", cor.method = "spearman", cor.limit = 0.2) + # a way to use the cor.limit argument just considering data1 + # obs1 <- 101:110 ; fun_permut(data1 = obs1, data2 = obs1, seed = 1, print.count = 10, cor.method = "spearman", cor.limit = 0.2) + # fun_permut(data1 = 1:1e3, data2 = 1e3:1, seed = 1, print.count = 1e6, text.print = "", cor.method = "spearman", cor.limit = 0.7) + # fun_permut(data1 = 1:1e2, data2 = 1e2:1, seed = 1, print.count = 1e3, cor.limit = 0.5) + # fun_permut(data1 = c(0,0,0,0,0), n = 5, data2 = NULL, seed = 1, print.count = 1e3, cor.limit = 0.5) + # DEBUGGING + # data1 = LETTERS[1:5] ; data2 = NULL ; n = 1e6 ; seed = NULL ; print.count = 1e3 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL + # data1 = LETTERS[1:5] ; data2 = NULL ; n = 10 ; seed = 22 ; print.count = 10 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL + # data1 = 101:110 ; data2 = 21:30 ; n = 10 ; seed = 22 ; print.count = 10 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL + # data1 = 1:1e3 ; data2 = 1e3:1 ; n = 20 ; seed = 22 ; print.count = 1e6 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.5 ; warn.print = TRUE ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_round", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data1, class = "vector", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & length(data1) < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A VECTOR OF MINIMUM LENGTH 2. HERE IT IS: ", length(data1)) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(data2)){ + tempo <- fun_check(data = data1, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) + if(tempo$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 MUST BE A NUMERIC VECTOR IF data2 ARGUMENT IS SPECIFIED") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = data2, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) + if(length(data1) != length(data2)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 AND data2 MUST BE VECTOR OF SAME LENGTH. HERE IT IS ", length(data1)," AND ", length(data2)) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else if(is.null(n)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT CANNOT BE NULL IF data2 ARGUMENT IS NULL") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(n)){ + tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(seed)){ + tempo <- fun_check(data = seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = text.print, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = cor.method, options = c("pearson", "kendall", "spearman"), length =1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = cor.limit, class = "vector", mode = "numeric", prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # package checking + fun_pack(req.package = "lubridate", lib.path = lib.path) + # end package checking + # main code + # code that protects set.seed() in the global environment + # see also Protocol 100-rev0 Parallelization in R.docx + if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment + tempo.random.seed <- .Random.seed + on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) + }else{ + on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness + } + set.seed(seed) + # end code that protects set.seed() in the global environment + ini.date <- Sys.time() # time of process begin, converted into seconds + ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds + ini.pos <- 1:length(data1) # positions of data1 before permutation loops + tempo.pos <- ini.pos # positions of data1 that will be modified during loops + # pos.selec.seq <- ini.pos[-length(data1)] # selection of 1 position in initial position, without the last because always up permutation (pos -> pos+1 & pos+1 -> pos) + pos.selec.seq.max <- length(ini.pos) - 1 # max position (used by sample.int() function). See below for - 1 + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + count <- 0 + round <- 0 + BREAK <- FALSE + tempo.cor <- 0 + if(is.null(data2)){ + if(length(table(data1)) == 1L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data1 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data1))) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # + }else{ + if(print.count > n){ + print.count <- n + } + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP OF ", n, " LOOPS INITIATED | LOOP COUNT: ", format(count, big.mark=","))) + print.count.loop <- logical(length = print.count) + print.count.loop[length(print.count.loop)] <- TRUE # not this to avoid long vector, but not forget to reset during printing: print.count.loop[(1:trunc(n / print.count) * print.count)] <- TRUE # counter to speedup + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.date.loop <- Sys.time() + tempo.time.loop <- as.numeric(tempo.date.loop) + for(i3 in 1:n){ + count.loop <- count.loop + 1 + pos2 <- pos[count.loop] # selection of 1 position + tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + if(print.count.loop[count.loop]){ + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) + final.loop <- (tempo.time - tempo.time.loop) / i3 * n # expected duration in seconds + final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP ", i3, " / ", n, " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) + } + } + count <- count + n # out of the loop to speedup + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP ENDED | LOOP COUNT: ", format(count, big.mark=","))) + cat("\n\n") + } + }else{ + if(length(table(data1)) == 1L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data1 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data1))) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # + tempo.cor <- 1 + }else if(length(table(data2)) == 1L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data2 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data2))) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # + tempo.cor <- 1 + }else{ + cor.ini <- cor(x = data1, y = data2, use = "pairwise.complete.obs", method = cor.method) + tempo.cor <- cor.ini # correlation that will be modified during loops + neg.cor <- FALSE + if(tempo.cor < 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") INITIAL ", toupper(cor.method), " CORRELATION BETWEEN data1 AND data2 HAS BEEN DETECTED AS NEGATIVE: ", tempo.cor, ". THE LOOP STEPS WILL BE PERFORMED USING POSITIVE CORRELATIONS BUT THE FINAL CORRELATION WILL BE NEGATIVE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # + neg.cor <- TRUE + tempo.cor <- abs(tempo.cor) + cor.ini <- abs(cor.ini) + } + if(tempo.cor < cor.limit){ # randomize directly all the position to be close to correlation zero + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") INITIAL ABSOLUTE VALUE OF THE ", toupper(cor.method), " CORRELATION ", fun_round(tempo.cor), " BETWEEN data1 AND data2 HAS BEEN DETECTED AS BELOW THE CORRELATION LIMIT PARAMETER ", cor.limit, "\nTHE data1 SEQUENCE HAS BEEN COMPLETELY RANDOMIZED TO CORRESPOND TO CORRELATION ZERO") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # + for(i4 in 1:5){ # done 5 times to be sure of the complete randomness + tempo.pos <- sample(x = tempo.pos, size = length(tempo.pos), replace = FALSE) + } + count <- count + 5 # out of the loop to speedup + }else{ + # smallest correlation decrease + count <- count + 1 # 1 and not 0 because already 1 performed just below + pos <- sample.int(n = pos.selec.seq.max , size = 1, replace = TRUE) # selection of 1 position # pos.selec.seq.max because selection of 1 position in initial position, without the last because always up permutation (pos -> pos+1 & pos+1 -> pos) + tempo.pos[c(pos + 1, pos)] <- tempo.pos[c(pos, pos + 1)] + tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) + smallest.cor.dec <- cor.ini - tempo.cor + # end smallest correlation decrease + # going out of tempo.cor == cor.ini + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "CORRELATION DECREASE AFTER A SINGLE PERMUTATION: ", fun_round(smallest.cor.dec, 4))) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP -> GOING OUT FROM EQUALITY | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) + print.count.loop <- logical(length = print.count) + print.count.loop[length(print.count.loop)] <- TRUE # counter to speedup + count.loop <- 0 # + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.date.loop <- Sys.time() + tempo.time.loop <- as.numeric(tempo.date.loop) + while(tempo.cor == cor.ini){ # to be out of equality between tempo.cor and cor.ini at the beginning (only valid for very long vector) + count <- count + 1 + count.loop <- count.loop + 1 + pos2 <- pos[count.loop] + tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) + if(print.count.loop[count.loop]){ + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP", format(count.loop, big.mark=","), " / ? | COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TIME SPENT: ", tempo.lapse)) + } + } + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP END | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TOTAL SPENT TIME: ", tempo.lapse)) + if(tempo.cor < cor.limit){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE FIRST FOR & WHILE LOOP STEPS HAVE BEEN TOO FAR AND SUBSEQUENT LOOP STEPS WILL NOT RUN") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end going out of tempo.cor == cor.ini + # estimation of the average correlation decrease per loop on x loops and for loop execution + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE/FOR LOOPS INITIATION | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) + count.est <- 1e5 + first.round <- TRUE + GOBACK <- FALSE + while(tempo.cor > cor.limit){ + round <- round + 1 + # estimation step + if(first.round == TRUE){ + first.round <- FALSE + cor.dec.per.loop <- numeric(length = 5) + loop.nb.est <- Inf + cor.est.ini <- tempo.cor + cor.est <- numeric(length = 5) + for(i6 in 1:5){ # connected to cor.dec.per.loop + tempo.pos.est <- tempo.pos + pos <- sample.int(n = pos.selec.seq.max , size = count.est, replace = TRUE) # selection of n position + for(i7 in 1:count.est){ + pos2 <- pos[i7] # selection of 1 position + tempo.pos.est[c(pos2 + 1, pos2)] <- tempo.pos.est[c(pos2, pos2 + 1)] + } + tempo.cor.est <- abs(cor(x = data1[tempo.pos.est], y = data2, use = "pairwise.complete.obs", method = cor.method)) + cor.est[i6] <- tempo.cor.est + tempo.cor.dec.per.loop <- (cor.est.ini - tempo.cor.est) / count.est # correlation decrease per loop + if(is.na(tempo.cor.dec.per.loop) | ! is.finite(tempo.cor.dec.per.loop)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2\ncor.est.ini: ", cor.est.ini, "\ntempo.cor.est: ", tempo.cor.est) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + cor.dec.per.loop[i6] <- tempo.cor.dec.per.loop + } + cor.est <- cor.est[which.max(cor.dec.per.loop)] # max to avoid to go to far with for loop (tempo.cor below tempo.limit) + cor.dec.per.loop <- max(cor.dec.per.loop, na.rm = TRUE) # max to avoid to go to far with for loop (tempo.cor below tempo.limit) + loop.nb.est <- round((tempo.cor - cor.limit) / cor.dec.per.loop) + }else{ + if(GOBACK == TRUE){ + loop.nb.est <- round(loop.nb.est / 2) + }else{ + cor.dec.per.loop <- (cor.ini - tempo.cor) / count + loop.nb.est <- round((tempo.cor - cor.limit) / cor.dec.per.loop) + } + } + # end estimation step + # loop step + if(is.na(loop.nb.est) | ! is.finite(loop.nb.est)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1\nloop.nb.est: ", loop.nb.est, "\ncor.ini: ", cor.ini, "\ntempo.cor: ", tempo.cor, "\ncor.limit: ", cor.limit, "\ncor.dec.per.loop: ", cor.dec.per.loop) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(loop.nb.est > 1e4){ # below -> leave the while loop + tempo.pos.secu <- tempo.pos + count.secu <- count + tempo.cor.secu <- tempo.cor + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "INITIAL SETTINGS BEFORE ROUND: ", round, " | LOOP COUNT: ", format(count, big.mark=","), " | GO BACK: ", GOBACK, " | LOOP NUMBER ESTIMATION: ", format(loop.nb.est, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) + print.count.loop <- logical(length = print.count) + print.count.loop[length(print.count.loop)] <- TRUE # not this to avoid long vector, but not forget to reset during printing: print.count.loop[(1:trunc(n / print.count) * print.count)] <- TRUE # counter to speedup + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.date.loop <- Sys.time() + tempo.time.loop <- as.numeric(tempo.date.loop) + for(i6 in 1:loop.nb.est){ + count.loop <- count.loop + 1 + pos2 <- pos[count.loop] # selection of 1 position + tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + if(print.count.loop[count.loop]){ + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) + final.loop <- (tempo.time - tempo.time.loop) / i6 * loop.nb.est # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining + final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP | ROUND ", round, " | LOOP: ", format(i6, big.mark=","), " / ", format(loop.nb.est, big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) + } + } + count <- count + loop.nb.est # out of the loop to speedup + tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) + if(tempo.cor > tempo.cor.secu | ((tempo.cor - cor.limit) < 0 & abs(tempo.cor - cor.limit) > smallest.cor.dec * round(log10(max(ini.pos, na.rm = TRUE))))){ + GOBACK <- TRUE + tempo.pos <- tempo.pos.secu + count <- count.secu + tempo.cor <- tempo.cor.secu + }else{ + GOBACK <- FALSE + } + }else{ + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FINAL WHILE LOOP | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) + print.count.loop <- logical(length = print.count) + print.count.loop[length(print.count.loop)] <- TRUE # counter to speedup + count.loop <- 0 # + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.cor.loop <- tempo.cor + tempo.date.loop <- Sys.time() + tempo.time.loop <- as.numeric(tempo.date.loop) + while(tempo.cor > cor.limit){ + count <- count + 1 + count.loop <- count.loop + 1 + pos2 <- pos[count.loop] + tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] + tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) + if(print.count.loop[count.loop]){ + count.loop <- 0 + pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) + final.loop <- (tempo.time - tempo.time.loop) / (tempo.cor.loop - tempo.cor) * (tempo.cor - cor.limit) # expected duration in seconds # tempo.cor.loop - tempo.cor always positive and tempo.cor decreases progressively starting from tempo.cor.loop + final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE LOOP | LOOP NB: ", format(count.loop, big.mark=","), " | COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) + } + } + } + } + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) + cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE/FOR LOOPS END | LOOP COUNT: ", format(count, big.mark=","), " | NB OF ROUNDS: ", round, " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TOTAL SPENT TIME: ", tempo.lapse)) + } + tempo.cor <- ifelse(neg.cor == TRUE, -tempo.cor, tempo.cor) + } + } + cat("\n\n") + if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE), add = TRUE) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + output <- list(data = data1[tempo.pos], warn = warn, cor = if(is.null(data2)){cor(ini.pos, tempo.pos, method = "spearman")}else{tempo.cor}, count = count) + return(output) } -output <- list(same.length = same.length, length = length, same.names = same.names, name = name, any.id.name = any.id.name, same.names.pos1 = same.names.pos1, same.names.pos2 = same.names.pos2, any.id.compartment = any.id.compartment, same.compartment.pos1 = same.compartment.pos1, same.compartment.pos2 = same.compartment.pos2, identical.object = identical.object, identical.content = identical.content) -return(output) + + +######## fun_slide() #### return a computation made on a vector using a sliding window + + +fun_slide <- function( + data, + window.size, + step, + from = NULL, + to = NULL, + fun, + args = NULL, + boundary = "left", + parall = FALSE, + thread.nb = NULL, + print.count = 100, + res.path = NULL, + lib.path = NULL, + verbose = TRUE, + cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" +){ + # AIM + # return a computation made on a vector using a sliding window + # WARNINGS + # The function uses two strategies, depending on the amout of memory required which depends on the data, window.size and step arguments. The first one uses lapply(), is generally fast but requires lots of memory. The second one uses a parallelized loop. The choice between the two strategies is automatic if parall argument is FALSE, and is forced toward parallelization if parall argument is TRUE + # The parall argument forces the parallelization, which is convenient when the data argument is big, because the lapply() function is sometimes slower than the parallelization + # Always use the env argument when fun_slide() is used inside functions + # ARGUMENTS + # data: vector, matrix, table or array of numeric values (mode must be numeric). Inf not allowed. NA will be removed before computation + # window.size: single numeric value indicating the width of the window sliding across data (in the same unit as data value) + # step: single numeric value indicating the step between each window (in the same unit as data value). Cannot be larger than window.size + # from: value of the left boundary of the first sliding window. If NULL, min(data) is used. The first window will strictly have from or min(data) as left boundary + # to: value of the right boundary of the last sliding window. If NULL, max(data) is used. Warning: (1) the final last window will not necessary have to|max(data) as right boundary. In fact the last window will be the one that contains to|max(data) for the first time, i.e., min[from|min(data) + window.size + n * step >= to|max(data)]; (2) In fact, the >= in min[from|min(data) + window.size + n * step >= to|max(data)] depends on the boundary argument (>= for "right" and > for "left"); (3) to have the rule (1) but for the center of the last window, use to argument as to = to|max(data) + window.size / 2 + # fun: function or character string (without brackets) indicating the name of the function to apply in each window. Example: fun = "mean", or fun = mean + # args: character string of additional arguments of fun (separated by a comma between the quotes). Example args = "na.rm = TRUE" for fun = mean. Ignored if NULL + # boundary: either "left" or "right". Indicates if the sliding window includes values equal to left boundary and exclude values equal to right boundary ("left") or the opposite ("right") + # parall: logical. Force parallelization ? + # thread.nb: numeric value indicating the number of threads to use if ever parallelization is required. If NULL, all the available threads will be used. Ignored if parall is FALSE + # print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired + # res.path: character string indicating the absolute pathway where the parallelization log file will be created if parallelization is used. If NULL, will be created in the R current directory + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # verbose: logical. Display messages? + # cute.path: character string indicating the absolute path of the cute.R file. Will be remove when cute will be a package. Ignored if parall is FALSE + # RETURN + # a data frame containing + #$left : the left boundary of each window (in the unit of the data argument) + #$right : the right boundary of each window (in the unit of data argument) + #$center : the center of each window (in the unit of data argument) + #$value : the computed value by the fun argument in each window) + # REQUIRED PACKAGES + # lubridate + # parallel if parall argument is TRUE (included in the R installation packages but not automatically loaded) + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_get_message + # fun_pack() + # EXAMPLES + # fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "left") + # fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "right") # effect of boundary argument + # fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "left", parall = TRUE) # effect of parall argument + # DEBUGGING + # data = c(1:10, 100:110, 500) ; window.size = 5 ; step = 2 ; from = NULL ; to = NULL ; fun = length ; args = NULL ; boundary = "left" ; parall = FALSE ; thread.nb = NULL ; print.count = 100 ; res.path = NULL ; lib.path = NULL ; verbose = TRUE ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" + # data = lag.pos; window.size = window.size; step = step; fun = length; from = min(a$pos); to = max(a$pos) + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check", + "fun_get_message", + "fun_pack" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words + # end reserved words + # arg with no default values + mandat.args <- c( + "data", + "window.size", + "step", + "fun" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data, mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = window.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = step, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(from)){ + tempo <- fun_check(data = from, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + } + if( ! is.null(to)){ + tempo <- fun_check(data = to, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + } + tempo1 <- fun_check(data = fun, class = "vector", mode = "character", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = fun, class = "function", length = 1, fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": fun ARGUMENT MUST BE A FUNCTION OR A CHARACTER STRING OF THE NAME OF A FUNCTION") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(args)){ + tempo <- fun_check(data = args, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = boundary, options = c("left", "right"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = parall, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(parall == TRUE){ + if( ! is.null(thread.nb)){ + tempo <- fun_check(data = thread.nb, typeof = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & thread.nb < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": thread.nb PARAMETER MUST EQUAL OR GREATER THAN 1: ", thread.nb) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if( ! is.null(res.path)){ + tempo <- fun_check(data = res.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + } + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = verbose, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = cute.path, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # new environment + env.name <- paste0("env", as.numeric(Sys.time())) + if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function + tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + assign(env.name, new.env()) + } + # end new environment + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "data", + "window.size", + "step", + # "from", # because can be NULL + # "to", # because can be NULL + "fun", + # "args", # because can be NULL + "boundary", + "parall", + # "thread.nb", # because can be NULL + "print.count", + # "res.path", # because can be NULL + # "lib.path", # because can be NULL + "verbose", + "cute.path" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + # end warning initiation + # other checkings + if(length(data) == 0){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT CANNOT BE LENGTH 0") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(any( ! is.finite(data))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT CANNOT CONTAIN Inf VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if(step > window.size){ + tempo.cat <- paste0("ERROR IN ", function.name, ": step ARGUMENT MUST BE LOWER THAN window.size ARGUMENT\nstep: ", paste(step, collapse = " "), "\nwindow.size: ", paste(window.size, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + if( ! is.null(res.path)){ + if( ! all(dir.exists(res.path))){ # separation to avoid the problem of tempo$problem == FALSE and res.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE res.path ARGUMENT DOES NOT EXISTS:\n", paste(res.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + }else{ + res.path <- getwd() # working directory + } + if( ! is.null(lib.path)){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) + } + } + if(grepl(x = cute.path, pattern = "^http")){ + tempo.error1 <- any(grepl(x = fun_get_message(data = "source(cute.path)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)), pattern = "^[Ee]rror")) + tempo.error2 <- FALSE + }else{ + tempo.error1 <- FALSE + tempo.error2 <- ! file.exists(cute.path) + } + if(tempo.error1 | tempo.error2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(grepl(x = cute.path, pattern = "^http"), "URL", "FILE"), " PATH INDICATED IN THE cute.path PARAMETER DOES NOT EXISTS:\n", cute.path) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + # end other checkings + # reserved word checking + # end reserved word checking + # end second round of checking and data preparation + # package checking + fun_pack(req.package = c("lubridate"), lib.path = lib.path) + fun_pack(req.package = c("parallel"), lib.path = lib.path) + # end package checking + # main code + if(verbose == TRUE){ + cat("\nfun_slide JOB IGNITION\n") + } + ini.date <- Sys.time() + ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds + fun <- match.fun(fun) # make fun <- get(fun) is fun is a function name written as character string of length 1 + if(boundary == "left"){ + left <- ">=" + right <- "<" + right.last.wind <- ">" + }else if(boundary == "right"){ + left <- ">" + right <- "<=" + right.last.wind <- ">=" + }else{ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + data <- as.vector(data) + data <- sort(data, na.last = NA) # NA removed + wind <- data.frame(left = seq(from = if(is.null(from)){min(data, na.rm = TRUE)}else{from}, to = if(is.null(to)){max(data, na.rm = TRUE)}else{to}, by = step), stringsAsFactors = TRUE) + wind <- data.frame(wind, right = wind$left + window.size, stringsAsFactors = TRUE) + wind <- data.frame(wind, center = (wind$left + wind$right) / 2, stringsAsFactors = TRUE) + if(all(wind$right < if(is.null(to)){max(data, na.rm = TRUE)}else{to})){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # The 3 next lines is for the rule of to argument with center (see to argument description) + # if(any(wind$center > max(data, na.rm = TRUE))){ + # wind <- wind[ ! wind$center > max(data, na.rm = TRUE),] + # } + if(sum(get(right.last.wind)(wind$right, if(is.null(to)){max(data, na.rm = TRUE)}else{to}), na.rm = TRUE) > 1){ # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + tempo.log <- get(right.last.wind)(wind$right, if(is.null(to)){max(data, na.rm = TRUE)}else{to}) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + tempo.log[min(which(tempo.log), na.rm = TRUE)] <- FALSE # convert the first left boundary that goes above max(data, na.rm = TRUE) to FALSE to keep it (the next ones will be removed) + wind <- wind[ ! tempo.log,] + } + + # test if lapply can be used + if(parall == FALSE){ + assign("wind", wind, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # wind assigned in a new envir for test + assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test + tempo.message <- fun_get_message(data="lapply(X = wind$left, Y = data, FUN = function(X, Y){res <- get(left)(Y, X) ; return(res)})", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE), print.no = FALSE) # no env = sys.nframe(), inherit = FALSE in get(left) because look for function in the classical scope + # rm(env.name) # optional, because should disappear at the end of the function execution + }else{ + tempo.message <- "ERROR" # with this, force the parallelization by default + } + # end test if lapply can be used + if( ! any(grepl(x = tempo.message, pattern = "ERROR.*"))){ + left.log <- lapply(X = wind$left, Y = data, FUN = function(X, Y){ + res <- get(left)(Y, X) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + return(res) + }) + right.log <- lapply(X = wind$right, Y = data, FUN = function(X, Y){ + res <- get(right)(Y, X) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + return(res) + }) + log <- mapply(FUN = "&", left.log, right.log, SIMPLIFY = FALSE) + output <- eval(parse(text = paste0("sapply(lapply(log, FUN = function(X){(data[X])}), FUN = fun", if( ! is.null(args)){paste0(", ", args)}, ")"))) # take the values of the data vector according to log (list of logical, each compartment of length(data)) and apply fun with args of fun + if(length(output) != nrow(wind)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + output <- data.frame(wind, value = output, stringsAsFactors = TRUE) + } + }else{ + if(verbose == TRUE){ + tempo.cat <- paste0("PARALLELIZATION INITIATED AT: ", ini.date) + cat(paste0("\n", tempo.cat, "\n")) + } + tempo.thread.nb = parallel::detectCores(all.tests = FALSE, logical = TRUE) # detect the number of threads + if( ! is.null(thread.nb)){ + if(tempo.thread.nb < thread.nb){ + thread.nb <- tempo.thread.nb + if(verbose == TRUE){ + tempo.cat <- paste0("ONLY: ", tempo.thread.nb, " THREADS AVAILABLE") + cat(paste0("\n", tempo.cat, "\n")) + } + } + }else{ + thread.nb <- tempo.thread.nb + } + if(verbose == TRUE){ + tempo.cat <- paste0("NUMBER OF THREADS USED: ", thread.nb) + cat(paste0("\n ", tempo.cat, "\n")) + } + Clust <- parallel::makeCluster(thread.nb, outfile = paste0(res.path, "/fun_slide_parall_log.txt")) # outfile to print or cat during parallelization (only possible in a file, outfile = "" do not work on windows) + cluster.list <- parallel::clusterSplit(Clust, 1:nrow(wind)) # split according to the number of cluster + if(verbose == TRUE){ + tempo.cat <- paste0("SPLIT OF TEST NUMBERS IN PARALLELISATION:") + cat(paste0("\n ", tempo.cat, "\n")) + str(cluster.list) # using print(str()) add a NULL below the result + cat("\n") + } + paral.output.list <- parallel::clusterApply( # + cl = Clust, + x = cluster.list, + function.name = function.name, + data = data, + FUN = fun, # because fun argument of clusterApply + args = args, + thread.nb = thread.nb, + print.count = print.count, + wind = wind, + left = left, + right = right, + res.path = res.path, + lib.path = lib.path, + verbose = verbose, + cute.path = cute.path, + fun = function( + x, + function.name, + data, + FUN, + args, + thread.nb, + print.count, + wind, + left, + right, + res.path, + lib.path, + verbose, + cute.path + ){ + # check again: very important because another R + process.id <- Sys.getpid() + cat(paste0("\nPROCESS ID ", process.id, " -> TESTS ", x[1], " TO ", x[length(x)], "\n")) + source(cute.path, local = .GlobalEnv) + fun_pack(req.package = "lubridate", lib.path = lib.path, load = TRUE) # load = TRUE to be sure that functions are present in the environment. And this prevent to use R.lib.path argument of fun_python_pack() + # end check again: very important because another R + ini.date <- Sys.time() + ini.time <- as.numeric(ini.date) # time of process begin, converted into + output <- NULL + print.count.loop <- 0 + for(i4 in 1:length(x)){ + print.count.loop <- print.count.loop + 1 + log <- get(left)(data, wind$left[x[i4]]) & get(right)(data, wind$right[x[i4]]) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + output <- c(output, eval(parse(text = paste0("FUN(data[log]", if( ! is.null(args)){paste0(", ", args)}, ")")))) + if(verbose == TRUE){ + if(print.count.loop == print.count){ + print.count.loop <- 0 + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) + final.loop <- (tempo.time - ini.time) / i4 * length(x) # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining + final.exp <- as.POSIXct(final.loop, origin = ini.date) + cat(paste0("\nIN PROCESS ", process.id, " | LOOP ", format(i4, big.mark=","), " / ", format(length(x), big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) + } + if(i4 == length(x)){ + tempo.time <- as.numeric(Sys.time()) + tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) + cat(paste0("\nPROCESS ", process.id, " ENDED | LOOP ", format(i4, big.mark=","), " / ", format(length(x), big.mark=","), " | TIME SPENT: ", tempo.lapse, "\n\n")) + } + } + } + wind <- wind[x, ] + if(length(output) != nrow(wind)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + output <- data.frame(wind, value = output, stringsAsFactors = TRUE) + return(output) + } + } + ) + parallel::stopCluster(Clust) + # result assembly + output <- data.frame() + for(i2 in 1:length(paral.output.list)){ # compartment relatives to each parallelization + output <- rbind(output, paral.output.list[[i2]], stringsAsFactors = TRUE) + } + # end result assembly + if(nrow(output) != nrow(wind)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5\nlength(output): ", length(output), "\nnrow(wind): ", nrow(wind)) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + output <- output[order(output$left), ] + } + } + if(verbose == TRUE){ + end.date <- Sys.time() + end.time <- as.numeric(end.date) + total.lapse <- round(lubridate::seconds_to_period(end.time - ini.time)) + cat(paste0("\nfun_slide JOB END\n\nTIME: ", end.date, "\n\nTOTAL TIME LAPSE: ", total.lapse, "\n\n\n")) + } + return(output) } -######## fun_test() #### test combinations of argument values of a function and return errors (and graphs) -# add traceback https://stackoverflow.com/questions/47414119/how-to-read-a-traceback-in-r +######## fun_codon2aa() #### convert codon to amino acid using standard genetic code -fun_test <- function( -fun, -arg, -val, -expect.error = NULL, -parall = FALSE, -thread.nb = NULL, -print.count = 10, -plot.fun = FALSE, -export = FALSE, -res.path = NULL, -lib.path = NULL, -cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" + +fun_codon2aa <- function( + data, + display = FALSE ){ -# AIM -# test combinations of argument values of a function -# WARNINGS -# Limited to 43 arguments with at least 2 values each. The total number of arguments tested can be more if the additional arguments have a single value. The limit is due to nested "for" loops (https://stat.ethz.ch/pipermail/r-help/2008-March/157341.html), but it should not be a problem since the number of tests would be 2^43 > 8e12 -# ARGUMENTS -# fun: character string indicating the name of the function tested (without brackets) -# arg: vector of character strings of arguments of fun. At least arguments that do not have default values must be present in this vector -# val: list with number of compartments equal to length of arg, each compartment containing values of the corresponding argument in arg. Each different value must be in a list or in a vector. For instance, argument 3 in arg is a logical argument (values accepted TRUE, FALSE, NA). Thus, compartment 3 of val can be either list(TRUE, FALSE, NA), or c(TRUE, FALSE, NA). NULL value alone must be written list(NULL) -# expect.error: list of exactly the same structure as val argument, but containing FALSE or TRUE, depending on whether error is expected (TRUE) or not (FALSE) for each corresponding value of val. A message is returned depending on discrepancies between the expected and observed errors. BEWARE: not always possible to write the expected errors for all the combination of argument values. Ignored if NULL -# parall: logical. Force parallelization ? -# thread.nb: numeric value indicating the number of threads to use if ever parallelization is required. If NULL, all the available threads will be used. Ignored if parall is FALSE -# print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired -# plot.fun: logical. Plot the plotting function tested for each test? -# export: logical. Export the results into a .RData file and into a .txt file? If FALSE, return a list into the console (see below). BEWARE: will be automatically set to TRUE if parall is TRUE. This means that when using parallelization, the results are systematically exported, not returned into the console -# res.path: character string indicating the absolute pathway of folder where the txt results and pdfs, containing all the plots, will be saved. Several txt and pdf, one per thread, if parallelization. Ignored if export is FALSE. Must be specified if parall is TRUE or if export is TRUE -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# cute.path: character string indicating the absolute path of the cute.R file. Will be remove when cute will be a package. Ignored if parall is FALSE -# REQUIRED PACKAGES -# lubridate -# parallel if parall arguemtn is TRUE (included in the R installation packages but not automatically loaded) -# pdftools if parall arguemtn is TRUE (included in the R installation packages but not automatically loaded) -# If the tested function is in a package, this package must be imported first (no parallelization) or must be in the classical R package folder indicated by the lib.path argument (parallelization) -# RETURN -# if export is FALSE a list containing: -# $fun: the tested function -# $instruction: the initial instruction -# $sys.info: system and packages info -# $data: a data frame of all the combination tested, containing the following columns: -# the different values tested, named by arguments -# $kind: a vector of character strings indicating the kind of test result: either "ERROR", or "WARNING", or "OK" -# $problem: a logical vector indicating if error or not -# $expected.error: optional logical vector indicating the expected error specified in the expect.error argument -# $message: either NULL if $kind is always "OK", or the messages -# if export is TRUE 1) the same list object into a .RData file, 2) also the $data data frame into a .txt file, and 3) if expect.error is non NULL and if any discrepancy, the $data data frame into a .txt file but containing only the rows with discrepancies between expected and observed errors -# one or several pdf if a plotting function is tested and if the plot.fun argument is TRUE -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_get_message() -# fun_pack() -# EXAMPLES -# fun_test(fun = "unique", arg = c("x", "incomparables"), val = list(x = list(1:10, c(1,1,2,8), NA), incomparable = c(TRUE, FALSE, NA))) -# fun_test(fun = "fun_round", arg = c("data", "dec.nb", "after.lead.zero"), val = list(L1 = list(c(1, 1.0002256, 1.23568), "a", NA), L2 = list(2, c(1,3), NA), L3 = c(TRUE, FALSE, NA))) -# fun_test(fun = "plot", arg = c("x", "y"), val = list(x = list(1:10, 12:13, NA, (1:10)^2), y = list(1:10, NA, NA)), expect.error = list(x = list(FALSE, TRUE, TRUE, FALSE), y = list(FALSE, TRUE, TRUE)), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = NULL) -# fun_test(fun = "plot", arg = c("x", "y"), val = list(x = list(1:10, 12:13, NA, (1:10)^2), y = list(1:10, NA, NA)), parall = FALSE, thread.nb = 4, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_test(fun = "fun_gg_boxplot", arg = c("data1", "y", "categ"), val = list(L1 = list(L1 = obs1), L2 = list(L1 = "Time"), L3 = list(L1 = "Group1"))) -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_test(fun = "fun_gg_boxplot", arg = c("data1", "y", "categ"), val = list(L1 = list(obs1), L2 = "Time", L3 = "Group1"), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") -# library(ggplot2) ; fun_test(fun = "geom_histogram", arg = c("data", "mapping"), val = list(x = list(data.frame(X = "a", stringsAsFactors = TRUE)), y = list(ggplot2::aes(x = X))), parall = FALSE, thread.nb = NULL, plot.fun = TRUE, res.path = "C:\\Users\\Gael\\Desktop\\", lib.path = "C:\\Program Files\\R\\R-4.0.2\\library\\") # BEWARE: ggplot2::geom_histogram does not work -# DEBUGGING -# fun = "unique" ; arg = "x" ; val = list(x = list(1:10, c(1,1,2,8), NA)) ; expect.error = list(x = list(FALSE, FALSE, TRUE)) ; parall = FALSE ; thread.nb = NULL ; plot.fun = FALSE ; export = FALSE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 1 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging -# fun = "unique" ; arg = c("x", "incomparables") ; val = list(x = list(1:10, c(1,1,2,8), NA), incomparable = c(TRUE, FALSE, NA)) ; expect.error = NULL ; parall = FALSE ; thread.nb = 2 ; plot.fun = FALSE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 10 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging -# fun = "plot" ; arg = c("x", "y") ; val = list(x = list(1:10, 12:13, NA), y = list(1:10, NA, NA)) ; expect.error = list(x = list(FALSE, FALSE, TRUE, FALSE), y = list(FALSE, TRUE, TRUE)) ; print.count = 10 ; parall = FALSE ; thread.nb = NULL ; plot.fun = TRUE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL # for function debugging -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun = "fun_gg_boxplot" ; arg = c("data1", "y", "categ") ; val = list(L1 = list(L1 = obs1), L2 = list(L1 = "Time"), L3 = list(L1 = "Group1")) ; expect.error = NULL ; print.count = 10 ; parall = FALSE ; thread.nb = NULL ; plot.fun = TRUE ; export = TRUE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL # for function debugging -# fun = "unique" ; arg = "x" ; val = list(list(1:3, mean)) ; expect.error = list(TRUE, TRUE) ; parall = FALSE ; thread.nb = NULL ; plot.fun = FALSE ; export = FALSE ; res.path = "C:\\Users\\Gael\\Desktop\\" ; lib.path = NULL ; print.count = 1 ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check", -"fun_get_message", -"fun_pack" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words -# end reserved words -# arg with no default values -mandat.args <- c( -"fun", -"arg", -"val" -) -tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), "))"))) -print(tempo) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = fun, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = arg, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = val, class = "list", fun.name = function.name) ; eval(ee) -if( ! is.null(expect.error)){ -tempo <- fun_check(data = expect.error, class = "list", fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = parall, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(parall == TRUE){ -if( ! is.null(thread.nb)){ -tempo <- fun_check(data = thread.nb, typeof = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & thread.nb < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": thread.nb PARAMETER MUST EQUAL OR GREATER THAN 1: ", thread.nb) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = plot.fun, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = export, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(res.path)){ -tempo <- fun_check(data = res.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -} -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = cute.path, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# new environment -env.name <- paste0("env", as.numeric(Sys.time())) -if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -assign(env.name, new.env()) -assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test -} -# end new environment -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"fun", -"arg", -"val", -# "expect.erro", # because can be NULL -"parall", -# "thread.nb", # because can be NULL -"print.count", -"plot.fun", -"export", -# "res.path", # because can be NULL -# "lib.path", # because can be NULL -"cute.path" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -# end warning initiation -# other checkings -if(grepl(x = fun, pattern = "()$")){ # remove () -fun <- sub(x = fun, pattern = "()$", replacement = "") -} -if( ! exists(fun)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CHARACTER STRING IN fun ARGUMENT DOES NOT EXIST IN THE R WORKING ENVIRONMENT: ", paste(fun, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -}else if( ! all(base::class(get(fun)) == "function")){ # here no env = sys.nframe(), inherit = FALSE for get() because fun is a function in the classical scope -tempo.cat <- paste0("ERROR IN ", function.name, ": fun ARGUMENT IS NOT CLASS \"function\" BUT: ", paste(base::class(get(fun)), collapse = "\n"), "\nCHECK IF ANY CREATED OBJECT WOULD HAVE THE NAME OF THE TESTED FUNCTION") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(tempo$problem == FALSE & base::length(arg) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": arg ARGUMENT CANNOT BE LENGTH 0") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -for(i2 in 1:base::length(val)){ -tempo1 <- fun_check(data = val[[i2]], class = "vector", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = val[[i2]], class = "list", na.contain = TRUE, fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": COMPARTMENT ", i2, " OF val ARGUMENT MUST BE A VECTOR OR A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -}else if(tempo1$problem == FALSE){ # vector split into list compartments -val[[i2]] <- split(x = val[[i2]], f = 1:base::length(val[[i2]])) -} -} -if(base::length(arg) != base::length(val)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF arg ARGUMENT MUST BE IDENTICAL TO LENGTH OF val ARGUMENT:\nHERE IT IS: ", base::length(arg), " VERSUS ", base::length(val)) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -args <- names(formals(get(fun))) # here no env = sys.nframe(), inherit = FALSE for get() because fun is a function in the classical scope -if( ! all(arg %in% args)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": SOME OF THE STRINGS IN arg ARE NOT ARGUMENTS OF fun\nfun ARGUMENTS: ", paste(args, collapse = " "),"\nPROBLEMATIC STRINGS IN arg: ", paste(arg[ ! arg %in% args], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(sum(sapply(val, FUN = length) > 1) > 43){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CANNOT TEST MORE THAN 43 ARGUMENTS IF THEY ALL HAVE AT LEAST 2 VALUES EACH\nHERE THE NUMBER IS: ", sum(sapply(val, FUN = length) > 1)) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if( ! is.null(expect.error)){ -if(base::length(val) != base::length(expect.error)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF val ARGUMENT MUST BE IDENTICAL TO LENGTH OF expect.error ARGUMENT:\nHERE IT IS: ", base::length(val), " VERSUS ", base::length(expect.error)) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -for(i3 in 1:base::length(expect.error)){ -tempo1 <- fun_check(data = expect.error[[i3]], class = "vector", mode = "logical", fun.name = function.name) -tempo2 <- fun_check(data = expect.error[[i3]], class = "list", fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": COMPARTMENT ", i3, " OF expect.error ARGUMENT MUST BE TRUE OR FALSE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -}else if(tempo1$problem == FALSE){ # vector split into list compartments -expect.error[[i3]] <- split(x = expect.error[[i3]], f = 1:base::length(expect.error[[i3]])) + # AIM + # Convert codon to amino acid using standard genetic code indicated in https://en.wikipedia.org/wiki/DNA_and_RNA_codon_tables + # WARNINGS + # None + # ARGUMENTS + # data: single caracter string of three characters, or vector of three caracters, indicating the DNA codon (only "A", "T", "G" and "C" allowed). Case insensitive. Omitted if display argument is TRUE + # display: logical. Display the whole genetic table? if TRUE, override data + # RETURN + # The 1 letter uppercase amino acid of the submitted codon or the whole table if display argument is TRUE + # REQUIRED PACKAGES + # None + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # fun_check() + # EXAMPLE + # fun_codon2aa(data = "ATC", display = TRUE) + # see http + # DEBUGGING + # data = "atg" ; display = FALSE + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words + # end reserved words + # arg with no default values + mandat.args <- c( + "data" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data, class = "vector", typeof = "character", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = display, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ # normally no NA + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "data", + "display" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + # end warning initiation + # other checkings + if(length(data) == 1L){ + data <- unlist(strsplit(data, split = "")) + }else if(length(data) != 3L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A STRING OF THREE CHARACTERS OR A VECTOR OF THREE CHARACTERS, MADE OF \"A\", \"C\", \"G\", \"T\" ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! all(toupper(data) %in% c("A", "C", "G","T"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A STRING OF THREE CHARACTERS OR A VECTOR OF THREE CHARACTERS, MADE OF \"A\", \"C\", \"G\", \"T\" ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end other checkings + # reserved word checking + # end reserved word checking + # end second round of checking and data preparation + # package checking + # end package checking + # main code + # standard genetic code + sgc <- array( + c( + "F", "L", "I", "V", + "S", "P", "T", "A", + "Y", "H", "N", "D", + "C", "R", "S", "G", + + "F", "L", "I", "V", + "S", "P", "T", "A", + "Y", "H", "N", "D", + "C", "R", "S", "G", + + "L", "L", "I", "V", + "S", "P", "T", "A", + "stop", "Q", "K", "E", + "stop", "R", "R", "G", + + "L", "L", "M", "V", + "S", "P", "T", "A", + "stop", "Q", "K", "E", + "W", "R", "R", "G" + ), + dim = c(4, 4, 4), + dimnames = list( + first = c("T", "C", "A", "G"), + second = c("T", "C", "A", "G"), + third = c("T", "C", "A", "G") + ) + ) + # end standard genetic code + if(display == TRUE){ + output <- sgc + }else{ + data <- toupper(data) + output <- eval(parse(text = paste0("sgc['", paste0(data, collapse = "','"), "']"))) + } + return(output) } + + +######## fun_codon_finder() #### gives the codon number and position in the codon of nucleotid positions + + +fun_codon_finder <- function( + pos, + begin, + end +){ + # AIM + # gives the codon number and position in the codon of nucleotid positions + # WARNINGS + # Only for coding sequences (no introns): ((end - begin) + 1) / 3 must be an integer (i.e., modulo zero) + # Negatives positions allowed but this implies that one base has the position 0 in the sequence + # ARGUMENTS + # pos: vector of integers indicating the positions of nucleotids in a sequence. Must be between begin and end arguments + # begin: single integer indicating the position of the first base of the coding sequence + # end: single indicating the position of the last base of the coding sequence + # RETURN + # a data frame with column names: + # pos: values of the pos argument + # codon_nb: the codon number in the CDS encompassing the pos value + # codon_pos: the position of pos in the codon (either 1, 2 or 3) + # codon_begin: the first base position of the codon + # codon_end: the last base position of the codon + # REQUIRED PACKAGES + # None + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # fun_check() + # EXAMPLE + # fun_codon_finder(c(5, 6, 8, 10), begin = 5, end = 10) + # fun_codon_finder(c(0, 5, 6, 8, 10), begin = -2, end = 12) + # see http + # DEBUGGING + # pos = c(5, 6, 8, 10) ; begin = 5 ; end = 10 + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words + # end reserved words + # arg with no default values + mandat.args <- c( + "pos", + "begin", + "end" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = pos, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = begin, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = end, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ # normally no NA + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "pos", + "begin", + "end" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + # end warning initiation + # other checkings + if(begin >= end){ + tempo.cat <- paste0("ERROR IN ", function.name, ": end ARGUMENT MUST BE STRICTLY GREATER THAN begin ARGUMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if((end - begin + 1) %% 3 != 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ((end - begin) + 1) / 3 MUST BE AN INTEGER (I.E., MODULO ZERO)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(pos < begin | pos > end)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": pos ARGUMENT VALUES MUST BE BETWEEN begin AND end ARGUMENT VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end other checkings + # reserved word checking + # end reserved word checking + # end second round of checking and data preparation + # package checking + # end package checking + # main code + first <- seq.int(from = begin, to = end, by = 3) + last <- seq.int(from = begin + 2, to = end, by = 3) + tempo <- lapply(X = pos, FUN = function(x = X){ + tempo.log <- x >= first & x <= last + if(sum(tempo.log, na.rm = TRUE) != 1){ # check that 1 possible TRUE + tempo.cat <- paste0("ERROR IN ", function.name, ": INTERNAL ERROR. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + codon_nb <- which(tempo.log) + codon_pos <- as.integer((x - (begin + (codon_nb - 1) * 3) + 1)) + codon_begin <- as.integer(first[tempo.log]) + codon_end <- as.integer(last[tempo.log]) + } + return(data.frame(codon_nb = codon_nb, codon_pos = codon_pos, codon_begin = codon_begin, codon_end = codon_end)) + }) + tempo <- do.call("rbind", tempo) + output <- data.frame(pos = as.integer(pos), tempo) + return(output) } + + +################ Graphics management + + +# this order can be used: +# fun_width() +# fun_open() +# fun_prior_plot() # not for ggplot2 +# plot() or any other plotting +# fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 +# fun_close() + + +######## fun_width() #### window width depending on classes to plot + + +fun_width <- function( + class.nb, + inches.per.class.nb = 1, + ini.window.width = 7, + inch.left.space, + inch.right.space, + boundarie.space = 0.5 +){ + # AIM + # rescale the width of a window to open depending on the number of classes to plot + # can be used for height, considering that it is as if it was a width + # this order can be used: + # fun_width() + # fun_open() + # fun_prior_plot() # not for ggplot2 + # plot() or any other plotting + # fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 + # fun_close() + # ARGUMENTS + # class.nb: number of class to plot + # inches.per.class.nb: number of inches per unit of class.nb. 2 means 2 inches for each boxplot for instance + # ini.window.width:initial window width in inches + # inch.left.space: left horizontal margin of the figure region (in inches) + # inch.right.space: right horizontal margin of the figure region (in inches) + # boundarie.space: space between the right and left limits of the plotting region and the plot (0.5 means half a class width) + # RETURN + # the new window width in inches + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_width(class.nb = 10, inches.per.class.nb = 0.2, ini.window.width = 7, inch.left.space = 1, inch.right.space = 1, boundarie.space = 0.5) + # DEBUGGING + # class.nb = 10 ; inches.per.class.nb = 0.2 ; ini.window.width = 7 ; inch.left.space = 1 ; inch.right.space = 1 ; boundarie.space = 0.5 # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = class.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = inches.per.class.nb, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = ini.window.width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = inch.left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = inch.right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = boundarie.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + range.max <- class.nb + boundarie.space # the max range of the future plot + range.min <- boundarie.space # the min range of the future plot + window.width <- inch.left.space + inch.right.space + inches.per.class.nb * (range.max - range.min) + return(window.width) } -if( ! is.null(res.path)){ -if( ! all(dir.exists(res.path))){ # separation to avoid the problem of tempo$problem == FALSE and res.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE res.path ARGUMENT DOES NOT EXISTS:\n", paste(res.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -} -if(parall == TRUE & is.null(res.path)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": res.path ARGUMENT MUST BE SPECIFIED IF parall ARGUMENT IS TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(is.null(res.path) & export == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": res.path ARGUMENT MUST BE SPECIFIED IF export ARGUMENT TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(parall == TRUE & export == FALSE){ -export <- TRUE -tempo.cat <- paste0("WARNING FROM ", function.name, ": export ARGUMENT CONVERTED TO TRUE BECAUSE thread.nb ARGUMENT IS NOT NULL") -warning(paste0("\n", tempo.cat, "\n"), call. = FALSE) -} -if( ! is.null(lib.path)){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -} -if(parall == TRUE){ -if(grepl(x = cute.path, pattern = "^http")){ -tempo.error1 <- any(grepl(x = fun_get_message(data = "source(cute.path)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)), pattern = "^[Ee]rror")) -tempo.error2 <- FALSE -}else{ -tempo.error1 <- FALSE -tempo.error2 <- ! file.exists(cute.path) -} -if(tempo.error1 | tempo.error2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(grepl(x = cute.path, pattern = "^http"), "URL", "FILE"), " PATH INDICATED IN THE cute.path PARAMETER DOES NOT EXISTS:\n", cute.path) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -} -# end other checkings -# reserved word checking -# end reserved word checking -# end second round of checking and data preparation -# package checking -fun_pack(req.package = c("lubridate"), lib.path = lib.path) -if(parall == TRUE){ -fun_pack(req.package = c("parallel", "pdftools"), lib.path = lib.path) -} -# end package checking -# declaration of special plot functions -sp.plot.fun <- c("fun_gg_scatter", "fun_gg_bar", "fun_gg_boxplot") -# end declaration of special plot functions -# main code -ini.warning.length <- base::options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -cat("\nfun_test JOB IGNITION\n") -ini.date <- Sys.time() -ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds -if(export == TRUE){ -res.path <- paste0(res.path, "/fun_test_res_", trunc(ini.time)) -if(dir.exists(res.path)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": FOLDER ALREADY EXISTS\n", res.path, "\nPLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -dir.create(res.path) -} -} -total.comp.nb <- prod(sapply(val, FUN = "length")) -cat(paste0("\nTHE TOTAL NUMBER OF TESTS IS: ", total.comp.nb, "\n")) -# creation of the txt instruction that includes several loops -loop.string <- NULL -end.loop.string <- NULL -fun.args <- NULL -fun.args2 <- NULL -error.values <- NULL -arg.values <- "list(" -for(i1 in 1:base::length(arg)){ -if(parall == FALSE){ -if(base::length(val[[i1]]) > 1){ # loop only if more than one value in base::length(val[[i1]]) -loop.string <- paste0(loop.string, "for(i", i1, " in 1:", base::length(val[[i1]]), "){") -end.loop.string <- paste0(end.loop.string, "}") -} -}else{ -loop.string <- "for(i in x){" -end.loop.string <- "}" -} -fun.args <- paste0( -fun.args, -ifelse(i1 == 1L, "", ", "), -arg[i1], -" = val[[", -i1, -"]][[", -if(parall == FALSE){ -if(base::length(val[[i1]]) > 1){ -paste0("i", i1) -}else{ -"1" # a unique element in val[[i1]] -} -}else{ -paste0("i.list[[", i1, "]][i]") -}, -"]]" -) -fun.args2 <- paste0( -fun.args2, -ifelse(i1 == 1L, "", ", "), -arg[i1], -" = val[[", -i1, -"]][[', ", -if(parall == FALSE){ -if(base::length(val[[i1]]) > 1){ -paste0("i", i1) -}else{ -"1" # a unique element in val[[i1]] -} -}else{ -paste0("i.list[[", i1, "]][i]") -}, -", ']]" -) -arg.values <- paste0( -arg.values, -"val[[", i1, "]][[", -if(parall == FALSE){ -if(base::length(val[[i1]]) > 1){ -paste0("i", i1) -}else{ -"1" # a unique element in val[[i1]] -} -}else{ -paste0("i.list[[", i1, "]][i]") -}, -"]]", -ifelse(i1 == base::length(arg), "", ", ") -) -error.values <- paste0( -error.values, -ifelse(i1 == 1L, "", " | "), -"expect.error[[", i1, "]][[", -if(parall == FALSE){ -if(base::length(expect.error[[i1]]) > 1){ -paste0("i", i1) -}else{ -"1" # a unique element in expect.error[[i1]] -} -}else{ -paste0("i.list[[", i1, "]][i]") -}, -"]]" -) -} -arg.values <- paste0(arg.values, ")") -fun.test <- paste0(fun, "(", fun.args, ")") -fun.test2 <- paste0("paste0('", fun, "(", fun.args2, ")')") -# plot title for special plot functions -if(plot.fun == TRUE){ -plot.kind <- "classic" -if(fun %in% sp.plot.fun){ -plot.kind <- "special" -if(any(arg %in% "title")){ # this is for the special functions -tempo.match <- regmatches(x = fun.test, m = regexpr(text = fun.test, pattern = "title = .+[,)]")) -tempo.match <- substring(tempo.match , 1, nchar(tempo.match) - 1) -fun.test <- sub(x = fun.test, pattern = tempo.match, replacement = paste0(tempo.match, "\ntempo.title")) -}else{ -fun.test <- sub(x = fun.test, pattern = ")$", replacement = ", title = tempo.title)") -} -} -} -# end plot title for special plot functions -kind <- character() -problem <- logical() -expected.error <- logical() -res <- character() -count <- 0 -print.count.loop <- 0 -plot.count <- 0 -if(base::length(arg) == 1L){ -data <- data.frame() -}else{ # base::length(arg) == 0L already tested above -data <- data.frame(t(vector("character", base::length(arg))), stringsAsFactors = FALSE)[-1, ] # -1 to remove the single row created and to have an empty data frame with base::length(arg) columns -} -code <- paste( -loop.string, ' -count <- count + 1 -print.count.loop <- print.count.loop + 1 -arg.values.print <- eval(parse(text = arg.values)) # recover the list of the i1 compartment -for(j3 in 1:base::length(arg.values.print)){ # WARNING: do not use i1, i2 etc., here because already in loop.string -tempo.capt <- capture.output(tempo.error <- fun_get_message(data = paste0("paste(arg.values.print[[", j3, "]])"), kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE))) # collapsing arg.values sometimes does not work (with function for instance) -if( ! is.null(tempo.error)){ -arg.values.print[[j3]] <- paste0("SPECIAL VALUE OF CLASS ", base::class(arg.values.print[[j3]]), " AND TYPE ", base::typeof(arg.values.print[[j3]])) -} -} -data <- rbind(data, as.character(sapply(arg.values.print, FUN = "paste", collapse = " ")), stringsAsFactors = FALSE) # each colum is a test -tempo.capt <- capture.output(tempo.try.error <- fun_get_message(data = eval(parse(text = fun.test2)), kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE))) # data argument needs a character string but eval(parse(text = fun.test2)) provides it (eval parse replace the i1, i2, etc., by the correct values, meaning that only val is required in the env.name environment) -tempo.capt <- capture.output(tempo.try.warning <- fun_get_message(data = eval(parse(text = fun.test2)), kind = "warning", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE), print.no = TRUE)) # data argument needs a character string but eval(parse(text = fun.test2)) provides it (eval parse replace the i1, i2, etc., by the correct values, meaning that only val is required in the env.name environment) -if( ! is.null(expect.error)){ -expected.error <- c(expected.error, eval(parse(text = error.values))) -} -if( ! is.null(tempo.try.error)){ -kind <- c(kind, "ERROR") -problem <- c(problem, TRUE) -res <- c(res, tempo.try.error) -}else{ -if( ! is.null(tempo.try.warning)){ -kind <- c(kind, "WARNING") -problem <- c(problem, FALSE) -res <- c(res, tempo.try.warning) -}else{ -kind <- c(kind, "OK") -problem <- c(problem, FALSE) -res <- c(res, "") -} -if(plot.fun == TRUE){ -invisible(dev.set(window.nb)) -plot.count <- plot.count + 1 -tempo.title <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), ifelse(parall == FALSE, count, x[count]))) -if(plot.kind == "classic"){ -eval(parse(text = fun.test)) -tempo <- fun_post_plot(corner.text = tempo.title) -}else if(plot.kind == "special"){ -eval(parse(text = fun.test)) -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR 1 IN ", function.name, ": CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -} -if(print.count.loop == print.count){ -print.count.loop <- 0 -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -final.loop <- (tempo.time - ini.time) / count * ifelse(parall == FALSE, total.comp.nb, base::length(x)) # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining -final.exp <- as.POSIXct(final.loop, origin = ini.date) -cat(paste0(ifelse(parall == FALSE, "\n", paste0("\nIN PROCESS ", process.id, " | ")), "LOOP ", format(count, big.mark=","), " / ", format(ifelse(parall == FALSE, total.comp.nb, base::length(x)), big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) -} -if(count == ifelse(parall == FALSE, total.comp.nb, base::length(x))){ -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -cat(paste0(ifelse(parall == FALSE, "\nLOOP PROCESS ENDED | ", paste0("\nPROCESS ", process.id, " ENDED | ")), "LOOP ", format(count, big.mark=","), " / ", format(ifelse(parall == FALSE, total.comp.nb, base::length(x)), big.mark=","), " | TIME SPENT: ", tempo.lapse, "\n\n")) -} -', -end.loop.string -) -# end creation of the txt instruction that includes several loops -if(parall == TRUE){ -# list of i numbers that will be split -i.list <- vector("list", base::length(val)) # positions to split in parallel jobs -for(i2 in 1:base::length(arg)){ -if(i2 == 1L){ -tempo.divisor <- total.comp.nb / base::length(val[[i2]]) -i.list[[i2]] <- rep(1:base::length(val[[i2]]), each = as.integer(tempo.divisor)) -tempo.multi <- base::length(val[[i2]]) -}else{ -tempo.divisor <- tempo.divisor / base::length(val[[i2]]) -i.list[[i2]] <- rep(rep(1:base::length(val[[i2]]), each = as.integer(tempo.divisor)), time = as.integer(tempo.multi)) -tempo.multi <- tempo.multi * base::length(val[[i2]]) -} -} -# end list of i numbers that will be split -tempo.cat <- paste0("PARALLELIZATION INITIATED AT: ", ini.date) -cat(paste0("\n", tempo.cat, "\n")) -tempo.thread.nb = parallel::detectCores(all.tests = FALSE, logical = TRUE) # detect the number of threads -if(tempo.thread.nb < thread.nb){ -thread.nb <- tempo.thread.nb -} -tempo.cat <- paste0("NUMBER OF THREADS USED: ", thread.nb) -cat(paste0("\n ", tempo.cat, "\n")) -Clust <- parallel::makeCluster(thread.nb, outfile = paste0(res.path, "/fun_test_parall_log.txt")) # outfile to print or cat during parallelization (only possible in a file, outfile = "" do not work on windows) -tempo.cat <- paste0("SPLIT OF TEST NUMBERS IN PARALLELISATION:") -cat(paste0("\n ", tempo.cat, "\n")) -cluster.list <- parallel::clusterSplit(Clust, 1:total.comp.nb) # split according to the number of cluster -str(cluster.list) # using print(str()) add a NULL below the result -cat("\n") -paral.output.list <- parallel::clusterApply( # paral.output.list is a list made of thread.nb compartments, each made of n / thread.nb (mat theo column number) compartment. Each compartment receive the corresponding results of fun_permut(), i.e., data (permuted mat1.perm), warning message, cor (final correlation) and count (number of permutations) -cl = Clust, -x = cluster.list, -function.name = function.name, -instruction = instruction, -thread.nb = thread.nb, -print.count = print.count, -total.comp.nb = total.comp.nb, -sp.plot.fun = sp.plot.fun, -i.list = i.list, -fun.tested = fun, -arg.values = arg.values, -fun.test = fun.test, -fun.test2 = fun.test2, -kind = kind, -problem = problem, -res = res, -count = count, -plot.count = plot.count, -data = data, -code = code, -plot.fun = plot.fun, -res.path = res.path, -lib.path = lib.path, -cute.path = cute.path, -fun = function( -x, -function.name, -instruction, -thread.nb, -print.count, -total.comp.nb, -sp.plot.fun, -i.list, -fun.tested, -arg.values, -fun.test, -fun.test2, -kind, -problem, -res, -count, -plot.count, -data, -code, -plot.fun, -res.path, -lib.path, -cute.path -){ -# check again: very important because another R -process.id <- Sys.getpid() -cat(paste0("\nPROCESS ID ", process.id, " -> TESTS ", x[1], " TO ", x[base::length(x)], "\n")) -source(cute.path, local = .GlobalEnv) -fun_pack(req.package = "lubridate", lib.path = lib.path, load = TRUE) # load = TRUE to be sure that functions are present in the environment. And this prevent to use R.lib.path argument of fun_python_pack() -# end check again: very important because another R -# plot management -if(plot.fun == TRUE){ -pdf(file = paste0(res.path, "/plots_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".pdf", paste0("-", x[base::length(x)], ".pdf")))) -}else{ -pdf(file = NULL) # send plots into a NULL file, no pdf file created -} -window.nb <- dev.cur() -invisible(dev.set(window.nb)) -# end plot management -# new environment -ini.date <- Sys.time() -ini.time <- as.numeric(ini.date) # time of process begin, converted into -env.name <- paste0("env", ini.time) -if(exists(env.name, where = -1)){ # verify if still ok when fun_test() is inside a function -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -assign(env.name, new.env()) -assign("val", val, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # var replaced by val -} -# end new environment -print.count.loop <- 0 -suppressMessages(suppressWarnings(eval(parse(text = code)))) -colnames(data) <- arg -if( ! is.null(expect.error)){ -data <- data.frame(data, kind = kind, problem = problem, expected.error = expected.error, message = res, stringsAsFactors = FALSE) -}else{ -data <- data.frame(data, kind = kind, problem = problem, message = res, stringsAsFactors = FALSE) -} -row.names(data) <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), x)) -sys.info <- sessionInfo() -sys.info$loadedOnly <- sys.info$loadedOnly[order(names(sys.info$loadedOnly))] # sort the packages -invisible(dev.off(window.nb)) -rm(env.name) # optional, because should disappear at the end of the function execution -# output -output <- list(fun = fun, instruction = instruction, sys.info = sys.info) # data = data finally removed from the output list, because everything combined in a RData file at the end -save(output, file = paste0(res.path, "/fun_test_", x[1], ifelse(base::length(x) == 1L, ".RData", paste0("-", x[base::length(x)], ".RData")))) -if(plot.fun == TRUE & plot.count == 0L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN PROCESS ", process.id, ": NO PDF PLOT BECAUSE ONLY ERRORS REPORTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -file.remove(paste0(res.path, "/plots_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".pdf", paste0("-", x[base::length(x)], ".pdf")))) -} -table.out <- as.matrix(data) -# table.out[table.out == ""] <- " " # does not work # because otherwise read.table() converts "" into NA -table.out <- gsub(table.out, pattern = "\n", replacement = " ") -write.table(table.out, file = paste0(res.path, "/table_from_fun_test_", x[1], ifelse(base::length(x) == 1L, ".txt", paste0("-", x[base::length(x)], ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") -} -) -parallel::stopCluster(Clust) -# files assembly -if(base::length(cluster.list) > 1){ -for(i2 in 1:base::length(cluster.list)){ -tempo.file <- paste0(res.path, "/table_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".txt", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".txt"))) # txt file -tempo <- read.table(file = tempo.file, header = TRUE, stringsAsFactors = FALSE, sep = "\t", row.names = 1, comment.char = "", colClasses = "character") # row.names = 1 (1st column) because now read.table() adds a NA in the header if the header starts by a tabulation, comment.char = "" because colors with #, colClasses = "character" otherwise convert "" (from NULL) into NA -if(file.exists(paste0(res.path, "/plots_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".pdf", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".pdf"))))){ -tempo.pdf <- paste0(res.path, "/plots_from_fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".pdf", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".pdf"))) # pdf file -}else{ -tempo.pdf <- NULL -} -tempo.rdata <- paste0(res.path, "/fun_test_", min(cluster.list[[i2]], na.rm = TRUE), ifelse(base::length(cluster.list[[i2]]) == 1L, ".RData", paste0("-", max(cluster.list[[i2]], na.rm = TRUE), ".RData"))) # RData file -if(i2 == 1L){ -final.file <- tempo -final.pdf <- tempo.pdf -# new env for RData combining -env.name <- paste0("env", ini.time) -if(exists(env.name, where = -1)){ # verify if still ok when fun_test() is inside a function -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -# end new env for RData combining -}else{ -assign(env.name, new.env()) -load(tempo.rdata, envir = get(env.name)) -tempo.rdata1 <- tempo.rdata -assign("final.output", get("output", envir = get(env.name)), envir = get(env.name)) -} -}else{ -final.file <- rbind(final.file, tempo, stringsAsFactors = TRUE) -final.pdf <- c(final.pdf, tempo.pdf) -load(tempo.rdata, envir = get(env.name)) -if( ! identical(get("final.output", envir = get(env.name))[c("R.version", "locale", "platform")], get("output", envir = get(env.name))[c("R.version", "locale", "platform")])){ -tempo.cat <- paste0("ERROR IN ", function.name, ": DIFFERENCE BETWEEN OUTPUTS WHILE THEY SHOULD BE IDENTICAL\nPLEASE CHECK\n", tempo.rdata1, "\n", tempo.rdata) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -# add the differences in RData $sysinfo into final.output -tempo.base1 <- sort(get("final.output", envir = get(env.name))$sys.info$basePkgs) -tempo.base2 <- sort(get("output", envir = get(env.name))$sys.info$basePkgs) -tempo.other1 <- names(get("final.output", envir = get(env.name))$sys.info$otherPkgs) -tempo.other2 <- names(get("output", envir = get(env.name))$sys.info$otherPkgs) -tempo.loaded1 <- names(get("final.output", envir = get(env.name))$sys.info$loadedOnly) -tempo.loaded2 <- names(get("output", envir = get(env.name))$sys.info$loadedOnly) -assign("final.output", { -x <- get("final.output", envir = get(env.name)) -y <- get("output", envir = get(env.name)) -x$sys.info$basePkgs <- sort(unique(tempo.base1, tempo.base2)) -if( ! all(tempo.other2 %in% tempo.other1)){ -x$sys.info$otherPkgs <- c(x$sys.info$otherPkgs, y$sys.info$otherPkgs[ ! (tempo.other2 %in% tempo.other1)]) -x$sys.info$otherPkgs <- x$sys.info$otherPkgs[order(names(x$sys.info$otherPkgs))] -} -if( ! all(tempo.loaded2 %in% tempo.loaded1)){ -x$sys.info$loadedOnly <- c(x$sys.info$loadedOnly, y$sys.info$loadedOnly[ ! (tempo.loaded2 %in% tempo.loaded1)]) -x$sys.info$loadedOnly <- x$sys.info$loadedOnly[order(names(x$sys.info$loadedOnly))] -} -x -}, envir = get(env.name)) -# add the differences in RData $sysinfo into final.output -} -} -file.remove(c(tempo.file, tempo.rdata)) -} -# combine pdf and save -if( ! is.null(final.pdf)){ -pdftools::pdf_combine( -input = final.pdf, -output = paste0(res.path, "/plots_from_fun_test_1-", total.comp.nb, ".pdf") -) -file.remove(final.pdf) -} -# end combine pdf and save -# save RData -assign("output", c(get("final.output", envir = get(env.name)), data = list(final.file)), envir = get(env.name)) -save(output, file = paste0(res.path, "/fun_test__1-", total.comp.nb, ".RData"), envir = get(env.name)) -rm(env.name) # optional, because should disappear at the end of the function execution -# end save RData -# save txt -write.table(final.file, file = paste0(res.path, "/table_from_fun_test_1-", total.comp.nb, ".txt"), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") -# end save txt -if( ! is.null(expect.error)){ -final.file <- final.file[ ! final.file$problem == final.file$expected.error, ] -if(nrow(final.file) == 0L){ -cat(paste0("NO DISCREPANCY BETWEEN EXPECTED AND OBSERVED ERRORS\n\n")) -}else{ -cat(paste0("DISCREPANCIES BETWEEN EXPECTED AND OBSERVED ERRORS (SEE THE discrepancy_table_from_fun_test_1-", total.comp.nb, ".txt FILE)\n\n")) -write.table(final.file, file = paste0(res.path, "/discrepancy_table_from_fun_test_1-", total.comp.nb, ".txt"), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") -} -} -} -# end files assembly -}else{ -# plot management -if(plot.fun == TRUE){ -pdf(file = paste0(res.path, "/plots_from_fun_test_1", ifelse(total.comp.nb == 1L, ".pdf", paste0("-", total.comp.nb, ".pdf")))) -}else{ -pdf(file = NULL) # send plots into a NULL file, no pdf file created -} -window.nb <- dev.cur() -invisible(dev.set(window.nb)) -# end plot management -# new environment -env.name <- paste0("env", ini.time) -if(exists(env.name, where = -1)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -assign(env.name, new.env()) -assign("val", val, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # var replaced by val -} -# end new environment -suppressMessages(suppressWarnings(eval(parse(text = code)))) -colnames(data) <- arg -expect.data <- data.frame() -if( ! is.null(expect.error)){ -data <- data.frame(data, kind = kind, problem = problem, expected.error = expected.error, message = res, stringsAsFactors = FALSE) -}else{ -data <- data.frame(data, kind = kind, problem = problem, message = res, stringsAsFactors = FALSE) -} -row.names(data) <- paste0("test_", sprintf(paste0("%0", nchar(total.comp.nb), "d"), 1:total.comp.nb)) -sys.info <- sessionInfo() -sys.info$loadedOnly <- sys.info$loadedOnly[order(names(sys.info$loadedOnly))] # sort the packages -invisible(dev.off(window.nb)) -rm(env.name) # optional, because should disappear at the end of the function execution -# output -output <- list(fun = fun, instruction = instruction, sys.info = sys.info, data = data) -if(plot.fun == TRUE & plot.count == 0L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO PDF PLOT BECAUSE ONLY ERRORS REPORTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -file.remove(paste0(res.path, "/plots_from_fun_test_1", ifelse(total.comp.nb == 1L, ".pdf", paste0("-", total.comp.nb, ".pdf")))) -} -if( ! is.null(expect.error)){ -expect.data <- output$data[ ! output$data$problem == output$data$expected.error, ] -if(nrow(expect.data) == 0L){ -cat(paste0("NO DISCREPANCY BETWEEN EXPECTED AND OBSERVED ERRORS\n\n")) -}else{ -cat(paste0("DISCREPANCIES BETWEEN EXPECTED AND OBSERVED ERRORS (SEE THE ", if(export == TRUE){paste0("discrepancy_table_from_fun_test_1", ifelse(total.comp.nb == 1L, "", paste0("-", total.comp.nb)), ".txt FILE")}else{"$data RESULT"}, ")\n\n")) -if(export == TRUE){ -expect.data <- as.matrix(expect.data) -expect.data <- gsub(expect.data, pattern = "\n", replacement = " ") -write.table(expect.data, file = paste0(res.path, "/discrepancy_table_from_fun_test_1", ifelse(total.comp.nb == 1L, ".txt", paste0("-", total.comp.nb, ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") -} -} -} -if( ! is.null(warn)){ -base::options(warning.length = 8170) -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = base::options(warning.length = ini.warning.length), add = TRUE) -if(export == TRUE){ -save(output, file = paste0(res.path, "/fun_test_1", ifelse(total.comp.nb == 1L, ".RData", paste0("-", total.comp.nb, ".RData")))) -table.out <- as.matrix(output$data) -table.out <- gsub(table.out, pattern = "\n", replacement = " ") -write.table(table.out, file = paste0(res.path, "/table_from_fun_test_1", ifelse(total.comp.nb == 1L, ".txt", paste0("-", total.comp.nb, ".txt"))), row.names = TRUE, col.names = NA, append = FALSE, quote = FALSE, sep = "\t", eol = "\n", na = "") -}else{ -return(output) -} -} -# after return() ? -end.date <- Sys.time() -end.time <- as.numeric(end.date) -total.lapse <- round(lubridate::seconds_to_period(end.time - ini.time)) -cat(paste0("fun_test JOB END\n\nTIME: ", end.date, "\n\nTOTAL TIME LAPSE: ", total.lapse, "\n\n\n")) -} - - -################ Object modification - - -######## fun_name_change() #### check a vector of character strings and modify any string if present in another vector - - -fun_name_change <- function(data1, data2, added.string = "_modif"){ -# AIM -# this function allow to check if a vector of character strings, like column names of a data frame, has elements present in another vector (vector of reserved words or column names of another data frame before merging) -# ARGUMENTS -# data1: vector of character strings to check and modify -# data2: reference vector of character strings -# added.string: string added at the end of the modified string in data1 if present in data2 -# RETURN -# a list containing -# $data: the modified data1 (in the same order as in the initial data1) -# $ini: the initial elements before modification. NULL if no modification -# $post: the modified elements in the same order as in ini. NULL if no modification -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# obs1 <- c("A", "B", "C", "D") ; obs2 <- c("A", "C") ; fun_name_change(obs1, obs2) -# obs1 <- c("A", "B", "C", "C_modif1", "D") ; obs2 <- c("A", "A_modif1", "C") ; fun_name_change(obs1, obs2) # the function checks that the new names are neither in obs1 nor in obs2 (increment the number after the added string) -# DEBUGGING -# data1 = c("A", "B", "C", "D") ; data2 <- c("A", "C") ; added.string = "_modif" # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data1, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = data2, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = added.string, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -ini <- NULL -post <- NULL -if(any(data1 %in% data2)){ -tempo.names <- data1[data1 %in% data2] -ini <- NULL -post <- NULL -for(i2 in 1:length(tempo.names)){ -count <- 0 -tempo <- tempo.names[i2] -while(any(tempo %in% data2) | any(tempo %in% data1)){ -count <- count + 1 -tempo <- paste0(tempo.names[i2], "_modif", count) -} -data1[data1 %in% tempo.names[i2]] <- paste0(tempo.names[i2], "_modif", count) -if(count != 0){ -ini <- c(ini, tempo.names[i2]) -post <- c(post, paste0(tempo.names[i2], "_modif", count)) -} -} -data <- data1 -}else{ -data <- data1 -} -output <- list(data = data, ini = ini, post = post) -return(output) -} - - -######## fun_df_remod() #### remodeling a data frame to have column name as a qualitative values and vice-versa - - -fun_df_remod <- function( -data, -quanti.col.name = "quanti", -quali.col.name = "quali" -){ -# AIM -# if the data frame is made of n numeric columns, a new data frame is created, with the 1st column gathering all the numeric values, and the 2nd column being the name of the columns of the initial data frame. If row names were present in the initial data frame, then a new ini_rowname column is added with the names of the rows - - -# If the data frame is made of one numeric column and one character or factor column, a new data frame is created, with the new columns corresponding to the split numeric values (according to the character column). NA are added a the end of each column to have the same number of rows. BEWARE: in such data frame, rows are not individuals. This means that in the example below, values 10 and 20 are associated on the same row but that means nothing in term of association - - - -# ARGUMENTS -# data: data frame to convert -# quanti.col.name: optional name for the quanti column of the new data frame -# quali.col.name: optional name for the quali column of the new data frame -# RETURN -# the modified data frame -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# obs <- data.frame(col1 = (1:4)*10, col2 = c("A", "B", "A", "A"), stringsAsFactors = TRUE) ; obs ; fun_df_remod(obs) -# obs <- data.frame(col1 = (1:4)*10, col2 = 5:8, stringsAsFactors = TRUE) ; obs ; fun_df_remod(obs, quanti.col.name = "quanti", quali.col.name = "quali") -# obs <- data.frame(col1 = (1:4)*10, col2 = 5:8, stringsAsFactors = TRUE) ; rownames(obs) <- paste0("row", 1:4) ; obs ; fun_df_remod(obs, quanti.col.name = "quanti", quali.col.name = "quali") -# DEBUGGING -# data = data.frame(a = 1:3, b = 4:6, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging -# data = data.frame(a = 1:3, b = 4:6, c = 11:13, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging -# data = data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging -# data = data.frame(a = 1:3, b = letters[1:3], stringsAsFactors = TRUE) ; quanti.col.name = "TEST" ; quali.col.name = "quali" # for function debugging -# data = data.frame(b = letters[1:3], a = 1:3, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging -# data = data.frame(b = c("e", "e", "h"), a = 1:3, stringsAsFactors = TRUE) ; quanti.col.name = "quanti" ; quali.col.name = "quali" # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking without fun_check() -if( ! any(class(data) %in% "data.frame")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data ARGUMENT MUST BE A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking without fun_check() -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = quanti.col.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = quali.col.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -tempo.factor <- unlist(lapply(data, class)) -for(i in 1:length(tempo.factor)){ # convert factor columns as character -if(all(tempo.factor[i] == "factor")){ -data[, i] <- as.character(data[, i]) -} -} -tempo.factor <- unlist(lapply(data, mode)) -if(length(data) == 2L){ -if( ! ((base::mode(data[, 1]) == "character" & base::mode(data[, 2]) == "numeric") | base::mode(data[, 2]) == "character" & base::mode(data[, 1]) == "numeric" | base::mode(data[, 2]) == "numeric" & base::mode(data[, 1]) == "numeric") ){ -tempo.cat <- paste0("ERROR IN ", function.name, ": IF data ARGUMENT IS A DATA FRAME MADE OF 2 COLUMNS, EITHER A COLUMN MUST BE NUMERIC AND THE OTHER CHARACTER, OR THE TWO COLUMNS MUST BE NUMERIC") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if((base::mode(data[, 1]) == "character" | base::mode(data[, 2]) == "character") & (quanti.col.name != "quanti" | quali.col.name != "quali")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": IMPROPER quanti.col.name OR quali.col.name RESETTINGS. THESE ARGUMENTS ARE RESERVED FOR DATA FRAMES MADE OF n NUMERIC COLUMNS ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -if( ! all(tempo.factor %in% "numeric")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": IF data ARGUMENT IS A DATA FRAME MADE OF ONE COLUMN, OR MORE THAN 2 COLUMNS, THESE COLUMNS MUST BE NUMERIC") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(( ! any(tempo.factor %in% "character")) & is.null(names(data))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": NUMERIC DATA FRAME in the data ARGUMENT MUST HAVE COLUMN NAMES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(all(tempo.factor %in% "numeric")){ # transfo 1 -quanti <- NULL -for(i in 1:length(data)){ -quanti <-c(quanti, data[, i]) -} -quali <- rep(names(data), each = nrow(data)) -output.data <- data.frame(quanti, quali, stringsAsFactors = TRUE, check.names = FALSE) -names(output.data) <- c(quanti.col.name, quali.col.name) -# add the ini_rowname column -ini.rownames <- rownames(data) -tempo.data <- data -rownames(tempo.data) <- NULL -null.rownames <- (tempo.data) -if( ! identical(ini.rownames, null.rownames)){ -ini_rowname <- rep(ini.rownames, times = ncol(data)) -output.data <- cbind(output.data, ini_rowname, stringsAsFactors = TRUE) -} -}else{ # transfo 2 -if(class(data[, 1]) == "character"){ -data <- cbind(data[2], data[1], stringsAsFactors = TRUE) -} -nc.max <- max(table(data[, 2])) # effectif maximum des classes -nb.na <- nc.max - table(data[,2]) # nombre de NA à ajouter pour réaliser la data frame -tempo<-split(data[, 1], data[, 2]) -for(i in 1:length(tempo)){tempo[[i]] <- append(tempo[[i]], rep(NA, nb.na[i]))} # des NA doivent être ajoutés lorsque les effectifs sont différents entre les classes. C'est uniquement pour que chaque colonne ait le même nombre de lignes -output.data<-data.frame(tempo, stringsAsFactors = TRUE, check.names = FALSE) -} -return(output.data) -} - - - - -######## fun_round() #### rounding number if decimal present - - -fun_round <- function(data, dec.nb = 2, after.lead.zero = TRUE){ -# AIM -# round a vector of values, if decimal, with the desired number of decimal digits after the decimal leading zeros -# WARNINGS -# Work well with numbers as character strings, but not always with numerical numbers because of the floating point -# Numeric values are really truncated from a part of their decimal digits, whatever options(digits) settings -# See ?.Machine or https://stackoverflow.com/questions/5173692/how-to-return-number-of-decimal-places-in-r, with the interexting formula: abs(x - round(x)) > .Machine$double.eps^0.5 -# ARGUMENTS -# data: a vector of numbers (numeric or character mode) -# dec.nb: number of required decimal digits -# after.lead.zero: logical. If FALSE, rounding is performed for all the decimal numbers, whatever the leading zeros (e.g., 0.123 -> 0.12 and 0.00128 -> 0.00). If TRUE, dec.nb are taken after the leading zeros (e.g., 0.123 -> 0.12 and 0.00128 -> 0.0013) -# RETURN -# the modified vector -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, 10, 100.001, 333.0001254, 12312.1235), dec.nb = 2, after.lead.zero = FALSE), "\n\n") ; options(digits = ini.options) -# ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, 10, 100.001, 333.0001254, 12312.1235), dec.nb = 2, after.lead.zero = TRUE), "\n\n") ; options(digits = ini.options) -# ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, "10", "100.001", "333.0001254", "12312.1235"), dec.nb = 2, after.lead.zero = FALSE), "\n\n") ; options(digits = ini.options) -# ini.options <- options()$digits ; options(digits = 8) ; cat(fun_round(data = c(NA, "10", "100.001", "333.0001254", "12312.1235"), dec.nb = 2, after.lead.zero = TRUE), "\n\n") ; options(digits = ini.options) -# DEBUGGING -# data = data = c(10, 100.001, 333.0001254, 12312.1235) ; dec.nb = 2 ; after.lead.zero = FALSE # # for function debugging -# data = data = c("10", "100.001", "333.0001254", "12312.1235") ; dec.nb = 2 ; after.lead.zero = TRUE # # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking without fun_check() -if( ! (all(typeof(data) == "character") | all(typeof(data) == "double") | all(typeof(data) == "integer"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A VECTOR OF NUMBERS (IN NUMERIC OR CHARACTER MODE)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking without fun_check() -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data, class = "vector", na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dec.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = after.lead.zero, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -tempo <- grepl(x = data, pattern = "\\.") # detection of decimal numbers -ini.mode <- base::mode(data) -data <- as.character(data) # to really truncate decimal digits -for(i in 1:length(data)){ # scan all the numbers of the vector -if(tempo[i] == TRUE){ # means decimal number -if(after.lead.zero == TRUE){ -zero.pos <- unlist(gregexpr(text=data[i], pattern = 0)) # recover all the position of the zeros in the number. -1 if no zeros (do not record the leading and trailing zeros) -}else{ -zero.pos <- -1 # -1 as if no zero -} -dot.pos <- unlist(gregexpr(text=data[i], pattern = "\\.")) # recover all the position of the zeros in the number -digit.pos <- unlist(gregexpr(text=data[i], pattern = "[[:digit:]]")) # recover all the position of the digits in the number -dec.pos <- digit.pos[digit.pos > dot.pos] -count <- 0 -while((dot.pos + count + 1) %in% zero.pos & (dot.pos + count + 1) <= max(dec.pos) & (count + dec.nb) < length(dec.pos)){ # count the number of leading zeros in the decimal part -count <- count + 1 -} -data[i] <- formatC(as.numeric(data[i]), digits = (count + dec.nb), format = "f") -} -} -if(ini.mode != "character"){ -data <- as.numeric(data) -} -return(data) -} - - -######## fun_mat_rotate() #### 90° clockwise matrix rotation - - -fun_mat_rotate <- function(data){ -# AIM -# 90° clockwise matrix rotation -# applied twice, the function provide the mirror matrix, according to vertical and horizontal symmetry -# ARGUMENTS -# data: matrix (matrix class) -# RETURN -# the modified matrix -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# obs <- matrix(1:10, ncol = 1) ; obs ; fun_mat_rotate(obs) -# obs <- matrix(LETTERS[1:10], ncol = 5) ; obs ; fun_mat_rotate(obs) -# DEBUGGING -# data = matrix(1:10, ncol = 1) -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data, class = "matrix", fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -for (i in 1:ncol(data)){data[,i] <- rev(data[,i])} -data <- t(data) -return(data) -} - - -######## fun_mat_num2color() #### convert a numeric matrix into hexadecimal color matrix - - -fun_mat_num2color <- function( -mat1, -mat.hsv.h = TRUE, -notch = 1, -s = 1, -v = 1, -forced.color = NULL -){ -# AIM -# convert a matrix made of numbers into a hexadecimal matrix for rgb colorization -# ARGUMENTS: -# mat1: matrix 1 of non negative numerical values that has to be colored (matrix class). NA allowed -# mat.hsv.h: logical. Is mat1 the h of hsv colors ? (if TRUE, mat1 must be between zero and 1). If FALSE, mat1 must be made of positive integer values without 0 -# notch: single value between 0 and 1 to shift the successive colors on the hsv circle by + notch -# s: s argument of hsv(). Must be between 0 and 1 -# v: v argument of hsv(). Must be between 0 and 1 -# forced.color: Must be NULL or hexadecimal color code or name given by colors(). The first minimal values of mat1 will be these colors. All the color of mat1 can be forced using this argument -# RETURN -# a list containing: -# $mat1.name: name of mat1 -# $colored.mat: colors of mat1 in hexa -# $problem: logical. Is any colors of forced.color overlap the colors designed by the function. NULL if forced.color = NULL -# $text.problem: text when overlapping colors. NULL if forced.color = NULL or problem == FALSE -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# mat1 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; dimnames(mat1) <- list(LETTERS[1:4], letters[1:2]) ; fun_mat_num2color(mat1, mat.hsv.h = FALSE, notch = 1, s = 1, v = 1, forced.color = NULL) -# DEBUGGING -# mat1 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; dimnames(mat1) <- list(LETTERS[1:4], letters[1:2]); mat.hsv.h = FALSE ; notch = 1 ; s = 1 ; v = 1 ; forced.color = c(hsv(1,1,1), hsv(0,0,0)) # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = mat1, mode = "numeric", class = "matrix", na.contain = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = mat.hsv.h, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = notch, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = s, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = v, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# argument checking without fun_check() -if(mat.hsv.h == TRUE & fun_check(data = mat1, mode = "numeric", prop = TRUE)$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 ARGUMENT MUST BE A MATRIX OF PROPORTIONS SINCE THE mat.hsv.h ARGUMENT IS SET TO TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(forced.color)){ -tempo <- fun_check(data = forced.color, class = "character") -if(any(tempo$problem == TRUE)){ -paste0("\n\n================\n\n", paste(tempo$text[tempo$problem], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! all(forced.color %in% colors() | grepl(pattern = "^#", forced.color))){ # check that all strings of forced.color start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": forced.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end argument checking without fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -problem <- NULL -text.problem <- NULL -mat1.name <- deparse(substitute(mat1)) -# change the scale of the plotted matrix -if(mat.hsv.h == TRUE){ -if(any(min(mat1, na.rm = TRUE) < 0 | max(mat1, na.rm = TRUE) > 1, na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 MUST BE MADE OF VALUES BETWEEN 0 AND 1 BECAUSE mat.hsv.h ARGUMENT SET TO TRUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -if(any(mat1 - floor(mat1) > 0, na.rm = TRUE) | any(mat1 == 0L, na.rm = TRUE)){ # no need of isTRUE(all.equal()) because we do not require approx here but strictly 0, thus == is ok -tempo.cat <- paste0("ERROR IN ", function.name, ": mat1 MUST BE MADE OF INTEGER VALUES WITHOUT 0 BECAUSE mat.hsv.h ARGUMENT SET TO FALSE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -mat1 <- mat1 / max(mat1, na.rm = TRUE) -} -} -if(notch != 1){ -different.color <- unique(as.vector(mat1)) -different.color <- different.color[ ! is.na(different.color)] -tempo.different.color <- different.color + c(0, cumsum(rep(notch, length(different.color) - 1))) -tempo.different.color <- tempo.different.color - floor(tempo.different.color) -if(any(duplicated(tempo.different.color) == TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": DUPLICATED VALUES AFTER USING notch (", paste(tempo.different.color[duplicated(tempo.different.color)], collapse = " "), "). TRY ANOTHER notch VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(length(different.color) != length(tempo.different.color)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": LENGTH OF different.color (", paste(different.color, collapse = " "), ") DIFFERENT FROM LENGTH OF tempo.different.color (", paste(tempo.different.color, collapse = " "), ")") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -for(i in 1:length(different.color)){ -mat1[mat1 == different.color[i]] <- tempo.different.color[i] # no need of isTRUE(all.equal()) because different.color comes from mat1 -} -} -} -if( ! is.null(forced.color)){ -hexa.values.to.change <- hsv(unique(sort(mat1))[1:length(forced.color)], s, v) -} -mat1[ ! is.na(mat1)] <- hsv(mat1[ ! is.na(mat1)], s, v) -if( ! is.null(forced.color)){ -if(any(forced.color %in% mat1, na.rm = TRUE)){ -problem <- TRUE -text.problem <- paste0("THE FOLLOWING COLORS WHERE INTRODUCED USING forced.color BUT WHERE ALREADY PRESENT IN THE COLORED MATRIX :", paste(forced.color[forced.color %in% mat1], collapse = " ")) -}else{ -problem <- FALSE -} -for(i in 1:length(hexa.values.to.change)){ -if( ! any(mat1 == hexa.values.to.change[i], na.rm = TRUE)){# no need of isTRUE(all.equal()) because character -tempo.cat <- paste0("ERROR IN ", function.name, ": THE ", hexa.values.to.change[i], " VALUE FROM hexa.values.to.change IS NOT REPRESENTED IN mat1 : ", paste(unique(as.vector(mat1)), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -mat1[which(mat1 == hexa.values.to.change[i])] <- forced.color[i] # no need of isTRUE(all.equal()) because character -} -} -} -output <- list(mat1.name = mat1.name, colored.mat = mat1, problem = problem, text.problem = text.problem) -return(output) -} - - -######## fun_mat_op() #### assemble several matrices with operation - - -fun_mat_op <- function(mat.list, kind.of.operation = "+"){ -# AIM -# assemble several matrices of same dimensions by performing by case operation. For instance add the value of all the case 1 (row1 & column1) of the matrices and put it in the case 1 of a new matrix M, add the value of all the case 2 (row2 & column1) of the matrices and put it in the case 2 of a new matrix M, etc. - -# c: case -# i: row number -# j: column number -# k: matrix number -# z: number of matrices -# ARGUMENTS: -# mat.list: list of matrices -# kind.of.operation: either "+" (by case addition), "-" (by case subtraction) or "*" (by case multiplication) -# RETURN -# the assembled matrix, with row and/or column names only if all the matrices have identical row/column names -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_comp_2d() -# EXAMPLES -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "+") -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "*") -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], c(NA, NA))) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2), kind.of.operation = "-") -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(c("A1", "A2", "A3", "A4"), letters[1:2])) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat3 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; fun_mat_op(mat.list = list(mat1, mat2, mat3), kind.of.operation = "+") -# DEBUGGING -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2) ; mat.list = list(mat1, mat2) ; kind.of.operation = "+" # for function debugging -# mat1 = matrix(c(1,1,1,2,1,5,9,8), ncol = 2, dimnames = list(LETTERS[1:4], c(NA, NA))) ; mat2 = matrix(c(1,1,1,2,1,5,9,NA), ncol = 2, dimnames = list(LETTERS[1:4], letters[1:2])) ; mat.list = list(mat1, mat2) ; kind.of.operation = "*" # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_comp_2d() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = mat.list, class = "list", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = kind.of.operation, options = c("+", "-", "*"), length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# argument checking without fun_check() -if(length(mat.list) < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat.list ARGUMENT MUST BE A LIST CONTAINING AT LEAST 2 MATRICES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -for(i1 in 1:length(mat.list)){ -tempo <- fun_check(data = mat.list[[i1]], class = "matrix", mode = "numeric", na.contain = TRUE) -if(tempo$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ELEMENT ", i1, " OF mat.list ARGUMENT MUST BE A NUMERIC MATRIX") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -ident.row.names <- TRUE -ident.col.names <- TRUE -for(i1 in 2:length(mat.list)){ -tempo <- fun_comp_2d(data1 = mat.list[[1]], data2 = mat.list[[i1]]) -if(tempo$same.dim == FALSE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX ", i1, " OF mat.list ARGUMENT MUST HAVE THE SAME DIMENSION (", paste(dim(mat.list[[i1]]), collapse = " "), ") THAN THE MATRIX 1 IN mat.list (", paste(dim(mat.list[[1]]), collapse = " "), ")") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(tempo$same.row.name)){ -if(tempo$same.row.name != TRUE){ # != TRUE to deal with NA -ident.row.names <- FALSE -} -} -if( ! is.null(tempo$same.col.name)){ -if(tempo$same.col.name != TRUE){ # != TRUE to deal with NA -ident.col.names <- FALSE -} -} -} -# end argument checking without fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -output <- mat.list[[1]] -for(i1 in 2:length(mat.list)){ -output <- get(kind.of.operation)(output, mat.list[[i1]]) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -} -dimnames(output) <- NULL -if(ident.row.names == TRUE){ -rownames(output) <- rownames(mat.list[[1]]) -} -if(ident.col.names == TRUE){ -colnames(output) <- colnames(mat.list[[1]]) -} -return(output) -} - - -######## fun_mat_inv() #### return the inverse of a square matrix - - -fun_mat_inv <- function(mat){ -# AIM -# return the inverse of a square matrix when solve() cannot -# ARGUMENTS: -# mat: a square numeric matrix without NULL, NA, Inf or single case (dimension 1, 1) of 0 -# RETURN -# the inversed matrix -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# mat1 = matrix(c(1,1,1,2,1,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) # use solve() -# mat1 = matrix(c(0,0,0,0,0,0,0,0,0), ncol = 3) ; fun_mat_inv(mat = mat1) # use the trick -# mat1 = matrix(c(1,1,1,2,Inf,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) -# mat1 = matrix(c(1,1,1,2,NA,5,9,8,9), ncol = 3) ; fun_mat_inv(mat = mat1) -# mat1 = matrix(c(1,2), ncol = 1) ; fun_mat_inv(mat = mat1) -# mat1 = matrix(0, ncol = 1) ; fun_mat_inv(mat = mat1) -# mat1 = matrix(2, ncol = 1) ; fun_mat_inv(mat = mat1) -# DEBUGGING -# mat = matrix(c(1,1,1,2,1,5,9,8,9), ncol = 3) # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = mat, class = "matrix", mode = "numeric", fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# argument checking without fun_check() -if(ncol(mat) != nrow(mat)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A SQUARE MATRIX") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(mat %in% c(Inf, -Inf, NA))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A MATRIX WITHOUT Inf, -Inf OR NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(all(mat == 0L) & ncol(mat) == 1L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT CANNOT BE A SQUARE MATRIX MADE OF A SINGLE CASE OF 0") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking without fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if(any(grepl(x = try(solve(mat), silent = TRUE)[], pattern = "[Ee]rror"))){ -tempo <- svd(mat) -val.critique <- which(tempo$d < 10^-8) -Diag.mod <- diag(1 / tempo$d) -for(i in val.critique){ -Diag.mod[i, i] <- 0 -} -return(tempo$v %*% Diag.mod %*% t(tempo$u)) -}else{ -return(solve(mat)) -} -} - - -######## fun_mat_fill() #### fill the empty half part of a symmetric square matrix - - -fun_mat_fill <- function(mat, empty.cell.string = 0, warn.print = FALSE){ -# AIM -# detect the empty half part of a symmetric square matrix (either topleft, topright, bottomleft or bottomright) -# fill this empty half part using the other symmetric half part of the matrix -# WARNINGS -# a plot verification using fun_gg_heatmap() is recommanded -# ARGUMENTS: -# mat: a numeric or character square matrix with the half part (according to the grand diagonal) filled with NA (any kind of matrix), "0" (character matrix) or 0 (numeric matrix) exclusively (not a mix of 0 and NA in the empty part) -# empty.cell.string: a numeric, character or NA (no quotes) indicating what empty cells are filled with -# warn.print: logical. Print warnings at the end of the execution? No print if no warning messages -# RETURN -# a list containing: -# $mat: the filled matrix -# $warn: the warning messages. Use cat() for proper display. NULL if no warning -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# mat1 = matrix(c(1,NA,NA,NA, 0,2,NA,NA, NA,3,4,NA, 5,6,7,8), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = NA, warn.print = TRUE) # bottomleft example -# mat1 = matrix(c(1,1,1,2, 0,2,3,0, NA,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = NA, warn.print = TRUE) # error example -# mat1 = matrix(c(1,1,1,2, 0,2,3,0, NA,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # bottomright example -# mat1 = matrix(c(1,1,1,2, "a",2,3,NA, "a","a",0,0, "a","a","a",0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = "a", warn.print = TRUE) # topright example -# mat1 = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # topleft example -# mat1 = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,0, 5,0,0,0), ncol = 4) ; mat1 ; fun_mat_fill(mat = mat1, empty.cell.string = 0, warn.print = TRUE) # error example -# DEBUGGING -# mat = matrix(c(1,NA,NA,NA, 0,2,NA,NA, NA,3,4,NA, 5,6,7,8), ncol = 4) ; empty.cell.string = NA ; warn.print = TRUE # for function debugging -# mat = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; empty.cell.string = 0 ; warn.print = TRUE # for function debugging # topleft example -# mat = matrix(c(0,0,0,2, 0,0,3,0, 0,3,0,NA, 5,0,0,0), ncol = 4) ; empty.cell.string = NA ; warn.print = TRUE # for function debugging # topleft example -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = mat, class = "matrix", na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = empty.cell.string, class = "vector", na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# argument checking without fun_check() -if(ncol(mat) != nrow(mat)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A SQUARE MATRIX") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (base::mode(mat) %in% c("numeric", "character"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MUST BE A NUMERIC OR CHARACTER MATRIX") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(nrow(mat) == 1L & ncol(mat) == 1L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT CANNOT BE A SQUARE MATRIX MADE OF A SINGLE CASE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(ifelse(is.na(empty.cell.string), ! any(is.na(mat)), ! any(mat == empty.cell.string, na.rm = TRUE))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": mat ARGUMENT MATRIX MUST HAVE CELLS WITH THE EMPTY STRING SPECIFIED IN empty.cell.string ARGUMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking without fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -list.diag <- vector("list", length = nrow(mat) - 1) -for(i1 in 1:(nrow(mat) - 1)){ -list.diag[[i1]] <- numeric(length = nrow(mat) - i1) # list made of zero -} -sector <- c("topleft", "topright", "bottomright", "bottomleft") -diag.scan <-c( # same order as sector. Recover each diag from center to corner -"mat[as.matrix(as.data.frame(list(1:(nrow(mat) - i2), (ncol(mat) -i2):1), stringsAsFactors = TRUE))]", # topleft part -"mat[as.matrix(as.data.frame(list(1:(nrow(mat) - i2), (1:ncol(mat))[-(1:i2)]), stringsAsFactors = TRUE))]", # topright part -"mat[as.matrix(as.data.frame(list((1 + i2):nrow(mat), ncol(mat):(1 + i2)), stringsAsFactors = TRUE))]", # bottomright part -"mat[as.matrix(as.data.frame(list((1 + i2):nrow(mat), 1:(ncol(mat) -i2)), stringsAsFactors = TRUE))]" # bottomleft part -) -# empty part detection -empty.sector <- NULL -full.sector <- NULL -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -for(i1 in 1:length(sector)){ -tempo.list.diag <- list.diag -for(i2 in 1:(nrow(mat) - 1)){ -tempo.list.diag[[i2]] <- eval(parse(text = diag.scan[i1])) -if(ifelse(is.na(empty.cell.string), ! all(is.na(tempo.list.diag[[i2]])), ! (all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = TRUE) & ! (is.na(all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE)))))){ # I had to add this ! (is.na(all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE))) because all(tempo.list.diag[[i2]] == empty.cell.string, na.rm = FALSE) gives NA and not FALSE if one NA in tempo.list.diag[[i2]] -> not good for if() -full.sector <- c(full.sector, sector[i1]) -break -} -} -if(i2 == nrow(mat) - 1){ -if(all(unlist(lapply(tempo.list.diag, FUN = function(x){if(is.na(empty.cell.string)){is.na(x)}else{x == empty.cell.string}})), na.rm = TRUE)){ -empty.sector <- c(empty.sector, sector[i1]) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") EMPTY SECTOR DETECTED ON THE ", toupper(sector[i1]), " CORNER, FULL OF ", empty.cell.string) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE ", toupper(sector[i1]), " SECTOR, DETECTED AS EMPTY, IS NOT? DIFFERENT VALUES IN THIS SECTOR:\n", paste(names(table(unlist(tempo.list.diag), useNA = "ifany")), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -} -# end empty part detection -if(length(empty.sector) == 0L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") ACCORDING TO empty.cell.string ARGUMENT (", empty.cell.string, "), mat ARGUMENT MATRIX HAS ZERO EMPTY HALF PART") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -if(length(empty.sector) > 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ACCORDING TO empty.cell.string ARGUMENT (", empty.cell.string, "), mat ARGUMENT MATRIX HAS MORE THAN ONE EMPTY HALF PART (ACCORDING TO THE GRAND DIAGONAL): ", paste(empty.sector, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(any(full.sector %in% empty.sector, na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE FUNCTION HAS DETECTED EMPTY AND NON EMPTY HALF PART IN THE SAME SECTOR: ", paste(full.sector[full.sector %in% empty.sector], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(length(empty.sector) + length(full.sector)!= 4){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE FUNCTION HAS DETECTED MORE OR LESS SECTORS THAN 4:\nHALF SECTORS:", paste(empty.sector, collapse = " "), "\nFULL SECTORS:", paste(full.sector, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") ", toupper(empty.sector), " SECTOR HAS BEEN COMPLETED TO BECOME SYMMETRICAL") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# matrix filling -for(i2 in 1:(nrow(mat) - 1)){ -if(empty.sector == "topleft"){ -eval(parse(text = paste0(diag.scan[1], " <- ", diag.scan[3]))) -}else if(empty.sector == "topright"){ -eval(parse(text = paste0(diag.scan[2], " <- ", diag.scan[4]))) -}else if(empty.sector == "bottomright"){ -eval(parse(text = paste0(diag.scan[3], " <- ", diag.scan[1]))) -}else if(empty.sector == "bottomleft"){ -eval(parse(text = paste0(diag.scan[4], " <- ", diag.scan[2]))) -} -} -# end matrix filling -} -if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -return(list(mat = mat, warn = warn)) -} - - -######## fun_permut() #### progressively breaks a vector order - - -fun_permut <- function( -data1, -data2 = NULL, -n = NULL, -seed = NULL, -print.count = 10, -text.print = "", -cor.method = "spearman", -cor.limit = 0.2, -warn.print = FALSE, -lib.path = NULL -){ -# AIM -# reorder the elements of the data1 vector by flipping 2 randomly selected consecutive positions either: -# 1) n times (when n is precised) or -# 2) until the correlation between data1 and data2 decreases down to the cor.limit (0.2 by default). See cor.limit below to deal with negative correlations -# Example of consecutive position flipping: ABCD -> BACD -> BADC, etc. -# designed for discrete values, but worls also for continuous values -# WARNINGS -# see # https://www.r-bloggers.com/strategies-to-speedup-r-code/ for code speedup -# the random switch of non consecutive positions (ABCD -> DBCA for instance) does not work very well as the correlation is quickly obtained but the initial vector structure is mainly kept (no much order). Ths code would be: pos <- ini.pos[1:2] ; pos <- sample.int(n = n , size = 2, replace = FALSE) ; tempo.pos[pos] <- tempo.pos[rev(pos)] -# ARGUMENTS -# data1: a vector of at least 2 elements. Must be numeric if data2 is specified -# data2: a numeric vector of same length as data1 -# n: number of times "flipping 2 randomly selected consecutive positions". Ignored if data2 is specified -# seed: integer number used by set.seed(). Write NULL if random result is required, an integer otherwise. BEWARE: if not NULL, fun_permut() will systematically return the same result when the other parameters keep the same settings -# print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired -# text.print: optional message to add to the working progress message every print.count loop -# cor.method: correlation method. Either "pearson", "kendall" or "spearman". Ignored if data2 is not specified -# cor.limit: a correlation limit (between 0 and 1). Ignored if data2 is not specified. Compute the correlation between data1 and data2, permute the data1 values, and stop the permutation process when the correlation between data1 and data2 decreases down below the cor limit value (0.2 by default). If cor(data1, data2) is negative, then -cor.limit is used and the process stops until the correlation between data1 and data2 increases up over cor.limit (-0.2 by default). BEWARE: write a positive cor.limit even if cor(data1, data2) is known to be negative. The function will automatically uses -cor.limit. If the initial correlation is already below cor.limit (positive correlation) or over -cor.limit (negative correlation), then the data1 value positions are completely randomized (correlation between data1 and data2 is expected to be 0) -# warn.print: logical. Print warnings at the end of the execution? No print if no warning messages -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# RETURN -# a list containing: -# $data: the modified vector -# $warn: potential warning messages (in case of negative correlation when data2 is specified). NULL if non warning message -# $cor: a spearman correlation between the initial positions (1:length(data1) and the final positions if data2 is not specified and the final correlation between data1 and data2 otherwise, according to cor.method -# $count: the number of loops used -# REQUIRED PACKAGES -# lubridate -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# fun_round() -# EXAMPLES -# example (1) showing that for loop, used in fun_permut(), is faster than while loop -# ini.time <- as.numeric(Sys.time()) ; count <- 0 ; for(i0 in 1:1e9){count <- count + 1} ; tempo.time <- as.numeric(Sys.time()) ; tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) ; tempo.lapse -# example (2) showing that for loop, used in fun_permut(), is faster than while loop -# ini.time <- as.numeric(Sys.time()) ; count <- 0 ; while(count < 1e9){count <- count + 1} ; tempo.time <- as.numeric(Sys.time()) ; tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) ; tempo.lapse -# fun_permut(data1 = LETTERS[1:5], data2 = NULL, n = 100, seed = 1, print.count = 10, text.print = "CPU NB 4") -# fun_permut(data1 = 101:110, data2 = 21:30, seed = 1, print.count = 1e4, text.print = "", cor.method = "spearman", cor.limit = 0.2) -# a way to use the cor.limit argument just considering data1 -# obs1 <- 101:110 ; fun_permut(data1 = obs1, data2 = obs1, seed = 1, print.count = 10, cor.method = "spearman", cor.limit = 0.2) -# fun_permut(data1 = 1:1e3, data2 = 1e3:1, seed = 1, print.count = 1e6, text.print = "", cor.method = "spearman", cor.limit = 0.7) -# fun_permut(data1 = 1:1e2, data2 = 1e2:1, seed = 1, print.count = 1e3, cor.limit = 0.5) -# fun_permut(data1 = c(0,0,0,0,0), n = 5, data2 = NULL, seed = 1, print.count = 1e3, cor.limit = 0.5) -# DEBUGGING -# data1 = LETTERS[1:5] ; data2 = NULL ; n = 1e6 ; seed = NULL ; print.count = 1e3 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL -# data1 = LETTERS[1:5] ; data2 = NULL ; n = 10 ; seed = 22 ; print.count = 10 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL -# data1 = 101:110 ; data2 = 21:30 ; n = 10 ; seed = 22 ; print.count = 10 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.2 ; warn.print = TRUE ; lib.path = NULL -# data1 = 1:1e3 ; data2 = 1e3:1 ; n = 20 ; seed = 22 ; print.count = 1e6 ; text.print = "" ; cor.method = "spearman" ; cor.limit = 0.5 ; warn.print = TRUE ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_round", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data1, class = "vector", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & length(data1) < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A VECTOR OF MINIMUM LENGTH 2. HERE IT IS: ", length(data1)) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(data2)){ -tempo <- fun_check(data = data1, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) -if(tempo$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 MUST BE A NUMERIC VECTOR IF data2 ARGUMENT IS SPECIFIED") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = data2, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) -if(length(data1) != length(data2)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 AND data2 MUST BE VECTOR OF SAME LENGTH. HERE IT IS ", length(data1)," AND ", length(data2)) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else if(is.null(n)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT CANNOT BE NULL IF data2 ARGUMENT IS NULL") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(n)){ -tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(seed)){ -tempo <- fun_check(data = seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = text.print, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = cor.method, options = c("pearson", "kendall", "spearman"), length =1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = cor.limit, class = "vector", mode = "numeric", prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# package checking -fun_pack(req.package = "lubridate", lib.path = lib.path) -# end package checking -# main code -# code that protects set.seed() in the global environment -# see also Protocol 100-rev0 Parallelization in R.docx -if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment -tempo.random.seed <- .Random.seed -on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) -}else{ -on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness -} -set.seed(seed) -# end code that protects set.seed() in the global environment -ini.date <- Sys.time() # time of process begin, converted into seconds -ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds -ini.pos <- 1:length(data1) # positions of data1 before permutation loops -tempo.pos <- ini.pos # positions of data1 that will be modified during loops -# pos.selec.seq <- ini.pos[-length(data1)] # selection of 1 position in initial position, without the last because always up permutation (pos -> pos+1 & pos+1 -> pos) -pos.selec.seq.max <- length(ini.pos) - 1 # max position (used by sample.int() function). See below for - 1 -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -count <- 0 -round <- 0 -BREAK <- FALSE -tempo.cor <- 0 -if(is.null(data2)){ -if(length(table(data1)) == 1L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data1 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data1))) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # -}else{ -if(print.count > n){ -print.count <- n -} -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP OF ", n, " LOOPS INITIATED | LOOP COUNT: ", format(count, big.mark=","))) -print.count.loop <- logical(length = print.count) -print.count.loop[length(print.count.loop)] <- TRUE # not this to avoid long vector, but not forget to reset during printing: print.count.loop[(1:trunc(n / print.count) * print.count)] <- TRUE # counter to speedup -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.date.loop <- Sys.time() -tempo.time.loop <- as.numeric(tempo.date.loop) -for(i3 in 1:n){ -count.loop <- count.loop + 1 -pos2 <- pos[count.loop] # selection of 1 position -tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -if(print.count.loop[count.loop]){ -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) -final.loop <- (tempo.time - tempo.time.loop) / i3 * n # expected duration in seconds -final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP ", i3, " / ", n, " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) -} -} -count <- count + n # out of the loop to speedup -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP ENDED | LOOP COUNT: ", format(count, big.mark=","))) -cat("\n\n") -} -}else{ -if(length(table(data1)) == 1L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data1 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data1))) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # -tempo.cor <- 1 -}else if(length(table(data2)) == 1L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO PERMUTATION PERFORMED BECAUSE data2 ARGUMENT SEEMS TO BE MADE OF IDENTICAL ELEMENTS: ", names(table(data2))) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # -tempo.cor <- 1 -}else{ -cor.ini <- cor(x = data1, y = data2, use = "pairwise.complete.obs", method = cor.method) -tempo.cor <- cor.ini # correlation that will be modified during loops -neg.cor <- FALSE -if(tempo.cor < 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") INITIAL ", toupper(cor.method), " CORRELATION BETWEEN data1 AND data2 HAS BEEN DETECTED AS NEGATIVE: ", tempo.cor, ". THE LOOP STEPS WILL BE PERFORMED USING POSITIVE CORRELATIONS BUT THE FINAL CORRELATION WILL BE NEGATIVE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # -neg.cor <- TRUE -tempo.cor <- abs(tempo.cor) -cor.ini <- abs(cor.ini) -} -if(tempo.cor < cor.limit){ # randomize directly all the position to be close to correlation zero -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") INITIAL ABSOLUTE VALUE OF THE ", toupper(cor.method), " CORRELATION ", fun_round(tempo.cor), " BETWEEN data1 AND data2 HAS BEEN DETECTED AS BELOW THE CORRELATION LIMIT PARAMETER ", cor.limit, "\nTHE data1 SEQUENCE HAS BEEN COMPLETELY RANDOMIZED TO CORRESPOND TO CORRELATION ZERO") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) # -for(i4 in 1:5){ # done 5 times to be sure of the complete randomness -tempo.pos <- sample(x = tempo.pos, size = length(tempo.pos), replace = FALSE) -} -count <- count + 5 # out of the loop to speedup -}else{ -# smallest correlation decrease -count <- count + 1 # 1 and not 0 because already 1 performed just below -pos <- sample.int(n = pos.selec.seq.max , size = 1, replace = TRUE) # selection of 1 position # pos.selec.seq.max because selection of 1 position in initial position, without the last because always up permutation (pos -> pos+1 & pos+1 -> pos) -tempo.pos[c(pos + 1, pos)] <- tempo.pos[c(pos, pos + 1)] -tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) -smallest.cor.dec <- cor.ini - tempo.cor -# end smallest correlation decrease -# going out of tempo.cor == cor.ini -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "CORRELATION DECREASE AFTER A SINGLE PERMUTATION: ", fun_round(smallest.cor.dec, 4))) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP -> GOING OUT FROM EQUALITY | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) -print.count.loop <- logical(length = print.count) -print.count.loop[length(print.count.loop)] <- TRUE # counter to speedup -count.loop <- 0 # -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.date.loop <- Sys.time() -tempo.time.loop <- as.numeric(tempo.date.loop) -while(tempo.cor == cor.ini){ # to be out of equality between tempo.cor and cor.ini at the beginning (only valid for very long vector) -count <- count + 1 -count.loop <- count.loop + 1 -pos2 <- pos[count.loop] -tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) -if(print.count.loop[count.loop]){ -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP", format(count.loop, big.mark=","), " / ? | COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TIME SPENT: ", tempo.lapse)) -} -} -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FIRST WHILE LOOP STEP END | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TOTAL SPENT TIME: ", tempo.lapse)) -if(tempo.cor < cor.limit){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE FIRST FOR & WHILE LOOP STEPS HAVE BEEN TOO FAR AND SUBSEQUENT LOOP STEPS WILL NOT RUN") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end going out of tempo.cor == cor.ini -# estimation of the average correlation decrease per loop on x loops and for loop execution -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE/FOR LOOPS INITIATION | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) -count.est <- 1e5 -first.round <- TRUE -GOBACK <- FALSE -while(tempo.cor > cor.limit){ -round <- round + 1 -# estimation step -if(first.round == TRUE){ -first.round <- FALSE -cor.dec.per.loop <- numeric(length = 5) -loop.nb.est <- Inf -cor.est.ini <- tempo.cor -cor.est <- numeric(length = 5) -for(i6 in 1:5){ # connected to cor.dec.per.loop -tempo.pos.est <- tempo.pos -pos <- sample.int(n = pos.selec.seq.max , size = count.est, replace = TRUE) # selection of n position -for(i7 in 1:count.est){ -pos2 <- pos[i7] # selection of 1 position -tempo.pos.est[c(pos2 + 1, pos2)] <- tempo.pos.est[c(pos2, pos2 + 1)] -} -tempo.cor.est <- abs(cor(x = data1[tempo.pos.est], y = data2, use = "pairwise.complete.obs", method = cor.method)) -cor.est[i6] <- tempo.cor.est -tempo.cor.dec.per.loop <- (cor.est.ini - tempo.cor.est) / count.est # correlation decrease per loop -if(is.na(tempo.cor.dec.per.loop) | ! is.finite(tempo.cor.dec.per.loop)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2\ncor.est.ini: ", cor.est.ini, "\ntempo.cor.est: ", tempo.cor.est) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -cor.dec.per.loop[i6] <- tempo.cor.dec.per.loop -} -cor.est <- cor.est[which.max(cor.dec.per.loop)] # max to avoid to go to far with for loop (tempo.cor below tempo.limit) -cor.dec.per.loop <- max(cor.dec.per.loop, na.rm = TRUE) # max to avoid to go to far with for loop (tempo.cor below tempo.limit) -loop.nb.est <- round((tempo.cor - cor.limit) / cor.dec.per.loop) -}else{ -if(GOBACK == TRUE){ -loop.nb.est <- round(loop.nb.est / 2) -}else{ -cor.dec.per.loop <- (cor.ini - tempo.cor) / count -loop.nb.est <- round((tempo.cor - cor.limit) / cor.dec.per.loop) -} -} -# end estimation step -# loop step -if(is.na(loop.nb.est) | ! is.finite(loop.nb.est)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1\nloop.nb.est: ", loop.nb.est, "\ncor.ini: ", cor.ini, "\ntempo.cor: ", tempo.cor, "\ncor.limit: ", cor.limit, "\ncor.dec.per.loop: ", cor.dec.per.loop) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(loop.nb.est > 1e4){ # below -> leave the while loop -tempo.pos.secu <- tempo.pos -count.secu <- count -tempo.cor.secu <- tempo.cor -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "INITIAL SETTINGS BEFORE ROUND: ", round, " | LOOP COUNT: ", format(count, big.mark=","), " | GO BACK: ", GOBACK, " | LOOP NUMBER ESTIMATION: ", format(loop.nb.est, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) -print.count.loop <- logical(length = print.count) -print.count.loop[length(print.count.loop)] <- TRUE # not this to avoid long vector, but not forget to reset during printing: print.count.loop[(1:trunc(n / print.count) * print.count)] <- TRUE # counter to speedup -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.date.loop <- Sys.time() -tempo.time.loop <- as.numeric(tempo.date.loop) -for(i6 in 1:loop.nb.est){ -count.loop <- count.loop + 1 -pos2 <- pos[count.loop] # selection of 1 position -tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -if(print.count.loop[count.loop]){ -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) -final.loop <- (tempo.time - tempo.time.loop) / i6 * loop.nb.est # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining -final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FOR LOOP | ROUND ", round, " | LOOP: ", format(i6, big.mark=","), " / ", format(loop.nb.est, big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) -} -} -count <- count + loop.nb.est # out of the loop to speedup -tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) -if(tempo.cor > tempo.cor.secu | ((tempo.cor - cor.limit) < 0 & abs(tempo.cor - cor.limit) > smallest.cor.dec * round(log10(max(ini.pos, na.rm = TRUE))))){ -GOBACK <- TRUE -tempo.pos <- tempo.pos.secu -count <- count.secu -tempo.cor <- tempo.cor.secu -}else{ -GOBACK <- FALSE -} -}else{ -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "FINAL WHILE LOOP | LOOP COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4))) -print.count.loop <- logical(length = print.count) -print.count.loop[length(print.count.loop)] <- TRUE # counter to speedup -count.loop <- 0 # -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # selection of random positions. BEWARE: n = pos.selec.seq.max because already - 1 (see above) but is connected to tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.cor.loop <- tempo.cor -tempo.date.loop <- Sys.time() -tempo.time.loop <- as.numeric(tempo.date.loop) -while(tempo.cor > cor.limit){ -count <- count + 1 -count.loop <- count.loop + 1 -pos2 <- pos[count.loop] -tempo.pos[c(pos2 + 1, pos2)] <- tempo.pos[c(pos2, pos2 + 1)] -tempo.cor <- abs(cor(x = data1[tempo.pos], y = data2, use = "pairwise.complete.obs", method = cor.method)) -if(print.count.loop[count.loop]){ -count.loop <- 0 -pos <- sample.int(n = pos.selec.seq.max , size = print.count, replace = TRUE) # BEWARE: never forget to resample here -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - tempo.time.loop)) -final.loop <- (tempo.time - tempo.time.loop) / (tempo.cor.loop - tempo.cor) * (tempo.cor - cor.limit) # expected duration in seconds # tempo.cor.loop - tempo.cor always positive and tempo.cor decreases progressively starting from tempo.cor.loop -final.exp <- as.POSIXct(final.loop, origin = tempo.date.loop) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE LOOP | LOOP NB: ", format(count.loop, big.mark=","), " | COUNT: ", format(count, big.mark=","), " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) -} -} -} -} -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -cat(paste0("\n", ifelse(text.print == "", "", paste0(text.print, " | ")), "WHILE/FOR LOOPS END | LOOP COUNT: ", format(count, big.mark=","), " | NB OF ROUNDS: ", round, " | CORRELATION LIMIT: ", fun_round(cor.limit, 4), " | ABS TEMPO CORRELATION: ", fun_round(tempo.cor, 4), " | TOTAL SPENT TIME: ", tempo.lapse)) -} -tempo.cor <- ifelse(neg.cor == TRUE, -tempo.cor, tempo.cor) -} -} -cat("\n\n") -if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE), add = TRUE) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -output <- list(data = data1[tempo.pos], warn = warn, cor = if(is.null(data2)){cor(ini.pos, tempo.pos, method = "spearman")}else{tempo.cor}, count = count) -return(output) -} - - -######## fun_slide() #### return a computation made on a vector using a sliding window - - -fun_slide <- function( -data, -window.size, -step, -from = NULL, -to = NULL, -fun, -args = NULL, -boundary = "left", -parall = FALSE, -thread.nb = NULL, -print.count = 100, -res.path = NULL, -lib.path = NULL, -verbose = TRUE, -cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" -){ -# AIM -# return a computation made on a vector using a sliding window -# WARNINGS -# The function uses two strategies, depending on the amout of memory required which depends on the data, window.size and step arguments. The first one uses lapply(), is generally fast but requires lots of memory. The second one uses a parallelized loop. The choice between the two strategies is automatic if parall argument is FALSE, and is forced toward parallelization if parall argument is TRUE -# The parall argument forces the parallelization, which is convenient when the data argument is big, because the lapply() function is sometimes slower than the parallelization -# Always use the env argument when fun_slide() is used inside functions -# ARGUMENTS -# data: vector, matrix, table or array of numeric values (mode must be numeric). Inf not allowed. NA will be removed before computation -# window.size: single numeric value indicating the width of the window sliding across data (in the same unit as data value) -# step: single numeric value indicating the step between each window (in the same unit as data value). Cannot be larger than window.size -# from: value of the left boundary of the first sliding window. If NULL, min(data) is used. The first window will strictly have from or min(data) as left boundary -# to: value of the right boundary of the last sliding window. If NULL, max(data) is used. Warning: (1) the final last window will not necessary have to|max(data) as right boundary. In fact the last window will be the one that contains to|max(data) for the first time, i.e., min[from|min(data) + window.size + n * step >= to|max(data)]; (2) In fact, the >= in min[from|min(data) + window.size + n * step >= to|max(data)] depends on the boundary argument (>= for "right" and > for "left"); (3) to have the rule (1) but for the center of the last window, use to argument as to = to|max(data) + window.size / 2 -# fun: function or character string (without brackets) indicating the name of the function to apply in each window. Example: fun = "mean", or fun = mean -# args: character string of additional arguments of fun (separated by a comma between the quotes). Example args = "na.rm = TRUE" for fun = mean. Ignored if NULL -# boundary: either "left" or "right". Indicates if the sliding window includes values equal to left boundary and exclude values equal to right boundary ("left") or the opposite ("right") -# parall: logical. Force parallelization ? -# thread.nb: numeric value indicating the number of threads to use if ever parallelization is required. If NULL, all the available threads will be used. Ignored if parall is FALSE -# print.count: interger value. Print a working progress message every print.count during loops. BEWARE: can increase substentially the time to complete the process using a small value, like 10 for instance. Use Inf is no loop message desired -# res.path: character string indicating the absolute pathway where the parallelization log file will be created if parallelization is used. If NULL, will be created in the R current directory -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# verbose: logical. Display messages? -# cute.path: character string indicating the absolute path of the cute.R file. Will be remove when cute will be a package. Ignored if parall is FALSE -# RETURN -# a data frame containing -#$left : the left boundary of each window (in the unit of the data argument) -#$right : the right boundary of each window (in the unit of data argument) -#$center : the center of each window (in the unit of data argument) -#$value : the computed value by the fun argument in each window) -# REQUIRED PACKAGES -# lubridate -# parallel if parall argument is TRUE (included in the R installation packages but not automatically loaded) -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_get_message -# fun_pack() -# EXAMPLES -# fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "left") -# fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "right") # effect of boundary argument -# fun_slide(data = c(1:10, 100:110, 500), window.size = 5, step = 2, fun = length, boundary = "left", parall = TRUE) # effect of parall argument -# DEBUGGING -# data = c(1:10, 100:110, 500) ; window.size = 5 ; step = 2 ; from = NULL ; to = NULL ; fun = length ; args = NULL ; boundary = "left" ; parall = FALSE ; thread.nb = NULL ; print.count = 100 ; res.path = NULL ; lib.path = NULL ; verbose = TRUE ; cute.path = "C:\\Users\\Gael\\Documents\\Git_projects\\cute_little_R_functions\\cute_little_R_functions.R" -# data = lag.pos; window.size = window.size; step = step; fun = length; from = min(a$pos); to = max(a$pos) -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check", -"fun_get_message", -"fun_pack" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words -# end reserved words -# arg with no default values -mandat.args <- c( -"data", -"window.size", -"step", -"fun" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data, mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = window.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = step, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(from)){ -tempo <- fun_check(data = from, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -} -if( ! is.null(to)){ -tempo <- fun_check(data = to, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -} -tempo1 <- fun_check(data = fun, class = "vector", mode = "character", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = fun, class = "function", length = 1, fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": fun ARGUMENT MUST BE A FUNCTION OR A CHARACTER STRING OF THE NAME OF A FUNCTION") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(args)){ -tempo <- fun_check(data = args, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = boundary, options = c("left", "right"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = parall, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(parall == TRUE){ -if( ! is.null(thread.nb)){ -tempo <- fun_check(data = thread.nb, typeof = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & thread.nb < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": thread.nb PARAMETER MUST EQUAL OR GREATER THAN 1: ", thread.nb) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -tempo <- fun_check(data = print.count, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if( ! is.null(res.path)){ -tempo <- fun_check(data = res.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -} -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = verbose, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = cute.path, class = "vector", typeof = "character", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# new environment -env.name <- paste0("env", as.numeric(Sys.time())) -if(exists(env.name, where = -1)){ # verify if still ok when fun_info() is inside a function -tempo.cat <- paste0("ERROR IN ", function.name, ": ENVIRONMENT env.name ALREADY EXISTS. PLEASE RERUN ONCE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -assign(env.name, new.env()) -} -# end new environment -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS", "THIS ARGUMENT"), " CANNOT JUST BE NA:", paste0(tempo.arg[tempo.log], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"data", -"window.size", -"step", -# "from", # because can be NULL -# "to", # because can be NULL -"fun", -# "args", # because can be NULL -"boundary", -"parall", -# "thread.nb", # because can be NULL -"print.count", -# "res.path", # because can be NULL -# "lib.path", # because can be NULL -"verbose", -"cute.path" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -# end warning initiation -# other checkings -if(length(data) == 0){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT CANNOT BE LENGTH 0") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(any( ! is.finite(data))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT CANNOT CONTAIN Inf VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if(step > window.size){ -tempo.cat <- paste0("ERROR IN ", function.name, ": step ARGUMENT MUST BE LOWER THAN window.size ARGUMENT\nstep: ", paste(step, collapse = " "), "\nwindow.size: ", paste(window.size, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -if( ! is.null(res.path)){ -if( ! all(dir.exists(res.path))){ # separation to avoid the problem of tempo$problem == FALSE and res.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE res.path ARGUMENT DOES NOT EXISTS:\n", paste(res.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -}else{ -res.path <- getwd() # working directory -} -if( ! is.null(lib.path)){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) -} -} -if(grepl(x = cute.path, pattern = "^http")){ -tempo.error1 <- any(grepl(x = fun_get_message(data = "source(cute.path)", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE)), pattern = "^[Ee]rror")) -tempo.error2 <- FALSE -}else{ -tempo.error1 <- FALSE -tempo.error2 <- ! file.exists(cute.path) -} -if(tempo.error1 | tempo.error2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(grepl(x = cute.path, pattern = "^http"), "URL", "FILE"), " PATH INDICATED IN THE cute.path PARAMETER DOES NOT EXISTS:\n", cute.path) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -# end other checkings -# reserved word checking -# end reserved word checking -# end second round of checking and data preparation -# package checking -fun_pack(req.package = c("lubridate"), lib.path = lib.path) -fun_pack(req.package = c("parallel"), lib.path = lib.path) -# end package checking -# main code -if(verbose == TRUE){ -cat("\nfun_slide JOB IGNITION\n") -} -ini.date <- Sys.time() -ini.time <- as.numeric(ini.date) # time of process begin, converted into seconds -fun <- match.fun(fun) # make fun <- get(fun) is fun is a function name written as character string of length 1 -if(boundary == "left"){ -left <- ">=" -right <- "<" -right.last.wind <- ">" -}else if(boundary == "right"){ -left <- ">" -right <- "<=" -right.last.wind <- ">=" -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -data <- as.vector(data) -data <- sort(data, na.last = NA) # NA removed -wind <- data.frame(left = seq(from = if(is.null(from)){min(data, na.rm = TRUE)}else{from}, to = if(is.null(to)){max(data, na.rm = TRUE)}else{to}, by = step), stringsAsFactors = TRUE) -wind <- data.frame(wind, right = wind$left + window.size, stringsAsFactors = TRUE) -wind <- data.frame(wind, center = (wind$left + wind$right) / 2, stringsAsFactors = TRUE) -if(all(wind$right < if(is.null(to)){max(data, na.rm = TRUE)}else{to})){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# The 3 next lines is for the rule of to argument with center (see to argument description) -# if(any(wind$center > max(data, na.rm = TRUE))){ -# wind <- wind[ ! wind$center > max(data, na.rm = TRUE),] -# } -if(sum(get(right.last.wind)(wind$right, if(is.null(to)){max(data, na.rm = TRUE)}else{to}), na.rm = TRUE) > 1){ # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -tempo.log <- get(right.last.wind)(wind$right, if(is.null(to)){max(data, na.rm = TRUE)}else{to}) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -tempo.log[min(which(tempo.log), na.rm = TRUE)] <- FALSE # convert the first left boundary that goes above max(data, na.rm = TRUE) to FALSE to keep it (the next ones will be removed) -wind <- wind[ ! tempo.log,] -} - -# test if lapply can be used -if(parall == FALSE){ -assign("wind", wind, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # wind assigned in a new envir for test -assign("data", data, envir = get(env.name, env = sys.nframe(), inherit = FALSE)) # data assigned in a new envir for test -tempo.message <- fun_get_message(data="lapply(X = wind$left, Y = data, FUN = function(X, Y){res <- get(left)(Y, X) ; return(res)})", kind = "error", header = FALSE, env = get(env.name, env = sys.nframe(), inherit = FALSE), print.no = FALSE) # no env = sys.nframe(), inherit = FALSE in get(left) because look for function in the classical scope -# rm(env.name) # optional, because should disappear at the end of the function execution -}else{ -tempo.message <- "ERROR" # with this, force the parallelization by default -} -# end test if lapply can be used -if( ! any(grepl(x = tempo.message, pattern = "ERROR.*"))){ -left.log <- lapply(X = wind$left, Y = data, FUN = function(X, Y){ -res <- get(left)(Y, X) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -return(res) -}) -right.log <- lapply(X = wind$right, Y = data, FUN = function(X, Y){ -res <- get(right)(Y, X) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -return(res) -}) -log <- mapply(FUN = "&", left.log, right.log, SIMPLIFY = FALSE) -output <- eval(parse(text = paste0("sapply(lapply(log, FUN = function(X){(data[X])}), FUN = fun", if( ! is.null(args)){paste0(", ", args)}, ")"))) # take the values of the data vector according to log (list of logical, each compartment of length(data)) and apply fun with args of fun -if(length(output) != nrow(wind)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -output <- data.frame(wind, value = output, stringsAsFactors = TRUE) -} -}else{ -if(verbose == TRUE){ -tempo.cat <- paste0("PARALLELIZATION INITIATED AT: ", ini.date) -cat(paste0("\n", tempo.cat, "\n")) -} -tempo.thread.nb = parallel::detectCores(all.tests = FALSE, logical = TRUE) # detect the number of threads -if( ! is.null(thread.nb)){ -if(tempo.thread.nb < thread.nb){ -thread.nb <- tempo.thread.nb -if(verbose == TRUE){ -tempo.cat <- paste0("ONLY: ", tempo.thread.nb, " THREADS AVAILABLE") -cat(paste0("\n", tempo.cat, "\n")) -} -} -}else{ -thread.nb <- tempo.thread.nb -} -if(verbose == TRUE){ -tempo.cat <- paste0("NUMBER OF THREADS USED: ", thread.nb) -cat(paste0("\n ", tempo.cat, "\n")) -} -Clust <- parallel::makeCluster(thread.nb, outfile = paste0(res.path, "/fun_slide_parall_log.txt")) # outfile to print or cat during parallelization (only possible in a file, outfile = "" do not work on windows) -cluster.list <- parallel::clusterSplit(Clust, 1:nrow(wind)) # split according to the number of cluster -if(verbose == TRUE){ -tempo.cat <- paste0("SPLIT OF TEST NUMBERS IN PARALLELISATION:") -cat(paste0("\n ", tempo.cat, "\n")) -str(cluster.list) # using print(str()) add a NULL below the result -cat("\n") -} -paral.output.list <- parallel::clusterApply( # -cl = Clust, -x = cluster.list, -function.name = function.name, -data = data, -FUN = fun, # because fun argument of clusterApply -args = args, -thread.nb = thread.nb, -print.count = print.count, -wind = wind, -left = left, -right = right, -res.path = res.path, -lib.path = lib.path, -verbose = verbose, -cute.path = cute.path, -fun = function( -x, -function.name, -data, -FUN, -args, -thread.nb, -print.count, -wind, -left, -right, -res.path, -lib.path, -verbose, -cute.path -){ -# check again: very important because another R -process.id <- Sys.getpid() -cat(paste0("\nPROCESS ID ", process.id, " -> TESTS ", x[1], " TO ", x[length(x)], "\n")) -source(cute.path, local = .GlobalEnv) -fun_pack(req.package = "lubridate", lib.path = lib.path, load = TRUE) # load = TRUE to be sure that functions are present in the environment. And this prevent to use R.lib.path argument of fun_python_pack() -# end check again: very important because another R -ini.date <- Sys.time() -ini.time <- as.numeric(ini.date) # time of process begin, converted into -output <- NULL -print.count.loop <- 0 -for(i4 in 1:length(x)){ -print.count.loop <- print.count.loop + 1 -log <- get(left)(data, wind$left[x[i4]]) & get(right)(data, wind$right[x[i4]]) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -output <- c(output, eval(parse(text = paste0("FUN(data[log]", if( ! is.null(args)){paste0(", ", args)}, ")")))) -if(verbose == TRUE){ -if(print.count.loop == print.count){ -print.count.loop <- 0 -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -final.loop <- (tempo.time - ini.time) / i4 * length(x) # expected duration in seconds # intra nb.compar loop lapse: time lapse / cycles done * cycles remaining -final.exp <- as.POSIXct(final.loop, origin = ini.date) -cat(paste0("\nIN PROCESS ", process.id, " | LOOP ", format(i4, big.mark=","), " / ", format(length(x), big.mark=","), " | TIME SPENT: ", tempo.lapse, " | EXPECTED END: ", final.exp)) -} -if(i4 == length(x)){ -tempo.time <- as.numeric(Sys.time()) -tempo.lapse <- round(lubridate::seconds_to_period(tempo.time - ini.time)) -cat(paste0("\nPROCESS ", process.id, " ENDED | LOOP ", format(i4, big.mark=","), " / ", format(length(x), big.mark=","), " | TIME SPENT: ", tempo.lapse, "\n\n")) -} -} -} -wind <- wind[x, ] -if(length(output) != nrow(wind)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -output <- data.frame(wind, value = output, stringsAsFactors = TRUE) -return(output) -} -} -) -parallel::stopCluster(Clust) -# result assembly -output <- data.frame() -for(i2 in 1:length(paral.output.list)){ # compartment relatives to each parallelization -output <- rbind(output, paral.output.list[[i2]], stringsAsFactors = TRUE) -} -# end result assembly -if(nrow(output) != nrow(wind)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5\nlength(output): ", length(output), "\nnrow(wind): ", nrow(wind)) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -output <- output[order(output$left), ] -} -} -if(verbose == TRUE){ -end.date <- Sys.time() -end.time <- as.numeric(end.date) -total.lapse <- round(lubridate::seconds_to_period(end.time - ini.time)) -cat(paste0("\nfun_slide JOB END\n\nTIME: ", end.date, "\n\nTOTAL TIME LAPSE: ", total.lapse, "\n\n\n")) -} -return(output) -} - - - - -######## fun_codon2aa() #### convert codon to amino acid using standard genetic code - - -fun_codon2aa <- function( -data, -display = FALSE -){ -# AIM -# Convert codon to amino acid using standard genetic code indicated in https://en.wikipedia.org/wiki/DNA_and_RNA_codon_tables -# WARNINGS -# None -# ARGUMENTS -# data: single caracter string of three characters, or vector of three caracters, indicating the DNA codon (only "A", "T", "G" and "C" allowed). Case insensitive. Omitted if display argument is TRUE -# display: logical. Display the whole genetic table? if TRUE, override data -# RETURN -# The 1 letter uppercase amino acid of the submitted codon or the whole table if display argument is TRUE -# REQUIRED PACKAGES -# None -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# fun_check() -# EXAMPLE -# fun_codon2aa(data = "ATC", display = TRUE) -# see http -# DEBUGGING -# data = "atg" ; display = FALSE -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words -# end reserved words -# arg with no default values -mandat.args <- c( -"data" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data, class = "vector", typeof = "character", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = display, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ # normally no NA -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"data", -"display" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -# end warning initiation -# other checkings -if(length(data) == 1L){ -data <- unlist(strsplit(data, split = "")) -}else if(length(data) != 3L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A STRING OF THREE CHARACTERS OR A VECTOR OF THREE CHARACTERS, MADE OF \"A\", \"C\", \"G\", \"T\" ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! all(toupper(data) %in% c("A", "C", "G","T"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A STRING OF THREE CHARACTERS OR A VECTOR OF THREE CHARACTERS, MADE OF \"A\", \"C\", \"G\", \"T\" ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end other checkings -# reserved word checking -# end reserved word checking -# end second round of checking and data preparation -# package checking -# end package checking -# main code -# standard genetic code -sgc <- array( -c( -"F", "L", "I", "V", -"S", "P", "T", "A", -"Y", "H", "N", "D", -"C", "R", "S", "G", - -"F", "L", "I", "V", -"S", "P", "T", "A", -"Y", "H", "N", "D", -"C", "R", "S", "G", - -"L", "L", "I", "V", -"S", "P", "T", "A", -"stop", "Q", "K", "E", -"stop", "R", "R", "G", - -"L", "L", "M", "V", -"S", "P", "T", "A", -"stop", "Q", "K", "E", -"W", "R", "R", "G" -), -dim = c(4, 4, 4), -dimnames = list( -first = c("T", "C", "A", "G"), -second = c("T", "C", "A", "G"), -third = c("T", "C", "A", "G") -) -) -# end standard genetic code -if(display == TRUE){ -output <- sgc -}else{ -data <- toupper(data) -output <- eval(parse(text = paste0("sgc['", paste0(data, collapse = "','"), "']"))) -} -return(output) -} - - -######## fun_codon_finder() #### gives the codon number and position in the codon of nucleotid positions - - -fun_codon_finder <- function( -pos, -begin, -end -){ -# AIM -# gives the codon number and position in the codon of nucleotid positions -# WARNINGS -# Only for coding sequences (no introns): ((end - begin) + 1) / 3 must be an integer (i.e., modulo zero) -# Negatives positions allowed but this implies that one base has the position 0 in the sequence -# ARGUMENTS -# pos: vector of integers indicating the positions of nucleotids in a sequence. Must be between begin and end arguments -# begin: single integer indicating the position of the first base of the coding sequence -# end: single indicating the position of the last base of the coding sequence -# RETURN -# a data frame with column names: -# pos: values of the pos argument -# codon_nb: the codon number in the CDS encompassing the pos value -# codon_pos: the position of pos in the codon (either 1, 2 or 3) -# codon_begin: the first base position of the codon -# codon_end: the last base position of the codon -# REQUIRED PACKAGES -# None -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# fun_check() -# EXAMPLE -# fun_codon_finder(c(5, 6, 8, 10), begin = 5, end = 10) -# fun_codon_finder(c(0, 5, 6, 8, 10), begin = -2, end = 12) -# see http -# DEBUGGING -# pos = c(5, 6, 8, 10) ; begin = 5 ; end = 10 -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words -# end reserved words -# arg with no default values -mandat.args <- c( -"pos", -"begin", -"end" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = pos, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = begin, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = end, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ # normally no NA -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"pos", -"begin", -"end" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -# end warning initiation -# other checkings -if(begin >= end){ -tempo.cat <- paste0("ERROR IN ", function.name, ": end ARGUMENT MUST BE STRICTLY GREATER THAN begin ARGUMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if((end - begin + 1) %% 3 != 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ((end - begin) + 1) / 3 MUST BE AN INTEGER (I.E., MODULO ZERO)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(pos < begin | pos > end)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": pos ARGUMENT VALUES MUST BE BETWEEN begin AND end ARGUMENT VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end other checkings -# reserved word checking -# end reserved word checking -# end second round of checking and data preparation -# package checking -# end package checking -# main code -first <- seq.int(from = begin, to = end, by = 3) -last <- seq.int(from = begin + 2, to = end, by = 3) -tempo <- lapply(X = pos, FUN = function(x = X){ -tempo.log <- x >= first & x <= last -if(sum(tempo.log, na.rm = TRUE) != 1){ # check that 1 possible TRUE -tempo.cat <- paste0("ERROR IN ", function.name, ": INTERNAL ERROR. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -codon_nb <- which(tempo.log) -codon_pos <- as.integer((x - (begin + (codon_nb - 1) * 3) + 1)) -codon_begin <- as.integer(first[tempo.log]) -codon_end <- as.integer(last[tempo.log]) -} -return(data.frame(codon_nb = codon_nb, codon_pos = codon_pos, codon_begin = codon_begin, codon_end = codon_end)) -}) -tempo <- do.call("rbind", tempo) -output <- data.frame(pos = as.integer(pos), tempo) -return(output) -} - - -################ Graphics management - - -# this order can be used: -# fun_width() -# fun_open() -# fun_prior_plot() # not for ggplot2 -# plot() or any other plotting -# fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 -# fun_close() - - -######## fun_width() #### window width depending on classes to plot - - -fun_width <- function( -class.nb, -inches.per.class.nb = 1, -ini.window.width = 7, -inch.left.space, -inch.right.space, -boundarie.space = 0.5 -){ -# AIM -# rescale the width of a window to open depending on the number of classes to plot -# can be used for height, considering that it is as if it was a width -# this order can be used: -# fun_width() -# fun_open() -# fun_prior_plot() # not for ggplot2 -# plot() or any other plotting -# fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 -# fun_close() -# ARGUMENTS -# class.nb: number of class to plot -# inches.per.class.nb: number of inches per unit of class.nb. 2 means 2 inches for each boxplot for instance -# ini.window.width:initial window width in inches -# inch.left.space: left horizontal margin of the figure region (in inches) -# inch.right.space: right horizontal margin of the figure region (in inches) -# boundarie.space: space between the right and left limits of the plotting region and the plot (0.5 means half a class width) -# RETURN -# the new window width in inches -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_width(class.nb = 10, inches.per.class.nb = 0.2, ini.window.width = 7, inch.left.space = 1, inch.right.space = 1, boundarie.space = 0.5) -# DEBUGGING -# class.nb = 10 ; inches.per.class.nb = 0.2 ; ini.window.width = 7 ; inch.left.space = 1 ; inch.right.space = 1 ; boundarie.space = 0.5 # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = class.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = inches.per.class.nb, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = ini.window.width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = inch.left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = inch.right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = boundarie.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -range.max <- class.nb + boundarie.space # the max range of the future plot -range.min <- boundarie.space # the min range of the future plot -window.width <- inch.left.space + inch.right.space + inches.per.class.nb * (range.max - range.min) -return(window.width) -} - - -######## fun_open() #### open a GUI or pdf graphic window - - -fun_open <- function( -pdf = TRUE, -pdf.path = "working.dir", -pdf.name = "graph", -width = 7, -height = 7, -paper = "special", -pdf.overwrite = FALSE, -rescale = "fixed", -remove.read.only = TRUE, -return.output = FALSE -){ -# AIM -# open a pdf or screen (GUI) graphic window and return initial graphic parameters -# this order can be used: -# fun_width() -# fun_open() -# fun_prior_plot() # not for ggplot2 -# plot() or any other plotting -# fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 -# fun_close() -# WARNINGS -# On Linux, use pdf = TRUE, if (GUI) graphic window is not always available, meaning that X is not installed (clusters for instance). Use X11() in R to test if available -# ARGUMENTS: -# pdf: logical. Use pdf display? If FALSE, a GUI is opened -# pdf.path: where the pdf is saved (do not terminate by / or \\). Write "working.dir" if working directory is required (default). Ignored if pdf == FALSE -# pdf.name: name of the pdf file containing the graphs (the .pdf extension is added by the function, if not detected in the name end). Ignored if pdf == FALSE -# width: width of the window (in inches) -# height: height of the window (in inches) -# paper: paper argument of the pdf function (paper format). Only used for pdf(). Either "a4", "letter", "legal", "us", "executive", "a4r", "USr" or "special". If "special", means that the paper dimension will be width and height. With another paper format, if width or height is over the size of the paper, width or height will be modified such that the plot is adjusted to the paper dimension (see $dim in the returned list below to see the modified dimensions). Ignored if pdf == FALSE -# pdf.overwrite: logical. Existing pdf can be overwritten? . Ignored if pdf == FALSE -# rescale: kind of GUI. Either "R", "fit", or "fixed". Ignored on Mac and Linux OS. See ?windows for details -# remove.read.only: logical. remove the read only (R.O.) graphical parameters? If TRUE, the graphical parameters are returned without the R.O. parameters. The returned $ini.par list can be used to set the par() of a new graphical device. If FALSE, graphical parameters are returned with the R.O. parameters, which provides information like text dimension (see ?par() ). The returned $ini.par list can be used to set the par() of a new graphical device, but generate a warning message. Ignored if return.output == FALSE. -# return.output: logical. Return output ? If TRUE the output list is displayed -# RETURN -# a list containing: -# $pdf.loc: path of the pdf created -# $ini.par: initial par() parameters -# $zone.ini: initial window spliting -# $dim: dimension of the graphical device (in inches) -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_open(pdf = FALSE, pdf.path = "C:/Users/Gael/Desktop", pdf.name = "graph", width = 7, height = 7, paper = "special", pdf.overwrite = FALSE, return.output = TRUE) -# DEBUGGING -# pdf = TRUE ; pdf.path = "C:/Users/Gael/Desktop" ; pdf.name = "graphs" ; width = 7 ; height = 7 ; paper = "special" ; pdf.overwrite = FALSE ; rescale = "fixed" ; remove.read.only = TRUE ; return.output = TRUE # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = pdf, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = pdf.path, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = pdf.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = height, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = paper, options = c("a4", "letter", "legal", "us", "executive", "a4r", "USr", "special", "A4", "LETTER", "LEGAL", "US"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data =pdf.overwrite, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = rescale, options = c("R", "fit", "fixed"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = remove.read.only, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.output, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if(pdf.path == "working.dir"){ -pdf.path <- getwd() -}else{ -if(grepl(x = pdf.path, pattern = ".+/$")){ -pdf.path <- sub(x = pdf.path, pattern = "/$", replacement = "") # remove the last / -}else if(grepl(x = pdf.path, pattern = ".+[\\]$")){ # or ".+\\\\$" # cannot be ".+\$" because \$ does not exist contrary to \n -pdf.path <- sub(x = pdf.path, pattern = "[\\]$", replacement = "") # remove the last / -} -if(dir.exists(pdf.path) == FALSE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\npdf.path ARGUMENT DOES NOT CORRESPOND TO EXISTING DIRECTORY\n", pdf.path) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# par.ini recovery -# cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt -if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows -open.fail <- NULL -grDevices::windows() -ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -}else if(Sys.info()["sysname"] == "Linux"){ -if(pdf == TRUE){# cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt -if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment -tempo.random.seed <- .Random.seed -on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) -}else{ -on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness -} -set.seed(NULL) -tempo.code <- sample(x = 1:1e7, size = 1) -while(file.exists(paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf")) == TRUE){ -tempo.code <- tempo.code + 1 -} -grDevices::pdf(width = width, height = height, file=paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf"), paper = paper) -ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the pdf window -file.remove(paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf")) # remove the pdf file -}else{ -# test if X11 can be opened -if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHIS FUNCTION CANNOT BE USED ON LINUX IF A Rplots.pdf FILE ALREADY EXISTS HERE\n", getwd()) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -open.fail <- suppressWarnings(try(grDevices::X11(), silent = TRUE))[] # try to open a X11 window. If open.fail == NULL, no problem, meaning that the X11 window is opened. If open.fail != NULL, a pdf can be opened here paste0(getwd(), "/Rplots.pdf") -if(is.null(open.fail)){ -ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -}else if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ -file.remove(paste0(getwd(), "/Rplots.pdf")) # remove the pdf file -tempo.cat <- ("ERROR IN fun_open()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, EITHER SET THE X GRAPHIC INTERFACE OF THE SYSTEM OR SET THE pdf ARGUMENT OF THE fun_open() FUNCTION TO TRUE AND RERUN") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -} -}else{ -open.fail <- NULL -grDevices::quartz() -ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -} -# end par.ini recovery -zone.ini <- matrix(1, ncol=1) # to recover the initial parameters for next figure region when device region split into several figure regions -if(pdf == TRUE){ -if(grepl(x = pdf.name, pattern = "\\.pdf$")){ -pdf.name <- sub(x = pdf.name, pattern = "\\.pdf$", replacement = "") # remove the last .pdf -} -pdf.loc <- paste0(pdf.path, "/", pdf.name, ".pdf") -if(file.exists(pdf.loc) == TRUE & pdf.overwrite == FALSE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\npdf.loc FILE ALREADY EXISTS AND CANNOT BE OVERWRITTEN DUE TO pdf.overwrite ARGUMENT SET TO TRUE\n", pdf.loc) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -grDevices::pdf(width = width, height = height, file=pdf.loc, paper = paper) -} -}else if(pdf == FALSE){ -pdf.loc <- NULL -if(Sys.info()["sysname"] == "Windows"){ # .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows -grDevices::windows(width = width, height = height, rescale = rescale) -}else if(Sys.info()["sysname"] == "Linux"){ -if( ! is.null(open.fail)){ -tempo.cat <- "ERROR IN fun_open()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, EITHER SET THE X GRAPHIC INTERFACE OF THE SYSTEM OR SET THE pdf ARGUMENT OF THE fun_open() FUNCTION TO TRUE AND RERUN" -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -grDevices::X11(width = width, height = height) -} -}else{ -grDevices::quartz(width = width, height = height) -} -} -if(return.output == TRUE){ -output <- list(pdf.loc = pdf.loc, ini.par = ini.par, zone.ini = zone.ini, dim = dev.size()) -return(output) -} -} - - -######## fun_prior_plot() #### set graph param before plotting (erase axes for instance) - - -fun_prior_plot <- function( -param.reinitial = FALSE, -xlog.scale = FALSE, -ylog.scale = FALSE, -remove.label = TRUE, -remove.x.axis = TRUE, -remove.y.axis = TRUE, -std.x.range = TRUE, -std.y.range = TRUE, -down.space = 1, -left.space = 1, -up.space = 1, -right.space = 1, -orient = 1, -dist.legend = 3.5, -tick.length = 0.5, -box.type = "n", -amplif.label = 1, -amplif.axis = 1, -display.extend = FALSE, -return.par = FALSE -){ -# AIM -# very convenient to erase the axes for post plot axis redrawing using fun_post_plot() -# reinitialize and set the graphic parameters before plotting -# CANNOT be used if no graphic device already opened -# ARGUMENTS -# param.reinitial: reinitialize graphic parameters before applying the new ones, as defined by the other arguments? Either TRUE or FALSE -# xlog.scale: Log scale for the x-axis? Either TRUE or FALSE. If TRUE, erases the x-axis, except legend, for further drawing by fun_post_plot()(xlog argument of par()) -# ylog.scale: Log scale for the y-axis? Either TRUE or FALSE. If TRUE, erases the y-axis, except legend, for further drawing by fun_post_plot()(ylog argument of par()) -# remove.label: remove labels (axis legend) of the two axes? Either TRUE or FALSE (ann argument of par()) -# remove.x.axis: remove x-axis except legend? Either TRUE or FALSE (control the xaxt argument of par()). Automately set to TRUE if xlog.scale == TRUE -# remove.y.axis: remove y-axis except legend? Either TRUE or FALSE (control the yaxt argument of par()). Automately set to TRUE if ylog.scale == TRUE -# std.x.range: standard range on the x-axis? TRUE (no range extend) or FALSE (4% range extend). Controls xaxs argument of par() (TRUE is xaxs = "i", FALSE is xaxs = "r") -# std.y.range: standard range on the y-axis? TRUE (no range extend) or FALSE (4% range extend). Controls yaxs argument of par() (TRUE is yaxs = "i", FALSE is yaxs = "r") -# down.space: lower vertical margin (in inches, mai argument of par()) -# left.space: left horizontal margin (in inches, mai argument of par()) -# up.space: upper vertical margin between plot region and grapical window (in inches, mai argument of par()) -# right.space: right horizontal margin (in inches, mai argument of par()) -# orient: scale number orientation (las argument of par()). 0, always parallel to the axis; 1, always horizontal; 2, always perpendicular to the axis; 3, always vertical -# dist.legend: numeric value that moves axis legends away in inches (first number of mgp argument of par() but in inches thus / 0.2) -# tick.length: length of the ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc. 0 means no tick -# box.type: bty argument of par(). Either "o", "l", "7", "c", "u", "]", the resulting box resembles the corresponding upper case letter. A value of "n" suppresses the box -# amplif.label: increase or decrease the size of the text in legends -# amplif.axis: increase or decrease the size of the scale numbers in axis -# display.extend: extend display beyond plotting region? Either TRUE or FALSE (xpd argument of par() without NA) -# return.par: return graphic parameter modification? -# RETURN -# return graphic parameter modification -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_prior_plot(param.reinitial = FALSE, xlog.scale = FALSE, ylog.scale = FALSE, remove.label = TRUE, remove.x.axis = TRUE, remove.y.axis = TRUE, std.x.range = TRUE, std.y.range = TRUE, down.space = 1, left.space = 1, up.space = 1, right.space = 1, orient = 1, dist.legend = 4.5, tick.length = 0.5, box.type = "n", amplif.label = 1, amplif.axis = 1, display.extend = FALSE, return.par = FALSE) -# DEBUGGING -# param.reinitial = FALSE ; xlog.scale = FALSE ; ylog.scale = FALSE ; remove.label = TRUE ; remove.x.axis = TRUE ; remove.y.axis = TRUE ; std.x.range = TRUE ; std.y.range = TRUE ; down.space = 1 ; left.space = 1 ; up.space = 1 ; right.space = 1 ; orient = 1 ; dist.legend = 4.5 ; tick.length = 0.5 ; box.type = "n" ; amplif.label = 1 ; amplif.axis = 1 ; display.extend = FALSE ; return.par = FALSE # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = param.reinitial, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = xlog.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = ylog.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = remove.label, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = remove.x.axis, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = remove.y.axis, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = std.x.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = std.y.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = down.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = up.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = orient, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.type, options = c("o", "l", "7", "c", "u", "]", "n"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = amplif.label, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = amplif.axis, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = display.extend, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.par, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if(is.null(dev.list())){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THIS FUNCTION CANNOT BE USED IF NO GRAPHIC DEVICE ALREADY OPENED (dev.list() IS CURRENTLY NULL)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# par.ini recovery -# cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt -if(param.reinitial == TRUE){ -if( ! all(names(dev.cur()) == "null device")){ -active.wind.nb <- dev.cur() -}else{ -active.wind.nb <- 0 -} -if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows -grDevices::windows() -ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -}else if(Sys.info()["sysname"] == "Linux"){ -if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THIS FUNCTION CANNOT BE USED ON LINUX WITH param.reinitial SET TO TRUE IF A Rplots.pdf FILE ALREADY EXISTS HERE: ", getwd()) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -open.fail <- suppressWarnings(try(grDevices::X11(), silent = TRUE))[] # try to open a X11 window. If open.fail == NULL, no problem, meaning that the X11 window is opened. If open.fail != NULL, a pdf can be opened here paste0(getwd(), "/Rplots.pdf") -if(is.null(open.fail)){ -ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -}else if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ -ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -invisible(dev.off()) # close the new window -file.remove(paste0(getwd(), "/Rplots.pdf")) # remove the pdf file -}else{ -tempo.cat <- ("ERROR IN fun_prior_plot()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, PLEASE USE A PDF GRAPHIC INTERFACE AND RERUN") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -}else{ # macOS -grDevices::quartz() -ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened) -invisible(dev.off()) # close the new window -} -if( ! all(names(dev.cur()) == "null device")){ -invisible(dev.set(active.wind.nb)) # go back to the active window if exists -par(ini.par) # apply the initial par to current window -} -} -# end par.ini recovery -if(remove.x.axis == TRUE){ -par(xaxt = "n") # suppress the y-axis label -}else{ -par(xaxt = "s") -} -if(remove.y.axis == TRUE){ -par(yaxt = "n") # suppress the y-axis label -}else{ -par(yaxt = "s") -} -if(std.x.range == TRUE){ -par(xaxs = "i") -}else{ -par(xaxs = "r") -} -if(std.y.range == TRUE){ -par(yaxs = "i") -}else{ -par(yaxs = "r") -} -par(mai = c(down.space, left.space, up.space, right.space), ann = ! remove.label, las = orient, mgp = c(dist.legend/0.2, 1, 0), xpd = display.extend, bty= box.type, cex.lab = amplif.label, cex.axis = amplif.axis) -par(tcl = -par()$mgp[2] * tick.length) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) -if(xlog.scale == TRUE){ -par(xaxt = "n", xlog = TRUE) # suppress the x-axis label -}else{ -par(xlog = FALSE) -} -if(ylog.scale == TRUE){ -par(yaxt = "n", ylog = TRUE) # suppress the y-axis label -}else{ -par(ylog = FALSE) -} -if(return.par == TRUE){ -tempo.par <- par() -return(tempo.par) -} -} - - -######## fun_scale() #### select nice label numbers when setting number of ticks on an axis - - - - - -fun_scale <- function(n, lim, kind = "approx", lib.path = NULL){ -# AIM -# attempt to select nice scale numbers when setting n ticks on a lim axis range -# ARGUMENTS -# n: desired number of main ticks on the axis (integer above 0) -# lim: vector of 2 numbers indicating the limit range of the axis. Order of the 2 values matters (for inverted axis). Can be log transformed values -# kind: either "approx" (approximative), "strict" (strict) or "strict.cl" (strict clean). If "approx", use the scales::trans_breaks() function to provide an easy to read scale of approximately n ticks spanning the range of the lim argument. If "strict", cut the range of the lim argument into n + 1 equidistant part and return the n numbers at each boundary. This often generates numbers uneasy to read. If "strict.cl", provide an easy to read scale of exactly n ticks, but sometimes not completely spanning the range of the lim argument -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# RETURN -# a vector of numbers -# REQUIRED PACKAGES -# if kind = "approx": -# ggplot2 -# scales -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_round() -# EXAMPLES -# approximate number of main ticks -# ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "approx") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) -# strict number of main ticks -# ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "strict") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) -# strict "clean" number of main ticks -# ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "strict.cl") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) -# approximate number of main ticks, scale inversion -# ymin = 3.101 ; ymax = 2 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "approx") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) -# DEBUGGING -# n = 9 ; lim = c(2, 3.101) ; kind = "approx" ; lib.path = NULL # for function debugging -# n = 10 ; lim = c(1e-4, 1e6) ; kind = "approx" ; lib.path = NULL # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# end initial argument checking -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_round", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_round() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & isTRUE(all.equal(n, 0))){ # isTRUE(all.equal(n, 0)) equivalent to n == 0 but deals with floats (approx ok) -tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A NON NULL AND POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) # -} -tempo <- fun_check(data = lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & all(diff(lim) == 0L)){ # isTRUE(all.equal(diff(lim), rep(0, length(diff(lim))))) not used because we strictly need zero as a result -tempo.cat <- paste0("ERROR IN ", function.name, ": lim ARGUMENT HAS A NULL RANGE (2 IDENTICAL VALUES)") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & any(lim %in% c(Inf, -Inf))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = kind, options = c("approx", "strict", "strict.cl"), length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -lim.rank <- rank(lim) # to deal with inverted axis -lim <- sort(lim) -if(kind == "approx"){ -# package checking -fun_pack(req.package = c("ggplot2"), lib.path = lib.path) -fun_pack(req.package = c("scales"), lib.path = lib.path) -# end package checking -output <- ggplot2::ggplot_build(ggplot2::ggplot() + ggplot2::scale_y_continuous( -breaks = scales::trans_breaks( -trans = "identity", -inv = "identity", -n = n -), -limits = lim -))$layout$panel_params[[1]]$y$breaks # pretty() alone is not appropriate: tempo.pret <- pretty(seq(lim[1] ,lim[2], length.out = n)) ; tempo.pret[tempo.pret > = lim[1] & tempo.pret < = lim[2]]. # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks -if( ! is.null(attributes(output))){ # layout$panel_params[[1]]$y$breaks can be characters (labels of the axis). In that case, it has attributes that corresponds to positions -output <- unlist(attributes(output)) -} -output <- output[ ! is.na(output)] -}else if(kind == "strict"){ -output <- fun_round(seq(lim[1] ,lim[2], length.out = n), 2) -}else if(kind == "strict.cl"){ -tempo.range <- diff(sort(lim)) -tempo.max <- max(lim) -tempo.min <- min(lim) -mid <- tempo.min + (tempo.range/2) # middle of axis -tempo.inter <- tempo.range / (n + 1) # current interval between two ticks, between 0 and Inf -if(tempo.inter == 0L){ # isTRUE(all.equal(tempo.inter, rep(0, length(tempo.inter)))) not used because we strictly need zero as a result -tempo.cat <- paste0("ERROR IN ", function.name, ": THE INTERVAL BETWEEN TWO TICKS OF THE SCALE IS NULL. MODIFY THE lim OR n ARGUMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -log10.abs.lim <- 200 -log10.range <- (-log10.abs.lim):log10.abs.lim -log10.vec <- 10^log10.range -round.vec <- c(5, 4, 3, 2.5, 2, 1.25, 1) -dec.table <- outer(log10.vec, round.vec) # table containing the scale units (row: power of ten from -201 to +199, column: the 5, 2.5, 2, 1.25, 1 notches - - - -# recover the number of leading zeros in tempo.inter -ini.scipen <- options()$scipen -options(scipen = -1000) # force scientific format -if(any(grepl(pattern = "\\+", x = tempo.inter))){ # tempo.inter > 1 -power10.exp <- as.integer(substring(text = tempo.inter, first = (regexpr(pattern = "\\+", text = tempo.inter) + 1))) # recover the power of 10. Example recover 08 from 1e+08 -mantisse <- as.numeric(substr(x = tempo.inter, start = 1, stop = (regexpr(pattern = "\\+", text = tempo.inter) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 -}else if(any(grepl(pattern = "\\-", x = tempo.inter))){ # tempo.inter < 1 -power10.exp <- as.integer(substring(text = tempo.inter, first = (regexpr(pattern = "\\-", text = tempo.inter)))) # recover the power of 10. Example recover 08 from 1e+08 -mantisse <- as.numeric(substr(x = tempo.inter, start = 1, stop = (regexpr(pattern = "\\-", text = tempo.inter) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -tempo.scale <- dec.table[log10.range == power10.exp, ] -# new interval -inter.select <- NULL -for(i1 in 1:length(tempo.scale)){ -tempo.first.tick <- trunc((tempo.min + tempo.scale[i1]) / tempo.scale[i1]) * (tempo.scale[i1]) # this would be use to have a number not multiple of tempo.scale[i1]: ceiling(tempo.min) + tempo.scale[i1] * 10^power10.exp -tempo.last.tick <- tempo.first.tick + tempo.scale[i1] * (n - 1) -if((tempo.first.tick >= tempo.min) & (tempo.last.tick <= tempo.max)){ -inter.select <- tempo.scale[i1] -break() -} -} -if(is.null(inter.select)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -options(scipen = ini.scipen) # restore the initial scientific penalty -# end new interval -# centering the new scale -tempo.mid <- trunc((mid + (-1:1) * inter.select) / inter.select) * inter.select # tempo middle tick closest to the middle axis -mid.tick <- tempo.mid[which.min(abs(tempo.mid - mid))] -if(isTRUE(all.equal(n, rep(1, length(n))))){ # isTRUE(all.equal(n, rep(1, length(n)))) is similar to n == 1L but deals with float -output <- mid.tick -}else if(isTRUE(all.equal(n, rep(2, length(n))))){ # isTRUE(all.equal(n, rep(0, length(n)))) is similar to n == 2L but deals with float -output <- mid.tick -tempo.min.dist <- mid.tick - inter.select - tempo.min -tempo.max.dist <- tempo.max - mid.tick + inter.select -if(tempo.min.dist <= tempo.max.dist){ # distance between lowest tick and bottom axis <= distance between highest tick and top axis. If yes, extra tick but at the top, otherwise at the bottom -output <- c(mid.tick, mid.tick + inter.select) -}else{ -output <- c(mid.tick - inter.select, mid.tick) -} -}else if((n / 2 - trunc(n / 2)) > 0.1){ # > 0.1 to avoid floating point. Because result can only be 0 or 0.5. Thus, > 0.1 means odd number -output <- c(mid.tick - (trunc(n / 2):1) * inter.select, mid.tick, mid.tick + (1:trunc(n / 2)) * inter.select) -}else if((n / 2 - trunc(n / 2)) < 0.1){ # < 0.1 to avoid floating point. Because result can only be 0 or 0.5. Thus, < 0.1 means even number -tempo.min.dist <- mid.tick - trunc(n / 2) * inter.select - tempo.min -tempo.max.dist <- tempo.max - mid.tick + trunc(n / 2) * inter.select -if(tempo.min.dist <= tempo.max.dist){ # distance between lowest tick and bottom axis <= distance between highest tick and top axis. If yes, extra tick but at the bottom, otherwise at the top -output <- c(mid.tick - ((trunc(n / 2) - 1):1) * inter.select, mid.tick, mid.tick + (1:trunc(n / 2)) * inter.select) -}else{ -output <- c(mid.tick - (trunc(n / 2):1) * inter.select, mid.tick, mid.tick + (1:(trunc(n / 2) - 1)) * inter.select) -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end centering the new scale -# last check -if(min(output) < tempo.min){ -output <- c(output[-1], max(output) + inter.select) # remove the lowest tick and add a tick at the top -}else if( max(output) > tempo.max){ -output <- c(min(output) - inter.select, output[-length(output)]) -} -if(min(output) < tempo.min | max(output) > tempo.max){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(is.na(output))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5 (NA GENERATION)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end last check -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 6") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(diff(lim.rank) < 0){ -output <- rev(output) -} -return(output) -} - - -######## fun_inter_ticks() #### define coordinates of secondary ticks - - -fun_inter_ticks <- function( -lim, -log = "log10", -breaks = NULL, -n = NULL, -warn.print = TRUE -){ -# AIM -# define coordinates and values of secondary ticks -# ARGUMENTS -# lim: vector of 2 numbers indicating the limit range of the axis. Order of the 2 values matters (for inverted axis). If log argument is "log2" or "log10", values in lim must be already log transformed. Thus, negative or zero values are allowed -# log: either "log2" (values in the lim argument are log2 transformed) or "log10" (values in the lim argument are log10 transformed), or "no" -# breaks: mandatory vector of numbers indicating the main ticks values/positions when log argument is "no". Ignored when log argument is "log2" or "log10" -# n: number of secondary ticks between each main tick when log argument is "no". Ignored when log argument is "log2" or "log10" -# warn.print: logical. Print potential warning messages at the end of the execution? If FALSE, warning messages are never printed, but can still be recovered in the returned list -# RETURN -# a list containing -# $log: value of the log argument used -# $coordinates: the coordinates of the secondary ticks on the axis, between the lim values -# $values: the corresponding values associated to each coordinate (with log scale, 2^$values or 10^$values is equivalent to the labels of the axis) -# $warn: the potential warning messages. Use cat() for proper display. NULL if no warning -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# no log scale -# fun_inter_ticks(lim = c(-4,4), log = "no", breaks = c(-2, 0, 2), n = 3) -# fun_inter_ticks(lim = c(10, 0), log = "no", breaks = c(10, 8, 6, 4, 2, 0), n = 4) -# log2 -# fun_inter_ticks(lim = c(-4,4), log = "log2") -# log10 -# fun_inter_ticks(lim = c(-2,3), log = "log10") -# DEBUGGING -# lim = c(2, 3.101) ; log = "no" ; breaks = NULL ; n = NULL ; warn.print = TRUE # for function debugging -# lim = c(0, 26.5) ; log = "no" ; breaks = c(0, 10, 20) ; n = 3 # for function debugging -# lim = c(10, 0); log = "no"; breaks = c(10, 8, 6, 4, 2, 0); n = 4 # for function debugging -# lim = c(-10, -20); log = "no"; breaks = c(-20, -15, -10); n = 4 # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -req.function <- c( -"fun_check" -) -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED ", i1, "() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end required function checking -# argument primary checking -# arg with no default values -mandat.args <- c( -"lim" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# using fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(breaks)){ -tempo <- fun_check(data = breaks, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) -} -if( ! is.null(n)){ -tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = warn.print, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA -if(any(is.na(lim)) | any(is.na(log)) | any(is.na(breaks)) | any(is.na(n)) | any(is.na(warn.print))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nNO ARGUMENT CAN HAVE NA VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA -# management of NULL -if(is.null(lim) | is.null(log)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHESE ARGUMENTS\nlim\nlog\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL -if(all(diff(lim) == 0L)){ # isTRUE(all.equal(diff(lim), rep(0, length(diff(lim))))) not used because we strictly need zero as a result -tempo.cat <- paste0("ERROR IN ", function.name, "\nlim ARGUMENT HAS A NULL RANGE (2 IDENTICAL VALUES): ", paste(lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(any(lim %in% c(Inf, -Inf))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nlim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(log == "no" & is.null(breaks)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT CANNOT BE NULL IF log ARGUMENT IS \"no\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(breaks)){ -if(length(breaks) < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT MUST HAVE 2 VALUES AT LEAST (OTHERWISE, INTER TICK POSITIONS CANNOT BE COMPUTED): ", paste(breaks, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! isTRUE(all.equal(diff(sort(breaks)), rep(diff(sort(breaks))[1], length(diff(sort(breaks))))))){ # isTRUE(all.equal(n, 0)) equivalent to n == 0 but deals with floats (approx ok) -tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT MUST HAVE EQUIDISTANT VALUES (OTHERWISE, EQUAL NUMBER OF INTER TICK BETWEEN MAIN TICKS CANNOT BE COMPUTED): ", paste(breaks, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if( ! is.null(n)){ -if(n <= 0){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nn ARGUMENT MUST BE A POSITIVE AND NON NULL INTEGER: ", paste(n, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end second round of checking and data preparation -# main code -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -lim.rank <- rank(lim) # to deal with inverse axis -if(log != "no"){ -ini.scipen <- options()$scipen -options(scipen = -1000) # force scientific format -power10.exp <- as.integer(substring(text = 10^lim, first = (regexpr(pattern = "\\+|\\-", text = 10^lim)))) # recover the power of 10, i.e., integer part of lim. Example recover 08 from 1e+08. Works for log2 -# mantisse <- as.numeric(substr(x = 10^lim, start = 1, stop = (regexpr(pattern = "\\+|\\-", text = 10^lim) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 -options(scipen = ini.scipen) # restore the initial scientific penalty -tick.pos <- unique(as.vector(outer(2:10, ifelse(log == "log2", 2, 10)^((power10.exp[1] - ifelse(diff(lim.rank) > 0, 1, -1)):(power10.exp[2] + ifelse(diff(lim.rank) > 0, 1, -1)))))) # use log10(2:10) even if log2: it is to get log values between 0 and 1 -tick.pos <- sort(tick.pos, decreasing = ifelse(diff(lim.rank) > 0, FALSE, TRUE)) -if(log == "log2"){ -tick.values <- tick.pos[tick.pos >= min(2^lim) & tick.pos <= max(2^lim)] -tick.pos <- log2(tick.values) -}else if(log == "log10"){ -tick.values <- tick.pos[tick.pos >= min(10^lim) & tick.pos <= max(10^lim)] -tick.pos <- log10(tick.values) -} -}else{ -# if(length(breaks) > 1){ # not required because already checked above -breaks.rank <- rank(c(breaks[1], breaks[length(breaks)])) -if(diff(breaks.rank) != diff(lim.rank)){ -breaks <- sort(breaks, decreasing = ifelse(diff(lim.rank) < 0, TRUE, FALSE)) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") VALUES IN breaks ARGUMENT NOT IN THE SAME ORDER AS IN lim ARGUMENT -> VALUES REORDERED AS IN lim: ", paste(breaks, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -breaks.rank <- rank(c(breaks[1], breaks[length(breaks)])) -} -# } -main.tick.dist <- mean(diff(breaks), na.rm = TRUE) -tick.dist <- main.tick.dist / (n + 1) -tempo.extra.margin <- max(abs(diff(breaks)), na.rm = TRUE) -tick.pos <- seq( -if(diff(breaks.rank) > 0){breaks[1] - tempo.extra.margin}else{breaks[1] + tempo.extra.margin}, -if(diff(breaks.rank) > 0){breaks[length(breaks)] + tempo.extra.margin}else{breaks[length(breaks)] - tempo.extra.margin}, -by = tick.dist -) -tick.pos <- tick.pos[tick.pos >= min(lim) & tick.pos <= max(lim)] -tick.values <- tick.pos -} -if(any(is.na(tick.pos) | ! is.finite(tick.pos))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": NA or Inf GENERATED FOR THE INTER TICK POSITIONS: ", paste(tick.pos, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(tick.pos) == 0L){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO INTER TICKS COMPUTED BETWEEN THE LIMITS INDICATED: ", paste(lim, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -output <- list(log = log, coordinates = tick.pos, values = tick.values, warn = warn) -if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) # to recover the warning messages, see $warn -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -return(output) -} - - -######## fun_post_plot() #### set graph param after plotting (axes redesign for instance) - - - - - -fun_post_plot <- function( -x.side = 0, -x.log.scale = FALSE, -x.categ = NULL, -x.categ.pos = NULL, -x.lab = "", -x.axis.size = 1.5, -x.label.size = 1.5, -x.dist.legend = 0.5, -x.nb.inter.tick = 1, -y.side = 0, -y.log.scale = FALSE, -y.categ = NULL, -y.categ.pos = NULL, -y.lab = "", -y.axis.size = 1.5, -y.label.size = 1.5, -y.dist.legend = 0.5, -y.nb.inter.tick = 1, -text.angle = 90, -tick.length = 0.5, -sec.tick.length = 0.3, -bg.color = NULL, -grid.lwd = NULL, -grid.col = "white", -corner.text = "", -corner.text.size = 1, -just.label.add = FALSE, -par.reset = FALSE, -custom.par = NULL -){ -# AIM -# redesign axis. If x.side = 0, y.side = 0, the function just adds text at topright of the graph and reset par() for next graphics and provides outputs (see below) -# provide also positions for legend or additional text on the graph -# use fun_prior_plot() before this function for initial inactivation of the axis drawings -# ARGUMENTS -# x.side: axis at the bottom (1) or top (3) of the region figure. Write 0 for no change -# x.log.scale: Log scale for the x-axis? Either TRUE or FALSE -# x.categ: character vector representing the classes (levels()) to specify when the x-axis is qualititative(stripchart, boxplot) -# x.categ.pos: position of the classes names (numeric vector of identical length than x.categ). If left NULL, this will be 1:length(levels()) -# x.lab: label of the x-axis. If x.side == 0 and x.lab != "", then x.lab is printed -# x.axis.size: positive numeric. Increase or decrease the size of the x axis numbers. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2. Also control the size of displayed categories -# x.label.size: positive numeric. Increase or decrease the size of the x axis legend text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 -# x.dist.legend: increase the number to move x-axis legends away in inches (first number of mgp argument of par() but in inches) -# x.nb.inter.tick: number of secondary ticks between main ticks on x-axis (only if not log scale). 0 means no secondary ticks -# y.side: axis at the left (2) or right (4) of the region figure. Write 0 for no change -# y.log.scale: Log scale for the y-axis? Either TRUE or FALSE -# y.categ: classes (levels()) to specify when the y-axis is qualititative(stripchart, boxplot) -# y.categ.pos: position of the classes names (numeric vector of identical length than y.categ). If left NULL, this will be 1:length(levels()) -# y.lab: label of the y-axis. If y.side == 0 and y.lab != "", then y.lab is printed -# y.axis.size: positive numeric. Increase or decrease the size of the y axis numbers. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2. Also control the size of displayed categories -# y.label.size: positive numeric. Increase or decrease the size of the y axis legend text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 -# y.dist.legend: increase the number to move y-axis legends away in inches (first number of mgp argument of par() but in inches) -# y.nb.inter.tick: number of secondary ticks between main ticks on y-axis (only if not log scale). 0 means non secondary ticks -# text.angle: angle of the text when axis is qualitative -# tick.length: length of the main ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) -# sec.tick.length: length of the secondary ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) -# bg.color: background color of the plot region. NULL for no color. BEWARE: cover/hide an existing plot ! -# grid.lwd: if non NULL, activate the grid line (specify the line width) -# grid.col: grid line color (only if grid.lwd non NULL) -# corner.text: text to add at the top right corner of the window -# corner.text.size: positive numeric. Increase or decrease the size of the text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 -# par.reset: to reset all the graphics parameters. BEWARE: TRUE can generate display problems, mainly in graphic devices with multiple figure regions -# just.label.add: just add axis labels (legend)? Either TRUE or FALSE. If TRUE, at least (x.side == 0 & x.lab != "") or (y.side == 0 & y.lab != "") must be set to display the corresponding x.lab or y.lab -# custom.par: list that provides the parameters that reset all the graphics parameters. BEWARE: if NULL and par.reset == TRUE, the default par() parameters are used -# RETURN -# a list containing: -# $x.mid.left.dev.region: middle of the left margin of the device region, in coordinates of the x-axis -# $x.left.dev.region: left side of the left margin (including the potential margin of the device region), in coordinates of the x-axis -# $x.mid.right.dev.region: middle of the right margin of the device region, in coordinates of the x-axis -# $x.right.dev.region: right side of the right margin (including the potential margin of the device region), in coordinates of the x-axis -# $x.mid.left.fig.region: middle of the left margin of the figure region, in coordinates of the x-axis -# $x.left.fig.region: left side of the left margin, in coordinates of the x-axis -# $x.mid.right.fig.region: middle of the right margin of the figure region, in coordinates of the x-axis -# $x.right.fig.region: right side of the right margin, in coordinates of the x-axis -# $x.left.plot.region: left side of the plot region, in coordinates of the x-axis -# $x.right.plot.region: right side of the plot region, in coordinates of the x-axis -# $x.mid.plot.region: middle of the plot region, in coordinates of the x-axis -# $y.mid.bottom.dev.region: middle of the bottom margin of the device region, in coordinates of the y-axis -# $y.bottom.dev.region: bottom side of the bottom margin (including the potential margin of the device region), in coordinates of the y-axis -# $y.mid.top.dev.region: middle of the top margin of the device region, in coordinates of the y-axis -# $y.top.dev.region: top side of the top margin (including the potential margin of the device region), in coordinates of the y-axis -# $y.mid.bottom.fig.region: middle of the bottom margin of the figure region, in coordinates of the y-axis -# $y.bottom.fig.region: bottom of the bottom margin of the figure region, in coordinates of the y-axis -# $y.mid.top.fig.region: middle of the top margin of the figure region, in coordinates of the y-axis -# $y.top.fig.region: top of the top margin of the figure region, in coordinates of the y-axis -# $y.top.plot.region: top of the plot region, in coordinates of the y-axis -# $y.bottom.plot.region: bottom of the plot region, in coordinates of the y-axis -# $y.mid.plot.region: middle of the plot region, in coordinates of the y-axis -# $text: warning text -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_open() to reinitialize graph parameters if par.reset = TRUE and custom.par = NULL -# EXAMPLES -# Example of log axis with log y-axis and unmodified x-axis: -# prior.par <- fun_prior_plot(param.reinitial = TRUE, xlog.scale = FALSE, ylog.scale = TRUE, remove.label = TRUE, remove.x.axis = FALSE, remove.y.axis = TRUE, down.space = 1, left.space = 1, up.space = 1, right.space = 1, orient = 1, dist.legend = 0.5, tick.length = 0.5, box.type = "n", amplif.label = 1, amplif.axis = 1, display.extend = FALSE, return.par = TRUE) ; plot(1:100, log = "y") ; fun_post_plot(y.side = 2, y.log.scale = prior.par$ylog, x.lab = "Values", y.lab = "TEST", y.axis.size = 1.25, y.label.size = 1.5, y.dist.legend = 0.7, just.label.add = ! prior.par$ann) -# Example of log axis with redrawn x-axis and y-axis: -# prior.par <- fun_prior_plot(param.reinitial = TRUE) ; plot(1:100) ; fun_post_plot(x.side = 1, x.lab = "Values", y.side = 2, y.lab = "TEST", y.axis.size = 1, y.label.size = 2, y.dist.legend = 0.6) -# Example of title easily added to a plot: -# plot(1:100) ; para <- fun_post_plot(corner.text = "TITLE ADDED") # try also: par(xpd = TRUE) ; text(x = para$x.mid.left.fig.region, y = para$y.mid.top.fig.region, labels = "TITLE ADDED", cex = 0.5) -# example with margins in the device region: -# windows(5,5) ; fun_prior_plot(box.type = "o") ; par(mai=c(0.5,0.5,0.5,0.5), omi = c(0.25,0.25,1,0.25), xaxs = "i", yaxs = "i") ; plot(0:10) ; a <- fun_post_plot(x.side = 0, y.side = 0) ; x <- c(a$x.mid.left.dev.region, a$x.left.dev.region, a$x.mid.right.dev.region, a$x.right.dev.region, a$x.mid.left.fig.region, a$x.left.fig.region, a$x.mid.right.fig.region, a$x.right.fig.region, a$x.right.plot.region, a$x.left.plot.region, a$x.mid.plot.region) ; y <- c(a$y.mid.bottom.dev.region, a$y.bottom.dev.region, a$y.mid.top.dev.region, a$y.top.dev.region, a$y.mid.bottom.fig.region, a$y.bottom.fig.region, a$y.mid.top.fig.region, a$y.top.fig.region, a$y.top.plot.region, a$y.bottom.plot.region, a$y.mid.plot.region) ; par(xpd = NA) ; points(x = rep(5, length(y)), y = y, pch = 16, col = "red") ; text(x = rep(5, length(y)), y = y, c("y.mid.bottom.dev.region", "y.bottom.dev.region", "y.mid.top.dev.region", "y.top.dev.region", "y.mid.bottom.fig.region", "y.bottom.fig.region", "y.mid.top.fig.region", "y.top.fig.region", "y.top.plot.region", "y.bottom.plot.region", "y.mid.plot.region"), cex = 0.65, col = grey(0.25)) ; points(y = rep(5, length(x)), x = x, pch = 16, col = "blue") ; text(y = rep(5, length(x)), x = x, c("x.mid.left.dev.region", "x.left.dev.region", "x.mid.right.dev.region", "x.right.dev.region", "x.mid.left.fig.region", "x.left.fig.region", "x.mid.right.fig.region", "x.right.fig.region", "x.right.plot.region", "x.left.plot.region", "x.mid.plot.region"), cex = 0.65, srt = 90, col = grey(0.25)) -# DEBUGGING -# x.side = 0 ; x.log.scale = FALSE ; x.categ = NULL ; x.categ.pos = NULL ; x.lab = "" ; x.axis.size = 1.5 ; x.label.size = 1.5 ; x.dist.legend = 1 ; x.nb.inter.tick = 1 ; y.side = 0 ; y.log.scale = FALSE ; y.categ = NULL ; y.categ.pos = NULL ; y.lab = "" ; y.axis.size = 1.5 ; y.label.size = 1.5 ; y.dist.legend = 0.7 ; y.nb.inter.tick = 1 ; text.angle = 90 ; tick.length = 0.5 ; sec.tick.length = 0.3 ; bg.color = NULL ; grid.lwd = NULL ; grid.col = "white" ; corner.text = "" ; corner.text.size = 1 ; just.label.add = FALSE ; par.reset = FALSE ; custom.par = NULL # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_open", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_open() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = x.side, options = c(0, 1, 3), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.log.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(x.categ)){ -tempo <- fun_check(data = x.categ, class = "character", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(x.categ.pos)){ -tempo <- fun_check(data = x.categ.pos, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = x.lab, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.axis.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.label.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.nb.inter.tick, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.side, options = c(0, 2, 4), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.log.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(y.categ)){ -tempo <- fun_check(data = y.categ, class = "character", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(y.categ.pos)){ -tempo <- fun_check(data = y.categ.pos, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = y.lab, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.axis.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.label.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.nb.inter.tick, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = text.angle, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = sec.tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -if( ! is.null(bg.color)){ -tempo <- fun_check(data = bg.color, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if( ! (bg.color %in% colors() | grepl(pattern = "^#", bg.color))){ # check color -tempo.cat <- paste0("ERROR IN ", function.name, ": bg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # OR A COLOR NAME GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -if( ! is.null(grid.lwd)){ -tempo <- fun_check(data = grid.lwd, class = "vector", mode = "numeric", neg.values = FALSE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(grid.col)){ -tempo <- fun_check(data = grid.col, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if( ! (grid.col %in% colors() | grepl(pattern = "^#", grid.col))){ # check color -tempo.cat <- paste0("ERROR IN ", function.name, ": grid.col ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # OR A COLOR NAME GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = corner.text, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = corner.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = just.label.add, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = par.reset, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(custom.par)){ -tempo <- fun_check(data = custom.par, typeof = "list", length = 1, fun.name = function.name) ; eval(ee) -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -text <- NULL -par(tcl = -par()$mgp[2] * tick.length) -if(x.log.scale == TRUE){ -grid.coord.x <- c(10^par("usr")[1], 10^par("usr")[2]) -}else{ -grid.coord.x <- c(par("usr")[1], par("usr")[2]) -} -if(y.log.scale == TRUE){ -grid.coord.y <- c(10^par("usr")[3], 10^par("usr")[4]) -}else{ -grid.coord.y <- c(par("usr")[3], par("usr")[4]) -} -if( ! is.null(bg.color)){ -rect(grid.coord.x[1], grid.coord.y[1], grid.coord.x[2], grid.coord.y[2], col = bg.color, border = NA) -} -if( ! is.null(grid.lwd)){ -grid(nx = NA, ny = NULL, col = grid.col, lty = 1, lwd = grid.lwd) -} -if(x.log.scale == TRUE){ -x.mid.left.dev.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) -x.left.dev.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) # in x coordinates -x.mid.right.dev.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) -x.right.dev.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2])) # in x coordinates -x.mid.left.fig.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) -x.left.fig.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1]) # in x coordinates -x.mid.right.fig.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) -x.right.fig.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2])) # in x coordinates -x.left.plot.region <- 10^par("usr")[1] # in x coordinates, left of the plot region (according to x scale) -x.right.plot.region <- 10^par("usr")[2] # in x coordinates, right of the plot region (according to x scale) -x.mid.plot.region <- 10^((par("usr")[2] + par("usr")[1]) / 2) # in x coordinates, right of the plot region (according to x scale) -}else{ -x.mid.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) -x.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) # in x coordinates -x.mid.right.dev.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) -x.right.dev.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2])) # in x coordinates -x.mid.left.fig.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) -x.left.fig.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1]) # in x coordinates -x.mid.right.fig.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) -x.right.fig.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2])) # in x coordinates -x.left.plot.region <- par("usr")[1] # in x coordinates, left of the plot region (according to x scale) -x.right.plot.region <- par("usr")[2] # in x coordinates, right of the plot region (according to x scale) -x.mid.plot.region <- (par("usr")[2] + par("usr")[1]) / 2 # in x coordinates, right of the plot region (according to x scale) -} -if(y.log.scale == TRUE){ -y.mid.bottom.dev.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (par("omd")[3] / 2)) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space -y.bottom.dev.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * par("omd")[3]) # in y coordinates -y.mid.top.dev.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space -y.top.dev.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) # in y coordinates -y.mid.bottom.fig.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] / 2) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space -y.bottom.fig.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3]) # in y coordinates -y.mid.top.fig.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space -y.top.fig.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4])) # in y coordinates -y.top.plot.region <- 10^par("usr")[4] # in y coordinates, top of the plot region (according to y scale) -y.bottom.plot.region <- 10^par("usr")[3] # in y coordinates, bottom of the plot region (according to y scale) -y.mid.plot.region <- (par("usr")[3] + par("usr")[4]) / 2 # in x coordinates, right of the plot region (according to x scale) -}else{ -y.mid.bottom.dev.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (par("omd")[3] / 2)) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space -y.bottom.dev.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * par("omd")[3]) # in y coordinates -y.mid.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space -y.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) # in y coordinates -y.mid.bottom.fig.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] / 2) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space -y.bottom.fig.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3]) # in y coordinates -y.mid.top.fig.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space -y.top.fig.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4])) # in y coordinates -y.top.plot.region <- par("usr")[4] # in y coordinates, top of the plot region (according to y scale) -y.bottom.plot.region <- par("usr")[3] # in y coordinates, bottom of the plot region (according to y scale) -y.mid.plot.region <- ((par("usr")[3] + par("usr")[4]) / 2) # in x coordinates, right of the plot region (according to x scale) -} -if(any(sapply(FUN = all.equal, c(1, 3), x.side) == TRUE)){ -par(xpd=FALSE, xaxt="s") -if(is.null(x.categ) & x.log.scale == TRUE){ -if(any(par()$xaxp[1:2] == 0L)){ # any(sapply(FUN = all.equal, par()$xaxp[1:2], 0) == TRUE) not used because we strictly need zero as a result. Beware: write "== TRUE", because the result is otherwise character and a warning message appears using any() -if(par()$xaxp[1] == 0L){ # isTRUE(all.equal(par()$xaxp[1], 0)) not used because we strictly need zero as a result -par(xaxp = c(10^-30, par()$xaxp[2:3])) # because log10(par()$xaxp[1] == 0) == -Inf -} -if(par()$xaxp[2] == 0L){ # isTRUE(all.equal(par()$xaxp[1], 0)) not used because we strictly need zero as a result -par(xaxp = c(par()$xaxp[1], 10^-30, par()$xaxp[3])) # because log10(par()$xaxp[2] == 0) == -Inf -} -} -axis(side = x.side, at = c(10^par()$usr[1], 10^par()$usr[2]), labels=rep("", 2), lwd=1, lwd.ticks = 0) # draw the axis line -mtext(side = x.side, text = x.lab, line = x.dist.legend / 0.2, las = 0, cex = x.label.size) -par(tcl = -par()$mgp[2] * sec.tick.length) # length of the secondary ticks are reduced -suppressWarnings(rug(10^outer(c((log10(par("xaxp")[1]) -1):log10(par("xaxp")[2])), log10(1:10), "+"), ticksize = NA, side = x.side)) # ticksize = NA to allow the use of par()$tcl value -par(tcl = -par()$mgp[2] * tick.length) # back to main ticks -axis(side = x.side, at = c(1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10), labels = expression(10^-15, 10^-14, 10^-13, 10^-12, 10^-11, 10^-10, 10^-9, 10^-8, 10^-7, 10^-6, 10^-5, 10^-4, 10^-3, 10^-2, 10^-1, 10^0, 10^1, 10^2, 10^3, 10^4, 10^5, 10^6, 10^7, 10^8, 10^9, 10^10), lwd = 0, lwd.ticks = 1, cex.axis = x.axis.size) -x.text <- 10^par("usr")[2] -}else if(is.null(x.categ) & x.log.scale == FALSE){ -axis(side=x.side, at=c(par()$usr[1], par()$usr[2]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line -axis(side=x.side, at=round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), cex.axis = x.axis.size) # axis(side=x.side, at=round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), labels = format(round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), big.mark=','), cex.axis = x.axis.size) # to get the 1000 comma separator -mtext(side = x.side, text = x.lab, line = x.dist.legend / 0.2, las = 0, cex = x.label.size) -if(x.nb.inter.tick > 0){ -inter.tick.unit <- (par("xaxp")[2] - par("xaxp")[1]) / par("xaxp")[3] -par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced -suppressWarnings(rug(seq(par("xaxp")[1] - 10 * inter.tick.unit, par("xaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + x.nb.inter.tick)), ticksize = NA, x.side)) # ticksize = NA to allow the use of par()$tcl value -par(tcl = -par()$mgp[2] * tick.length) # back to main ticks -} -x.text <- par("usr")[2] -}else if(( ! is.null(x.categ)) & x.log.scale == FALSE){ -if(is.null(x.categ.pos)){ -x.categ.pos <- 1:length(x.categ) -}else if(length(x.categ.pos) != length(x.categ)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.categ.pos MUST BE THE SAME LENGTH AS x.categ") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -par(xpd = TRUE) -if(isTRUE(all.equal(x.side, 1))){ #isTRUE(all.equal(x.side, 1)) is similar to x.side == 1L but deals with float -segments(x0 = x.left.plot.region, x1 = x.right.plot.region, y0 = y.bottom.plot.region, y1 = y.bottom.plot.region) # draw the line of the axis -text(x = x.categ.pos, y = y.mid.bottom.fig.region, labels = x.categ, srt = text.angle, cex = x.axis.size) -}else if(isTRUE(all.equal(x.side, 3))){ #isTRUE(all.equal(x.side, 1)) is similar to x.side == 3L but deals with float -segments(x0 = x.left.plot.region, x1 = x.right.plot.region, y0 = y.top.plot.region, y1 = y.top.plot.region) # draw the line of the axis -text(x = x.categ.pos, y = y.mid.top.fig.region, labels = x.categ, srt = text.angle, cex = x.axis.size) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": ARGUMENT x.side CAN ONLY BE 1 OR 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -par(xpd = FALSE) -x.text <- par("usr")[2] -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": PROBLEM WITH THE x.side (", x.side ,") OR x.log.scale (", x.log.scale,") ARGUMENTS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -x.text <- par("usr")[2] -} -if(any(sapply(FUN = all.equal, c(2, 4), y.side) == TRUE)){ -par(xpd=FALSE, yaxt="s") -if(is.null(y.categ) & y.log.scale == TRUE){ -if(any(par()$yaxp[1:2] == 0L)){ # any(sapply(FUN = all.equal, par()$yaxp[1:2], 0) == TRUE) not used because we strictly need zero as a result. Beware: write "== TRUE", because the result is otherwise character and a warning message appears using any() -if(par()$yaxp[1] == 0L){ # strict zero needed -par(yaxp = c(10^-30, par()$yaxp[2:3])) # because log10(par()$yaxp[1] == 0) == -Inf -} -if(par()$yaxp[2] == 0L){ # strict zero needed -par(yaxp = c(par()$yaxp[1], 10^-30, par()$yaxp[3])) # because log10(par()$yaxp[2] == 0) == -Inf -} -} -axis(side=y.side, at=c(10^par()$usr[3], 10^par()$usr[4]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line -par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced -suppressWarnings(rug(10^outer(c((log10(par("yaxp")[1])-1):log10(par("yaxp")[2])), log10(1:10), "+"), ticksize = NA, side = y.side)) # ticksize = NA to allow the use of par()$tcl value -par(tcl = -par()$mgp[2] * tick.length) # back to main tick length -axis(side = y.side, at = c(1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10), labels = expression(10^-15, 10^-14, 10^-13, 10^-12, 10^-11, 10^-10, 10^-9, 10^-8, 10^-7, 10^-6, 10^-5, 10^-4, 10^-3, 10^-2, 10^-1, 10^0, 10^1, 10^2, 10^3, 10^4, 10^5, 10^6, 10^7, 10^8, 10^9, 10^10), lwd = 0, lwd.ticks = 1, cex.axis = y.axis.size) -y.text <- 10^(par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) -mtext(side = y.side, text = y.lab, line = y.dist.legend / 0.2, las = 0, cex = y.label.size) -}else if(is.null(y.categ) & y.log.scale == FALSE){ -axis(side=y.side, at=c(par()$usr[3], par()$usr[4]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line -axis(side=y.side, at=round(seq(par()$yaxp[1], par()$yaxp[2], length.out=par()$yaxp[3]+1), 2), cex.axis = y.axis.size) -mtext(side = y.side, text = y.lab, line = y.dist.legend / 0.2, las = 0, cex = y.label.size) -if(y.nb.inter.tick > 0){ -inter.tick.unit <- (par("yaxp")[2] - par("yaxp")[1]) / par("yaxp")[3] -par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced -suppressWarnings(rug(seq(par("yaxp")[1] - 10 * inter.tick.unit, par("yaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + y.nb.inter.tick)), ticksize = NA, side=y.side)) # ticksize = NA to allow the use of par()$tcl value -par(tcl = -par()$mgp[2] * tick.length) # back to main tick length -} -y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) -}else if(( ! is.null(y.categ)) & y.log.scale == FALSE){ -if(is.null(y.categ.pos)){ -y.categ.pos <- 1:length(y.categ) -}else if(length(y.categ.pos) != length(y.categ)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.categ.pos MUST BE THE SAME LENGTH AS y.categ") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -axis(side = y.side, at = y.categ.pos, labels = rep("", length(y.categ)), lwd=0, lwd.ticks=1) # draw the line of the axis -par(xpd = TRUE) -if(isTRUE(all.equal(y.side, 2))){ #isTRUE(all.equal(y.side, 2)) is similar to y.side == 2L but deals with float -text(x = x.mid.left.fig.region, y = y.categ.pos, labels = y.categ, srt = text.angle, cex = y.axis.size) -}else if(isTRUE(all.equal(y.side, 4))){ # idem -text(x = x.mid.right.fig.region, y = y.categ.pos, labels = y.categ, srt = text.angle, cex = y.axis.size) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": ARGUMENT y.side CAN ONLY BE 2 OR 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -par(xpd = FALSE) -y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": PROBLEM WITH THE y.side (", y.side ,") OR y.log.scale (", y.log.scale,") ARGUMENTS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) -} -par(xpd=NA) -text(x = x.mid.right.fig.region, y = y.text, corner.text, adj=c(1, 1.1), cex = corner.text.size) # text at the topright corner. Replace x.right.fig.region by x.text if text at the right edge of the plot region -if(just.label.add == TRUE & isTRUE(all.equal(x.side, 0)) & x.lab != ""){ -text(x = x.mid.plot.region, y = y.mid.bottom.fig.region, x.lab, adj=c(0.5, 0.5), cex = x.label.size) # x label -} -if(just.label.add == TRUE & isTRUE(all.equal(y.side, 0)) & y.lab != ""){ -text(x = y.mid.plot.region, y = x.mid.left.fig.region, y.lab, adj=c(0.5, 0.5), cex = y.label.size) # x label -} -par(xpd=FALSE) -if(par.reset == TRUE){ -tempo.par <- fun_open(pdf = FALSE, return.output = TRUE) -invisible(dev.off()) # close the new window -if( ! is.null(custom.par)){ -if( ! names(custom.par) %in% names(tempo.par$ini.par)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": custom.par ARGUMENT SHOULD HAVE THE NAMES OF THE COMPARTMENT LIST COMING FROM THE par() LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -par(custom.par) -text <- c(text, "\nGRAPH PARAMETERS SET TO VALUES DEFINED BY custom.par ARGUMENT\n") -}else{ -par(tempo.par$ini.par) -text <- c(text, "\nGRAPH PARAMETERS RESET TO par() DEFAULT VALUES\n") -} -} -output <- list(x.mid.left.dev.region = x.mid.left.dev.region, x.left.dev.region = x.left.dev.region, x.mid.right.dev.region = x.mid.right.dev.region, x.right.dev.region = x.right.dev.region, x.mid.left.fig.region = x.mid.left.fig.region, x.left.fig.region = x.left.fig.region, x.mid.right.fig.region = x.mid.right.fig.region, x.right.fig.region = x.right.fig.region, x.left.plot.region = x.left.plot.region, x.right.plot.region = x.right.plot.region, x.mid.plot.region = x.mid.plot.region, y.mid.bottom.dev.region = y.mid.bottom.dev.region, y.bottom.dev.region = y.bottom.dev.region, y.mid.top.dev.region = y.mid.top.dev.region, y.top.dev.region = y.top.dev.region, y.mid.bottom.fig.region = y.mid.bottom.fig.region, y.bottom.fig.region = y.bottom.fig.region, y.mid.top.fig.region = y.mid.top.fig.region, y.top.fig.region = y.top.fig.region, y.top.plot.region = y.top.plot.region, y.bottom.plot.region = y.bottom.plot.region, y.mid.plot.region = y.mid.plot.region, text = text) -return(output) -} - - -######## fun_close() #### close specific graphic windows - - -fun_close <- function(kind = "pdf", return.text = FALSE){ -# AIM -# close only specific graphic windows (devices) -# ARGUMENTS: -# kind: vector, among c("windows", "quartz", "x11", "X11", "pdf", "bmp", "png", "tiff"), indicating the kind of graphic windows (devices) to close. BEWARE: either "windows", "quartz", "x11" or "X11" means that all the X11 GUI graphics devices will be closed, whatever the OS used -# return.text: print text regarding the kind parameter and the devices that were finally closed? -# RETURN -# text regarding the kind parameter and the devices that were finally closed -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# windows() ; windows() ; pdf() ; dev.list() ; fun_close(kind = c("pdf", "x11"), return.text = TRUE) ; dev.list() -# DEBUGGING -# kind = c("windows", "pdf") ; return.text = FALSE # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = kind, options = c("windows", "quartz", "x11", "X11", "pdf", "bmp", "png", "tiff"), fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.text, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -text <- paste0("THE REQUIRED KIND OF GRAPHIC DEVICES TO CLOSE ARE ", paste(kind, collapse = " ")) -if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows -if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ -tempo <- kind %in% c("windows", "quartz", "x11", "X11") -kind[tempo] <- "windows" # term are replaced by what is displayed when using a <- dev.list() ; names(a) -} -}else if(Sys.info()["sysname"] == "Linux"){ -if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ -tempo.device <- suppressWarnings(try(X11(), silent = TRUE))[] # open a X11 window to try to recover the X11 system used -if( ! is.null(tempo.device)){ -text <- paste0(text, "\nCANNOT CLOSE GUI GRAPHIC DEVICES AS REQUIRED BECAUSE THIS LINUX SYSTEM DOES NOT HAVE IT") -}else{ -tempo <- kind %in% c("windows", "quartz", "x11", "X11") -kind[tempo] <- names(dev.list()[length(dev.list())]) # term are replaced by what is displayed when using a <- dev.list() ; names(a) -invisible(dev.off()) # close the X11 opened by tempo -} -} -}else{ # for macOS -if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ -tempo <- kind %in% c("windows", "quartz", "x11", "X11") -kind[tempo] <- "quartz" # term are replaced by what is displayed when using a <- dev.list() ; names(a) -} -} -kind <- unique(kind) -if(length(dev.list()) != 0){ -for(i in length(names(dev.list())):1){ -if(names(dev.list())[i] %in% kind){ -text <- paste0(text, "\n", names(dev.list())[i], " DEVICE NUMBER ", dev.list()[i], " HAS BEEN CLOSED") -invisible(dev.off(dev.list()[i])) -} -} -} -if(return.text == TRUE){ -return(text) -} -} - - -################ Standard graphics - - -######## fun_empty_graph() #### text to display for empty graphs - - - - - -fun_empty_graph <- function( -text = NULL, -text.size = 1, -title = NULL, -title.size = 1.5 -){ -# AIM -# display an empty plot with a text in the middle of the window (for instance to specify that no plot can be drawn) -# ARGUMENTS -# text: character string of the message to display -# text.size: numeric value of the text size -# title: character string of the graph title -# title.size: numeric value of the title size (in points) -# RETURN -# an empty plot -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# simple example -# fun_empty_graph(text = "NO GRAPH") -# white page -# fun_empty_graph() # white page -# all the arguments -# fun_empty_graph(text = "NO GRAPH", text.size = 2, title = "GRAPH1", title.size = 1) -# DEBUGGING -# text = "NO GRAPH" ; title = "GRAPH1" ; text.size = 1 -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if( ! is.null(text)){ -tempo <- fun_check(data = text, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(title)){ -tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = title.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -ini.par <- par(no.readonly = TRUE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened -par(ann=FALSE, xaxt="n", yaxt="n", mar = rep(1, 4), bty = "n", xpd = NA) -plot(1, 1, type = "n") # no display with type = "n" -x.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) -y.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) -if( ! is.null(text)){ -text(x = 1, y = 1, labels = text, cex = text.size) -} -if( ! is.null(title)){ -text(x = x.left.dev.region, y = y.top.dev.region, labels = title, adj=c(0, 1), cex = title.size) -} -par(ini.par) -} - - -################ gg graphics - - -######## fun_gg_palette() #### ggplot2 default color palette - - - - - -fun_gg_palette <- function(n, kind = "std"){ -# AIM -# provide colors used by ggplot2 -# the interest is to use another single color that is not the red one used by default -# for ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html -# ARGUMENTS -# n: number of groups on the graph -# kind: either "std" for standard gg colors, "dark" for darkened gg colors, or "light" for pastel gg colors -# RETURN -# the vector of hexadecimal colors -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# output of the function -# fun_gg_palette(n = 2) -# the ggplot2 palette when asking for 7 different colors -# plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7)) -# selection of the 5th color of the ggplot2 palette made of 7 different colors -# plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7)[5]) -# the ggplot2 palette made of 7 darkened colors -# plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7, kind = "dark")) -# the ggplot2 palette made of 7 lighten colors -# plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7, kind = "light")) -# DEBUGGING -# n = 0 -# kind = "std" -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = n, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & isTRUE(all.equal(n, 0))){ # isTRUE(all.equal(n, 0))) is similar to n == 0 but deals with float -tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A NON ZERO INTEGER. HERE IT IS: ", paste(n, collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -tempo <- fun_check(data = kind, options = c("std", "dark", "light"), length = 1, fun.name = function.name) ; eval(ee) -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -hues = seq(15, 375, length = n + 1) -hcl(h = hues, l = if(kind == "std"){65}else if(kind == "dark"){35}else if(kind == "light"){85}, c = 100)[1:n] -} - - -######## fun_gg_just() #### ggplot2 justification of the axis labeling, depending on angle - - - - - -fun_gg_just <- function(angle, pos, kind = "axis"){ -# AIM -# provide correct justification for text labeling, depending on the chosen angle -# WARNINGS -# justification behave differently on plot, depending whether it is used for annotayed text or for axis labelling. Indeed the latter has labelling constrained -# Of note, a bug in ggplot2: vjust sometimes does not work, i.e., the same justification result is obtained whatever the value used. This is the case with angle = 90, pos = "top", kind = "axis". While everything is fine with angle = 90, pos = "bottom", kind = "axis". At least, everything seems fine for kind = "axis" and pos = c("left", "bottom") -# ARGUMENTS -# angle: integer value of the text angle, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. -# pos: where text is? Either "top", "right", "bottom" or "left" of the elements to justify from -# kind: kind of text? Either "axis" or "text". In the first case, the pos argument refers to the axis position, and in the second to annotated text (using ggplot2::annotate() or ggplot2::geom_text()) -# RETURN -# a list containing: -# $angle: the submitted angle (value potentially reduced to fit the [-360 ; 360] interval, e.g., 460 -> 100, without impact on the final angle displayed) -# $pos: the selected position (argument pos) -# $kind: the selected kind of text (argument kind) -# $hjust: the horizontal justification -# $vjust: the vertical justification -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_gg_just(angle = 45, pos = "bottom") -# fun_gg_just(angle = (360*2 + 45), pos = "left") -# output <- fun_gg_just(angle = 45, pos = "bottom") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)) -# output <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.y = ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)) + ggplot2::coord_flip() -# output1 <- fun_gg_just(angle = 90, pos = "bottom") ; output2 <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = output1$angle, hjust = output1$hjust, vjust = output1$vjust), axis.text.y = ggplot2::element_text(angle = output2$angle, hjust = output2$hjust, vjust = output2$vjust)) -# output <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1, km = 1, bird = "pigeon", stringsAsFactors = FALSE) ; ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point() + ggplot2::geom_text(mapping = ggplot2::aes(label = bird), angle = output$angle, hjust = output$hjust, vjust = output$vjust) -# obs1 <- data.frame(time = 1:10, km = 1:10, bird = c(NA, NA, NA, "pigeon", NA, "cat", NA, NA, NA, NA), stringsAsFactors = FALSE) ; fun_open(width = 4, height = 4) ; for(i0 in c("text", "axis")){for(i1 in c("top", "right", "bottom", "left")){for(i2 in c(0, 45, 90, 135, 180, 225, 270, 315, 360)){output <- fun_gg_just(angle = i2, pos = i1, kind = i0) ; title <- paste0("kind: ", i0, " | pos: ", i1, " | angle = ", i2, " | hjust: ", output$hjust, " | vjust: ", output$vjust) ; if(i0 == "text"){print(ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point(color = fun_gg_palette(1), alpha = 0.5) + ggplot2::ggtitle(title) + ggplot2::geom_text(mapping = ggplot2::aes(label = bird), angle = output$angle, hjust = output$hjust, vjust = output$vjust) + ggplot2::theme(title = ggplot2::element_text(size = 5)))}else{print(ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point(color = fun_gg_palette(1), alpha = 0.5) + ggplot2::ggtitle(title) + ggplot2::geom_text(mapping = ggplot2::aes(label = bird)) + ggplot2::scale_x_continuous(position = ifelse(i1 == "top", "top", "bottom")) + ggplot2::scale_y_continuous(position = ifelse(i1 == "right", "right", "left")) + ggplot2::theme(title = ggplot2::element_text(size = 5), axis.text.x = if(i1 %in% c("top", "bottom")){ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)}, axis.text.y = if(i1 %in% c("right", "left")){ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)}))}}}} ; dev.off() -# DEBUGGING -# angle = 45 ; pos = "left" ; kind = "axis" -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument primary checking -# arg with no default values -mandat.args <- c( -"angle", -"pos" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# using fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = angle, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = pos, options = c("left", "top", "right", "bottom"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = kind, options = c("axis", "text"), length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <- c( -"angle", -"pos", -"kind" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# end second round of checking and data preparation -# main code -# to get angle between -360 and 360 -while(angle > 360){ -angle <- angle - 360 -} -while(angle < -360){ -angle <- angle + 360 -} -# end to get angle between -360 and 360 -# justifications -if(pos %in% c("bottom", "top")){ -# code below is for if(pos == "bottom"){ -if(any(sapply(FUN = all.equal, c(-360, -180, 0, 180, 360), angle) == TRUE)){ # equivalent of angle == -360 | angle == -180 | angle == 0 | angle == 180 | angle == 360 but deals with floats -hjust <- 0.5 -if(kind == "text"){ -if(any(sapply(FUN = all.equal, c(-360, 0, 360), angle) == TRUE)){ -vjust <- 1 -}else if(any(sapply(FUN = all.equal, c(-180, 180), angle) == TRUE)){ -vjust <- 0 -} -}else{ -vjust <- 0.5 -} -}else if(any(sapply(FUN = all.equal, c(-270, 90), angle) == TRUE)){ -hjust <- 1 -vjust <- 0.5 -}else if(any(sapply(FUN = all.equal, c(-90, 270), angle) == TRUE)){ -hjust <- 0 -vjust <- 0.5 -}else if((angle > -360 & angle < -270) | (angle > 0 & angle < 90)){ -hjust <- 1 -vjust <- 1 -}else if((angle > -270 & angle < -180) | (angle > 90 & angle < 180)){ -hjust <- 1 -vjust <- 0 -}else if((angle > -180 & angle < -90) | (angle > 180 & angle < 270)){ -hjust <- 0 -vjust <- 0 -if(kind == "text" & pos == "top"){ -hjust <- 1 -} -}else if((angle > -90 & angle < 0) | (angle > 270 & angle < 360)){ -hjust <- 0 -vjust <- 1 -} -if(pos == "top"){ -if( ! ((angle > -180 & angle < -90) | (angle > 180 & angle < 270))){ -hjust <- 1 - hjust -} -vjust <- 1 - vjust -} -}else if(pos %in% c("left", "right")){ -# code below is for if(pos == "left"){ -if(any(sapply(FUN = all.equal, c(-270, -90, 90, 270), angle) == TRUE)){ # equivalent of angle == -270 | angle == -90 | angle == 90 | angle == 270 but deals with floats -hjust <- 0.5 -if(kind == "text"){ -if(any(sapply(FUN = all.equal, c(-90, 90), angle) == TRUE)){ -vjust <- 0 -}else if(any(sapply(FUN = all.equal, c(-270, 270), angle) == TRUE)){ -vjust <- 1 -} -}else{ -vjust <- 0.5 -} -}else if(any(sapply(FUN = all.equal, c(-360, 0, 360), angle) == TRUE)){ -hjust <- 1 -vjust <- 0.5 -}else if(any(sapply(FUN = all.equal, c(-180, 180), angle) == TRUE)){ -hjust <- 0 -vjust <- 0.5 -}else if((angle > -360 & angle < -270) | (angle > 0 & angle < 90)){ -hjust <- 1 -vjust <- 0 -}else if((angle > -270 & angle < -180) | (angle > 90 & angle < 180)){ -hjust <- 0 -vjust <- 0 -}else if((angle > -180 & angle < -90) | (angle > 180 & angle < 270)){ -hjust <- 0 -vjust <- 1 -}else if((angle > -90 & angle < 0) | (angle > 270 & angle < 360)){ -hjust <- 1 -vjust <- 1 -} -if(pos == "right"){ -hjust <- 1 - hjust -if( ! (((angle > -270 & angle < -180) | (angle > 90 & angle < 180)) | ((angle > -180 & angle < -90) | (angle > 180 & angle < 270)))){ -vjust <- 1 - vjust -} -} -} -# end justifications -output <- list(angle = angle, pos = pos, kind = kind, hjust = hjust, vjust = vjust) -return(output) -} - - -######## fun_gg_get_legend() #### get the legend of ggplot objects - - - - - -fun_gg_get_legend <- function(ggplot_built, fun.name = NULL, lib.path = NULL){ -# AIM -# get legend of ggplot objects -# # from https://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot -# ARGUMENTS -# ggplot_built: a ggplot build object -# fun.name: single character string indicating the name of the function using fun_gg_get_legend() for warning and error messages. Ignored if NULL -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# RETURN -# a list of class c("gtable", "gTree", "grob", "gDesc"), providing legend information of ggplot_built objet, or NULL if the ggplot_built object has no legend -# REQUIRED PACKAGES -# ggplot2 -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# EXAMPLES -# Simple example -# obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time, fill = group)) ; fun_gg_get_legend(ggplot_built = ggplot2::ggplot_build(p)) -# Error message because no legend in the ggplot -# obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time)) ; fun_gg_get_legend(ggplot_built = ggplot2::ggplot_build(p)) -# DEBUGGING -# obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time)) ; ggplot_built = ggplot2::ggplot_build(p) ; fun.name = NULL ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -req.function <- c( -"fun_check", -"fun_pack" -) -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED ", i1, "() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end required function checking -# argument primary checking -# arg with no default values -mandat.args <- c( -"ggplot_built" -) -tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# using fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = ggplot_built, class = "ggplot_built", mode = "list", fun.name = function.name) ; eval(ee) -if( ! is.null(fun.name)){ -tempo <- fun_check(data = fun.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -} -if( ! is.null(arg.check)){ -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -} -# end using fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking -# management of NA -if(any(is.na(ggplot_built)) | any(is.na(fun.name)) | any(is.na(lib.path))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": NO ARGUMENT CAN HAVE NA VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA -# management of NULL -if(is.null(ggplot_built)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nggplot_built ARGUMENT CANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL -if( ! is.null(lib.path)){ -if( ! all(dir.exists(lib.path))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end second round of checking -# package checking -fun_pack(req.package = c("ggplot2"), lib.path = lib.path) -# end package checking -# main code -win.nb <- dev.cur() -pdf(file = NULL) -tmp <- ggplot2::ggplot_gtable(ggplot_built) -# BEWARE with ggplot_gtable : open a blanck device https://stackoverflow.com/questions/17012518/why-does-this-r-ggplot2-code-bring-up-a-blank-display-device -invisible(dev.off()) -if(win.nb > 1){ # to go back to the previous active device, if == 1 means no opened device -dev.set(win.nb) -} -leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box") -if(length(leg) == 0L){ -legend <- NULL -}else{ -legend <- tmp$grobs[[leg]] -} -return(legend) -} - - -######## fun_gg_point_rast() #### ggplot2 raster scatterplot layer - - - - - -fun_gg_point_rast <- function( -data = NULL, -mapping = NULL, -stat = "identity", -position = "identity", -..., -na.rm = FALSE, -show.legend = NA, -inherit.aes = TRUE, -raster.width = NULL, -raster.height = NULL, -raster.dpi = 300, -inactivate = TRUE, -lib.path = NULL -){ -# AIM -# equivalent to ggplot2::geom_point() but in raster mode -# use it like ggplot2::geom_point() with the main raster.dpi additional argument -# WARNINGS -# can be long to generate the plot -# use a square plot region. Otherwise, the dots will have ellipsoid shape -# solve the transparency problems with some GUI -# this function is derived from the geom_point_rast() function, created by Viktor Petukhov , and present in the ggrastr package (https://rdrr.io/github/VPetukhov/ggrastr/src/R/geom-point-rast.R, MIT License, Copyright (c) 2017 Viktor Petukhov). Has been placed here to minimize package dependencies -# ARGUMENTS -# classical arguments of geom_point(), shown here https://rdrr.io/github/VPetukhov/ggrastr/man/geom_point_rast.html -# raster.width : width of the result image (in inches). Default: deterined by the current device parameters -# raster.height: height of the result image (in inches). Default: deterined by the current device parameters -# raster.dpi: resolution of the result image -# inactivate: logical. Inactivate the fun.name argument of the fun_check() function? If TRUE, the name of the fun_check() function in error messages coming from this function. Use TRUE if fun_gg_point_rast() is used like this: eval(parse(text = "fun_gg_point_rast")) -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# RETURN -# a raster scatter plot -# REQUIRED PACKAGES -# ggplot2 -# grid (included in the R installation packages but not automatically loaded) -# Cairo -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# EXAMPLES -# Two pdf in the current directory -# set.seed(1) ; data1 = data.frame(x = rnorm(100000), y = rnorm(10000), stringsAsFactors = TRUE) ; fun_open(pdf.name = "Raster") ; ggplot2::ggplot() + fun_gg_point_rast(data = data1, mapping = ggplot2::aes(x = x, y = y)) ; fun_open(pdf.name = "Vectorial") ; ggplot2::ggplot() + ggplot2::geom_point(data = data1, mapping = ggplot2::aes(x = x, y = y)) ; dev.off() ; dev.off() -# DEBUGGING -# -# function name -if(all(inactivate == FALSE)){ # inactivate has to be used here but will be fully checked below -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -}else if(all(inactivate == TRUE)){ -function.name <- NULL -}else{ -tempo.cat <- paste0("ERROR IN fun_gg_point_rast(): CODE INCONSISTENCY 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if( ! is.null(data)){ -tempo <- fun_check(data = data, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(mapping)){ -tempo <- fun_check(data = mapping, class = "uneval", typeof = "list", fun.name = function.name) ; eval(ee) # aes() is tested -} -# stat and position not tested because too complicate -tempo <- fun_check(data = na.rm, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = show.legend, class = "vector", mode = "logical", length = 1, na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = inherit.aes, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(raster.width)){ -tempo <- fun_check(data = raster.width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -} -if( ! is.null(raster.height)){ -tempo <- fun_check(data = raster.height, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = raster.dpi, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = inactivate, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# package checking -fun_pack(req.package = c("ggplot2"), lib.path = lib.path) -fun_pack(req.package = c("grid"), lib.path = lib.path) -fun_pack(req.package = c("Cairo"), lib.path = lib.path) -# end package checking -# additional functions -DrawGeomPointRast <- function(data, panel_params, coord, na.rm = FALSE, raster.width = NULL, raster.height= NULL, raster.dpi = raster.dpi){ -if (is.null(raster.width)){ -raster.width <- par('fin')[1] -} -if (is.null(raster.height)){ - raster.height <- par('fin')[2] -} -prev_dev_id <- dev.cur() -p <- ggplot2::GeomPoint$draw_panel(data, panel_params, coord) -dev_id <- Cairo::Cairo(type='raster', width = raster.width*raster.dpi, height = raster.height*raster.dpi, dpi = raster.dpi, units = 'px', bg = "transparent")[1] -grid::pushViewport(grid::viewport(width = 1, height = 1)) -grid::grid.points(x = p$x, y = p$y, pch = p$pch, size = p$size, -name = p$name, gp = p$gp, vp = p$vp, draw = T) -grid::popViewport() -cap <- grid::grid.cap() -invisible(dev.off(dev_id)) -invisible(dev.set(prev_dev_id)) -grid::rasterGrob(cap, x = 0, y = 0, width = 1, height = 1, default.units = "native", just = c("left","bottom")) -} -# end additional functions -# main code -GeomPointRast <- ggplot2::ggproto("GeomPointRast", ggplot2::GeomPoint, draw_panel = DrawGeomPointRast) -ggplot2::layer( -data = data, -mapping = mapping, -stat = stat, -geom = GeomPointRast, -position = position, -show.legend = show.legend, -inherit.aes = inherit.aes, -params = list( -na.rm = na.rm, -raster.width = raster.width, -raster.height = raster.height, -raster.dpi = raster.dpi, -... -) -) -# end main code -} - - -######## fun_gg_boxplot() #### ggplot2 boxplot + background dots if required - - - - -######## fun_gg_scatter() #### ggplot2 scatterplot + lines (up to 6 overlays totally) - - - - -######## fun_gg_heatmap() #### ggplot2 heatmap + overlaid mask if required - - -#test plot.margin = margin(up.space.mds, right.space.mds, down.space.mds, left.space.mds, "inches") to set the dim of the region plot ? -# if matrix is full of zero (or same value I guess), heatmap is complicate. Test it and error message - -fun_gg_heatmap <- function( -data1, -legend.name1 = "", -low.color1 = "blue", -mid.color1 = "white", -high.color1 = "red", -limit1 = NULL, -midpoint1 = NULL, -data2 = NULL, -color2 = "black", -alpha2 = 0.5, -invert2 = FALSE, -text.size = 12, -title = "", -title.text.size = 12, -show.scale = TRUE, -rotate = FALSE, -return = FALSE, -plot = TRUE, -add = NULL, -warn.print = FALSE, -lib.path = NULL -){ -# AIM -# ggplot2 heatmap with the possibility to overlay a mask -# see also: -# draw : http://www.sthda.com/english/wiki/ggplot2-quick-correlation-matrix-heatmap-r-software-and-data-visualization -# same range scale : https://stackoverflow.com/questions/44655723/r-ggplot2-heatmap-fixed-scale-color-between-graphs -# for ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html -# ARGUMENTS -# data1: numeric matrix or data frame resulting from the conversion of the numeric matrix by reshape2::melt() -# legend.name1: character string of the data1 heatmap scale legend -# low.color1: character string of the color (i.e., "blue" or "#0000FF") of the lowest scale value -# mid.color1: same as low.color1 but for the middle scale value. If NULL, the middle color is the default color between low.color1 and high.color1. BEWARE: argument midpoint1 is not ignored, even if mid.color1 is NULL, meaning that the default mid color can still be controled -# high.color1: same as low.color1 but for the highest scale value -# limit1: 2 numeric values defining the lowest and higest color scale values. If NULL, take the range of data1 values -# midpoint1: single numeric value defining the value corresponding to the mid.color1 argument. A warning message is returned if midpoint1 does not correspond to the mean of limit1 values, because the color scale is not linear anymore. If NULL, takes the mean of limit1 values. Mean of data1, instead of mean of limit1, can be used here if required -# data2: binary mask matrix (made of 0 and 1) of same dimension as data1 or a data frame resulting from the conversion of the binary mask matrix by reshape2::melt(). Value 1 of data2 will correspond to color2 argument (value 0 will be NA color), and the opposite if invert2 argument is TRUE (inverted mask) -# color2: color of the 1 values of the binary mask matrix. The 0 values will be color NA -# alpha2: numeric value (from 0 to 1) of the mask transparency -# invert2: logical. Invert the mask (1 -> 0 and 0 -> 1)? -# text.size: numeric value of the size of the texts in scale -# title: character string of the graph title -# title.text.size: numeric value of the title size (in points) -# show.scale: logical. Show color scale? -# rotate: logical. Rotate the heatmap 90° clockwise? -# return: logical. Return the graph parameters? -# plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting -# add: character string allowing to add more ggplot2 features (dots, lines, themes, etc.). BEWARE: (1) must start with "+" just after the simple or double opening quote (no space, end of line, carriage return, etc., allowed), (2) must finish with ")" just before the simple or double closing quote (no space, end of line, carriage return, etc., allowed) and (3) each function must be preceded by "ggplot2::" (for instance: "ggplot2::coord_flip()). If the character string contains the "ggplot2::theme" string, then internal ggplot2 theme() and theme_classic() functions will be inactivated to be reused by add. BEWARE: handle this argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions -# warn.print: logical. Print warnings at the end of the execution? No print if no warning messages -# lib.path: absolute path of the required packages, if not in the default folders -# RETURN -# a heatmap if plot argument is TRUE -# a list of the graph info if return argument is TRUE: -# $data: a list of the graphic info -# $axes: a list of the axes info -# $scale: the scale info (lowest, mid and highest values) -# $warn: the warning messages. Use cat() for proper display. NULL if no warning -# REQUIRED PACKAGES -# ggplot2 -# reshape2 -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# fun_round() -# EXAMPLES -# fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), title = "GRAPH 1") -# fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), return = TRUE) -# fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), legend.name1 = "VALUE", title = "GRAPH 1", text.size = 5, data2 = matrix(rep(c(1,0,0,0), 4), ncol = 4), invert2 = FALSE, return = TRUE) -# diagonal matrix -# fun_gg_heatmap(data1 = matrix(c(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1), ncol = 4)) -# fun_gg_heatmap(data1 = reshape2::melt(matrix(c(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1), ncol = 4))) -# error message -# fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), data2 = matrix(rep(c(1,0,0,0), 5), ncol = 5)) -# fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), data2 = reshape2::melt(matrix(rep(c(1,0,0,0), 4), ncol = 4))) -# fun_gg_heatmap(data1 = reshape2::melt(matrix(1:16, ncol = 4)), data2 = reshape2::melt(matrix(rep(c(1,0,0,0), 4), ncol = 4))) -# DEBUGGING -# data1 = matrix(1:16, ncol = 4) ; legend.name1 = "" ; low.color1 = "blue" ; mid.color1 = "white" ; high.color1 = "red" ; limit1 = NULL ; midpoint1 = NULL ; data2 = matrix(rep(c(1,0,0,0), 4), ncol = 4) ; color2 = "black" ; alpha2 = 0.5 ; invert2 = FALSE ; text.size = 12 ; title = "" ; title.text.size = 12 ; show.scale = TRUE ; rotate = FALSE ; return = FALSE ; plot = TRUE ; add = NULL ; warn.print = TRUE ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_round", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_round() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# no reserved words required for this function -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if(all(is.matrix(data1))){ -tempo <- fun_check(data = data1, class = "matrix", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -}else if(all(is.data.frame(data1))){ -tempo <- fun_check(data = data1, class = "data.frame", length = 3, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -# structure of reshape2::melt() data frame -tempo <- fun_check(data = data1[, 1], data.name = "COLUMN 1 OF data1 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = data1[, 2], data.name = "COLUMN 2 OF data1 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = data1[, 3], data.name = "COLUMN 3 OF data1 (reshape2::melt() DATA FRAME)", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A NUMERIC MATRIX OR A DATA FRAME OUTPUT OF THE reshape::melt() FUNCTION") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = legend.name1, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = low.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (all(low.color1 %in% colors() | grepl(pattern = "^#", low.color1)))){ # check that all strings of low.color1 start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": low.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(mid.color1)){ -tempo <- fun_check(data = mid.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (all(mid.color1 %in% colors() | grepl(pattern = "^#", mid.color1)))){ # check that all strings of mid.color1 start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": mid.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = high.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (all(high.color1 %in% colors() | grepl(pattern = "^#", high.color1)))){ # check that all strings of high.color1 start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": high.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(limit1)){ -tempo <- fun_check(data = limit1, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & any(limit1 %in% c(Inf, -Inf))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": limit1 ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -if( ! is.null(midpoint1)){ -tempo <- fun_check(data = midpoint1, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -} -if( ! is.null(data2)){ -if(all(is.matrix(data2))){ -tempo <- fun_check(data = data2, class = "matrix", mode = "numeric", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! all(unique(data2) %in% c(0,1))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX IN data2 MUST BE MADE OF 0 AND 1 ONLY (MASK MATRIX)") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & all(is.matrix(data1)) & ! identical(dim(data1), dim(data2))){ # matrix and matrix -tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX DIMENSION IN data2 MUST BE IDENTICAL AS MATRIX DIMENSION IN data1. HERE IT IS RESPECTIVELY:\n", paste(dim(data2), collapse = " "), "\n", paste(dim(data1), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & all(is.data.frame(data1)) & nrow(data1) != prod(dim(data2))){ # reshape2 and matrix -tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME IN data2 MUST HAVE ROW NUMBER EQUAL TO PRODUCT OF DIMENSIONS OF data1 MATRIX. HERE IT IS RESPECTIVELY:\n", paste(nrow(data1), collapse = " "), "\n", paste(prod(dim(data2)), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else if(all(is.data.frame(data2))){ -tempo <- fun_check(data = data2, class = "data.frame", length = 3, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -# structure of reshape2::melt() data frame -tempo <- fun_check(data = data2[, 1], data.name = "COLUMN 1 OF data2 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = data2[, 2], data.name = "COLUMN 2 OF data2 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = data2[, 3], data.name = "COLUMN 3 OF data2 (reshape2::melt() DATA FRAME)", mode = "numeric", fun.name = function.name) ; eval(ee) -} -if(tempo$problem == FALSE & ! all(unique(data2[, 3]) %in% c(0,1))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THIRD COLUMN OF DATA FRAME IN data2 MUST BE MADE OF 0 AND 1 ONLY (MASK DATA FRAME)") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & all(is.data.frame(data1)) & ! identical(dim(data1), dim(data2))){ # data frame and data frame -tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME DIMENSION IN data2 MUST BE IDENTICAL TO DATA FRAME DIMENSION IN data1. HERE IT IS RESPECTIVELY:\n", paste(dim(data2), collapse = " "), "\n", paste(dim(data1), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & all(is.matrix(data1)) & nrow(data2) != prod(dim(data1))){ # reshape2 and matrix -tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME IN data2 MUST HAVE ROW NUMBER EQUAL TO PRODUCT OF DIMENSION OF data1 MATRIX. HERE IT IS RESPECTIVELY:\n", paste(nrow(data2), collapse = " "), "\n", paste(prod(dim(data1)), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A NUMERIC MATRIX OR A DATA FRAME OUTPUT OF THE reshape::melt() FUNCTION") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = color2, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (all(color2 %in% colors() | grepl(pattern = "^#", color2)))){ # check that all strings of color2 start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": color2 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = alpha2, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = invert2, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = show.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(add)){ -tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! grepl(pattern = "^\\+", add)){ # check that the add string start by + -tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & ! grepl(pattern = "ggplot2::", add)){ # -tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST CONTAIN \"ggplot2::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & ! grepl(pattern = ")$", add)){ # check that the add string finished by ) -tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# package checking -fun_pack(req.package = c("reshape2", "ggplot2"), lib.path = lib.path) -# end package checking -# main code -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -if(all(is.matrix(data1))){ -data1 <- reshape2::melt(data1) # transform a matrix into a data frame with 2 coordinates columns and the third intensity column -} -if(rotate == TRUE){ -data1[, 1] <- rev(data1[, 1]) -} -if(is.null(limit1)){ -if(any(data1[, 3] %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE THIRD COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -limit1 <- range(data1[, 3], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE limit1 ARGUMENT IS NULL -> RANGE OF data1 ARGUMENT HAS BEEN TAKEN: ", paste(fun_round(limit1), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -if(suppressWarnings(any(limit1 %in% c(Inf, -Inf)))){ -tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED LIMIT CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(is.null(midpoint1)){ -midpoint1 <- mean(limit1, na.rm = TRUE) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE midpoint1 ARGUMENT IS NULL -> MEAN OF limit1 ARGUMENT HAS BEEN TAKEN: ", paste(fun_round(midpoint1), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else if(fun_round(midpoint1, 9) != fun_round(mean(limit1), 9)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE midpoint1 ARGUMENT (", fun_round(mean(midpoint1), 9), ") DOES NOT CORRESPOND TO THE MEAN OF THE limit1 ARGUMENT (", fun_round(mean(limit1), 9), "). COLOR SCALE IS NOT LINEAR") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data2)){ -if(all(is.matrix(data2))){ -data2 <- reshape2::melt(data2) # transform a matrix into a data frame with 2 coordinates columns and the third intensity column -} -if(rotate == TRUE){ -data2[, 1] <- rev(data2[, 1]) -} -data2[, 3] <- factor(data2[, 3]) # to converte continuous scale into discrete scale -} -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 # to facilitate debugging -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggplot()) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data1, mapping = ggplot2::aes_string(x = names(data1)[ifelse(rotate == FALSE, 2, 1)], y = names(data1)[ifelse(rotate == FALSE, 1, 2)], fill = names(data1)[3]), show.legend = show.scale)) # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_gradient2(low = low.color1, high = high.color1, mid = mid.color1, midpoint = midpoint1, limit = limit1, breaks = c(limit1[1], midpoint1, limit1[2]), labels = fun_round(c(limit1[1], midpoint1, limit1[2])), name = legend.name1)) -if( ! is.null(data2)){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data2, mapping = ggplot2::aes_string(x = names(data2)[ifelse(rotate == FALSE, 2, 1)], y = names(data2)[ifelse(rotate == FALSE, 1, 2)], alpha = names(data2)[3]), fill = color2, show.legend = FALSE)) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", values = if(invert2 == FALSE){c(0, alpha2)}else{c(alpha2, 0)}, guide = FALSE)) -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data2, mapping = ggplot2::aes_string(x = names(data2)[ifelse(rotate == FALSE, 2, 1)], y = names(data2)[ifelse(rotate == FALSE, 1, 2)], group = names(data2)[3]), fill = data2[, 3], alpha = alpha2, show.legend = FALSE)) # BEWARE: this does not work if NA present, because geom_raster() has a tendency to complete empty spaces, and thus, behave differently than geom_tile(). See https://github.com/tidyverse/ggplot2/issues/3025 -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_fixed()) # x = y -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_reverse()) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) -add.check <- TRUE -if( ! is.null(add)){ # if add is NULL, then = 0 -if(grepl(pattern = "ggplot2::theme", add) == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT -> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -add.check <- FALSE -} -} -if(add.check == TRUE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_blank(), -axis.title = ggplot2::element_blank(), -axis.text = ggplot2::element_blank(), -axis.ticks = ggplot2::element_blank(), -panel.background = ggplot2::element_blank() -)) -} -if(plot == TRUE){ -# suppressWarnings( -print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add})))) -# ) -}else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -if(return == TRUE){ -output <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))) -output <- output$data -names(output)[1] <- "heatmap" -if( ! is.null(data2)){ -names(output)[2] <- "mask" -} -return(list(data = output, axes = output$layout$panel_params[[1]], scale = c(limit1[1], midpoint1, limit1[2]), warn = warn)) -} -} - - -######## fun_gg_empty_graph() #### text to display for empty graphs - - - - - -fun_gg_empty_graph <- function( -text = NULL, -text.size = 12, -title = NULL, -title.size = 8, -lib.path = NULL -){ -# AIM -# display an empty ggplot2 plot with a text in the middle of the window (for instance to specify that no plot can be drawn) -# ARGUMENTS -# text: character string of the message to display -# text.size: numeric value of the text size (in points) -# title: character string of the graph title -# title.size: numeric value of the title size (in points) -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL -# RETURN -# an empty plot -# REQUIRED PACKAGES -# ggplot2 -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# EXAMPLES -### simple example -# fun_gg_empty_graph(text = "NO GRAPH") -### white page -# fun_gg_empty_graph() -### all the arguments -# fun_gg_empty_graph(text = "NO GRAPH", text.size = 8, title = "GRAPH1", title.size = 10, lib.path = NULL) -# DEBUGGING -# text = "NO GRAPH" ; text.size = 12 ; title = "GRAPH1" ; title.size = 8 ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if( ! is.null(text)){ -tempo <- fun_check(data = text, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(title)){ -tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = title.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# package checking -fun_pack(req.package = c("ggplot2"), lib.path = lib.path) -# end package checking -# main code -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 -# no need loop part -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggplot()) -if( ! is.null(text)){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(data = data.frame(x = 1, y = 1, stringsAsFactors = TRUE), ggplot2::aes(x = x, y = y, label = text), size = text.size)) -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void()) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -plot.title = ggplot2::element_text(size = title.size) # stronger than text -)) -suppressWarnings(print(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))) -} - - -################ Graphic extraction - - -######## fun_trim() #### display values from a quantitative variable and trim according to defined cut-offs - -# Add name of the variable in the graph -# not max and min for boxplot but 1.5IQR -fun_trim <- function( -data, -displayed.nb = NULL, -single.value.display = FALSE, -trim.method = "", -trim.cutoffs = c(0.05, -0.975), -interval.scale.disp = TRUE, -down.space = 0.75, -left.space = 0.75, -up.space = 0.3, -right.space = 0.25, -orient = 1, -dist.legend = 0.37, -box.type = "l", -amplif.label = 1.25, -amplif.axis = 1.25, -std.x.range = TRUE, -std.y.range = TRUE, -cex.pt = 0.2, -col.box = hsv(0.55, -0.8, -0.8), -x.nb.inter.tick = 4, -y.nb.inter.tick = 0, -tick.length = 1, -sec.tick.length = 0.75, -corner.text = "", -amplif.legend = 1, -corner.text.size = 0.75, -trim.return = FALSE -){ -# AIM -# trim and display values from a numeric vector or matrix -# plot 4 graphs: stripchart of values, stripchart of rank of values, histogram and normal QQPlot -# different kinds of intervals are displayed on the top of graphes to facilitate the analysis of the variable and a trimming setting -# the trimming interval chosen is displayed on top of graphs -# both trimmed and not trimmed values are returned in a list -# ARGUMENTS -# data: values to plot (either a numeric vector or a numeric matrix) -# displayed.nb: number of values displayed. If NULL, all the values are displayed. Otherwise, if the number of values is over displayed.nb, then displayed.nb values are displayed after random selection -# single.value.display: provide the 4 graphs if data is made of a single (potentially repeated value)? If FALSE, an empty graph is displayed if data is made of a single (potentially repeated value). And the return list is made of NULL compartments -# trim.method: Write "" if not required. write "mean.sd" if mean +/- sd has to be displayed as a trimming interval (only recommanded for normal distribution). Write "quantile" to display a trimming interval based on quantile cut-offs. No other possibility allowed. See trim.cutoffs below -# trim.cutoffs: 2 values cutoff for the trimming interval displayed, each value between 0 and 1. Not used if trim.method == "".The couple of values c(lower, upper) represents the lower and upper boundaries of the trimming interval (in proportion), which represent the interval of distribution kept (between 0 and 1). Example: trim.cutoffs = c(0.05, 0.975). What is strictly kept for the display is ]lower , upper[, boundaries excluded. Using the "mean.sd" method, 0.025 and 0.975 represent 95% CI which is mean +/- 1.96 * sd -# interval.scale.disp: display sd and quantiles intervals on top of graphs ? -# down.space: lower vertical margin (in inches, mai argument of par()) -# left.space: left horizontal margin (in inches, mai argument of par()) -# up.space: upper vertical margin between plot region and grapical window (in inches, mai argument of par()) -# right.space: right horizontal margin (in inches, mai argument of par()) -# orient: scale number orientation (las argument of par()). 0, always parallel to the axis; 1, always horizontal; 2, always perpendicular to the axis; 3, always vertical -# dist.legend: numeric value that moves axis legends away in inches (first number of mgp argument of par() but in inches thus / 0.2) -# box.type: bty argument of par(). Either "o", "l", "7", "c", "u", "]", the resulting box resembles the corresponding upper case letter. A value of "n" suppresses the box -# amplif.label: increase or decrease the size of the text in legends -# amplif.axis: increase or decrease the size of the scale numbers in axis -# std.x.range: standard range on the x-axis? TRUE (no range extend) or FALSE (4% range extend). Controls xaxs argument of par() (TRUE is xaxs = "i", FALSE is xaxs = "r") -# std.y.range: standard range on the y-axis? TRUE (no range extend) or FALSE (4% range extend). Controls yaxs argument of par() (TRUE is yaxs = "i", FALSE is yaxs = "r") -# cex.pt: size of points in stripcharts (in inches, thus cex.pt will be thereafter / 0.2) -# col.box: color of boxplot -# x.nb.inter.tick: number of secondary ticks between main ticks on x-axis (only if not log scale). Zero means non secondary ticks -# y.nb.inter.tick: number of secondary ticks between main ticks on y-axis (only if not log scale). Zero means non secondary ticks -# tick.length: length of the ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc. 0 means no tick -# sec.tick.length: length of the secondary ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) -# corner.text: text to add at the top right corner of the window -# amplif.legend: increase or decrease the size of the text of legend -# corner.text.size: positive numeric. Increase or decrease the size of the text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 -# trim.return: return the trimmed and non trimmed values? NULL returned for trimmed and non trimmed values if trim.method == "" -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# RETURN -# a list containing: -# $trim.method: correspond to trim.method above -# $trim.cutoffs: correspond to trim.cutoffs above -# $real.trim.cutoffs: the two boundary values (in the unit of the numeric vector or numeric matrix analyzed). NULL -# $trimmed.values: the values outside of the trimming interval as defined in trim.cutoffs above -# $kept.values: the values inside the trimming interval as defined in trim.cutoffs above -# EXAMPLES -# fun_trim(data = c(1:100, 1:10), displayed.nb = NULL, single.value.display = FALSE, trim.method = "mean.sd", trim.cutoffs = c(0.05, 0.975), interval.scale.disp = TRUE, down.space = 0.75, left.space = 0.75, up.space = 0.3, right.space = 0.25, orient = 1, dist.legend = 0.37, box.type = "l", amplif.label = 1.25, amplif.axis = 1.25, std.x.range = TRUE, std.y.range = TRUE, cex.pt = 0.2, col.box = hsv(0.55, 0.8, 0.8), x.nb.inter.tick = 4, y.nb.inter.tick = 0, tick.length = 0.5, sec.tick.length = 0.3, corner.text = "", amplif.legend = 1, corner.text.size = 0.75, trim.return = TRUE) -# DEBUGGING -# data = c(1:100, 1:10) ; displayed.nb = NULL ; single.value.display = FALSE ; trim.method = "quantile" ; trim.cutoffs = c(0.05, 0.975) ; interval.scale.disp = TRUE ; down.space = 1 ; left.space = 1 ; up.space = 0.5 ; right.space = 0.25 ; orient = 1 ; dist.legend = 0.5 ; box.type = "l" ; amplif.label = 1 ; amplif.axis = 1 ; std.x.range = TRUE ; std.y.range = TRUE ; cex.pt = 0.1 ; col.box = hsv(0.55, 0.8, 0.8) ; x.nb.inter.tick = 4 ; y.nb.inter.tick = 0 ; tick.length = 0.5 ; sec.tick.length = 0.3 ; corner.text = "" ; amplif.legend = 1 ; corner.text.size = 0.75 ; trim.return = TRUE # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -# argument checking without fun_check() -if( ! (all(class(data) == "numeric") | all(class(data) == "integer") | (all(class(data) %in% c("matrix", "array")) & base::mode(data) == "numeric"))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A NUMERIC VECTOR OR NUMERIC MATRIX") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking without fun_check() -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -if( ! is.null(displayed.nb)){ -tempo <- fun_check(data = displayed.nb, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(displayed.nb < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": displayed.nb ARGUMENT MUST BE A SINGLE INTEGER VALUE GREATER THAN 1 AND NOT: ", paste(displayed.nb, collapse = " ")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = single.value.display, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = trim.method, options = c("", "mean.sd", "quantile"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = trim.cutoffs, class = "vector", mode = "numeric", length = 2, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = interval.scale.disp, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = down.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = up.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = orient, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.type, options = c("o", "l", "7", "c", "u", "]", "n"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = amplif.label, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = amplif.axis, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = std.x.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = std.y.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = cex.pt, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = col.box, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.nb.inter.tick, class = "integer", length = 1, neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.nb.inter.tick, class = "integer", length = 1, neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = sec.tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = corner.text, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = amplif.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = corner.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = trim.return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -if(all(is.na(data) | ! is.finite(data))){ -tempo.cat <- paste0("ERROR IN fun_trim FUNCTION\ndata ARGUMENT CONTAINS ONLY NA OR Inf") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end argument checking -# main code -if(all(class(data)%in% c("matrix", "array"))){ -data <- as.vector(data) -} -na.nb <- NULL -if(any(is.na(data))){ -na.nb <- sum(c(is.na(data))) -data <- data[ ! is.na(data)] -} -color.cut <- hsv(0.75, 1, 1) # color of interval selected -col.mean <- hsv(0.25, 1, 0.8) # color of interval using mean+/-sd -col.quantile <- "orange" # color of interval using quantiles -quantiles.selection <- c(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.975, 0.99) # quantiles used in axis to help for choosing trimming cutoffs -if(single.value.display == FALSE & length(unique(data)) == 1L){ -par(bty = "n", xaxt = "n", yaxt = "n", xpd = TRUE) -plot(1, pch = 16, col = "white", xlab = "", ylab = "") -text(x = 1, y = 1, paste0("No graphic displayed\nBecause data made of a single different value (", formatC(as.double(table(data))), ")"), cex = 2) -output <- list(trim.method = NULL, trim.cutoffs = NULL, real.trim.cutoffs = NULL, trimmed.values = NULL, kept.values = NULL) -}else{ -output <- list(trim.method = trim.method, trim.cutoffs = trim.cutoffs, real.trim.cutoffs = NULL, trimmed.values = NULL, kept.values = NULL) -fun.rug <- function(sec.tick.length.f = sec.tick.length, x.nb.inter.tick.f = x.nb.inter.tick, y.nb.inter.tick.f = y.nb.inter.tick){ -if(x.nb.inter.tick.f > 0){ -inter.tick.unit <- (par("xaxp")[2] - par("xaxp")[1]) / par("xaxp")[3] -par.ini <- par()[c("xpd", "tcl")] -par(xpd = FALSE) -par(tcl = -par()$mgp[2] * sec.tick.length.f) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) -suppressWarnings(rug(seq(par("xaxp")[1] - 10 * inter.tick.unit, par("xaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + x.nb.inter.tick.f)), ticksize = NA, side = 1)) # ticksize = NA to allow the use of par()$tcl value -par(par.ini) -rm(par.ini) -} -if(y.nb.inter.tick.f > 0){ -inter.tick.unit <- (par("yaxp")[2] - par("yaxp")[1]) / par("yaxp")[3] -par.ini <- par()[c("xpd", "tcl")] -par(xpd = FALSE) -par(tcl = -par()$mgp[2] * sec.tick.length.f) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) -suppressWarnings(rug(seq(par("yaxp")[1] - 10 * inter.tick.unit, par("yaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + y.nb.inter.tick.f)), ticksize = NA, side = 2)) # ticksize = NA to allow the use of par()$tcl value -par(par.ini) -rm(par.ini) -} -} -fun.add.cut <- function(data.f, trim.method.f = trim.method, trim.cutoffs.f = trim.cutoffs, color.cut.f = color.cut, return.f = FALSE){ -# DEBUGGING -# data.f = data ; trim.method.f = "mean.sd"; trim.cutoffs.f = trim.cutoffs ; color.cut.f = color.cut ; return.f = TRUE -real.trim.cutoffs.f <- NULL -if(trim.method.f != ""){ -data.f <- sort(data.f) -par.ini <- par()$xpd -par(xpd = FALSE) -if(trim.method.f == "mean.sd"){ -real.trim.cutoffs.f <- qnorm(trim.cutoffs.f, mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)) -abline(v = qnorm(trim.cutoffs.f, mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), col = color.cut.f) -segments(qnorm(trim.cutoffs.f[1], mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), par()$usr[4] * 0.75, qnorm(trim.cutoffs.f[2], mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), par()$usr[4] * 0.75, col = color.cut.f) -} -if(trim.method.f == "quantile"){ -real.trim.cutoffs.f <- quantile(data.f, probs = trim.cutoffs.f, type = 7, na.rm = TRUE) -abline(v = quantile(data.f, probs = trim.cutoffs.f, type = 7, na.rm = TRUE), col = color.cut.f) -segments(quantile(data.f, probs = trim.cutoffs.f[1], type = 7, na.rm = TRUE), par()$usr[4] * 0.75, quantile(data.f, probs = trim.cutoffs.f[2], type = 7, na.rm = TRUE), par()$usr[4] * 0.75, col = color.cut.f) -} -par(par.ini) -if(return.f == TRUE){ -trimmed.values.f <- data.f[data.f <= real.trim.cutoffs.f[1] | data.f >= real.trim.cutoffs.f[2]] -kept.values.f <- data.f[data.f > real.trim.cutoffs.f[1] & data.f < real.trim.cutoffs.f[2]] -} -}else{ -real.trim.cutoffs.f <- NULL -trimmed.values.f <- NULL -kept.values.f <- NULL -} -if(return.f == TRUE){ -output <- list(trim.method = trim.method.f, trim.cutoffs = trim.cutoffs.f, real.trim.cutoffs = real.trim.cutoffs.f, trimmed.values = trimmed.values.f, kept.values = kept.values.f) -return(output) -} -} -fun.interval.scale.display <- function(data.f, col.quantile.f = col.quantile, quantiles.selection.f = quantiles.selection, col.mean.f = col.mean){ # intervals on top of graphs -par.ini <- par()[c("mgp", "xpd")] -par(mgp = c(0.25, 0.25, 0), xpd = NA) -axis(side = 3, at = c(par()$usr[1], par()$usr[2]), labels = rep("", 2), col = col.quantile.f, lwd.ticks = 0) -par(xpd = FALSE) -axis(side = 3, at = quantile(as.vector(data.f), probs = quantiles.selection.f, type = 7, na.rm = TRUE), labels = quantiles.selection.f, col.axis = col.quantile.f, col = col.quantile.f) -par(mgp = c(1.75, 1.75, 1.5), xpd = NA) -axis(side = 3, at = c(par()$usr[1], par()$usr[2]), labels = rep("", 2), col = col.mean.f, lwd.ticks = 0) -par(xpd = FALSE) -axis(side = 3, at = m + s * qnorm(quantiles.selection.f), labels = formatC(round(qnorm(quantiles.selection.f), 2)), col.axis = col.mean.f, col = col.mean.f, lwd.ticks = 1) -par(par.ini) -} -zone<-matrix(1:4, ncol=2) -layout(zone) -par(omi = c(0, 0, 1.5, 0), mai = c(down.space, left.space, up.space, right.space), las = orient, mgp = c(dist.legend / 0.2, 0.5, 0), xpd = FALSE, bty= box.type, cex.lab = amplif.label, cex.axis = amplif.axis, xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) -par(tcl = -par()$mgp[2] * tick.length) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) -if(is.null(displayed.nb)){ -sampled.data <- as.vector(data) -if(corner.text == ""){ -corner.text <- paste0("ALL VALUES OF THE DATASET DISPLAYED") -}else{ -corner.text <- paste0(corner.text, "\nALL VALUES OF THE DATASET DISPLAYED") -} -}else{ -if(length(as.vector(data)) > displayed.nb){ -sampled.data <- sample(as.vector(data), displayed.nb, replace = FALSE) -if(corner.text == ""){ -corner.text <- paste0("WARNING: ONLY ", displayed.nb, " VALUES ARE DISPLAYED AMONG THE ", length(as.vector(data)), " VALUES OF THE DATASET ANALYZED") -}else{ -corner.text <- paste0(corner.text, "\nWARNING: ONLY ", displayed.nb, " VALUES ARE DISPLAYED AMONG THE ", length(as.vector(data)), " VALUES OF THE DATASET ANALYZED") -} -}else{ -sampled.data <- as.vector(data) -if(corner.text == ""){ -corner.text <- paste0("WARNING: THE DISPLAYED NUMBER OF VALUES PARAMETER ", deparse(substitute(displayed.nb)), " HAS BEEN SET TO ", displayed.nb, " WHICH IS ABOVE THE NUMBER OF VALUES OF THE DATASET ANALYZED -> ALL VALUES DISPLAYED") -}else{ -corner.text <- paste0(corner.text, "\nWARNING: THE DISPLAYED NUMBER OF VALUES PARAMETER ", deparse(substitute(displayed.nb)), " HAS BEEN SET TO ", displayed.nb, " WHICH IS ABOVE THE NUMBER OF VALUES OF THE DATASET ANALYZED -> ALL VALUES DISPLAYED") -} -} -} -if( ! is.null(na.nb)){ -if(corner.text == ""){ -corner.text <- paste0("WARNING: NUMBER OF NA REMOVED IS ", na.nb) -}else{ -corner.text <- paste0("WARNING: NUMBER OF NA REMOVED IS ", na.nb) -} -} -stripchart(sampled.data, method="jitter", jitter=0.4, vertical=FALSE, ylim=c(0.5, 1.5), group.names = "", xlab = "Value", ylab="", pch=1, cex = cex.pt / 0.2) -fun.rug(y.nb.inter.tick.f = 0) -boxplot(as.vector(data), horizontal=TRUE, add=TRUE, boxwex = 0.4, staplecol = col.box, whiskcol = col.box, medcol = col.box, boxcol = col.box, range = 0, whisklty = 1) -m <- mean(as.vector(data), na.rm = TRUE) -s <- sd(as.vector(data), na.rm = TRUE) -segments(m, 0.8, m, 1, lwd=2, col="red") # mean -segments(m -1.96 * s, 0.9, m + 1.96 * s, 0.9, lwd=1, col="red") # mean -graph.xlim <- par()$usr[1:2] # for hist() and qqnorm() below -if(interval.scale.disp == TRUE){ -fun.interval.scale.display(data.f = data) -if(corner.text == ""){ -corner.text <- paste0("MULTIPLYING FACTOR DISPLAYED (MEAN +/- SD) ON SCALES: ", paste(formatC(round(qnorm(quantiles.selection), 2))[-(1:(length(quantiles.selection) - 1) / 2)], collapse = ", "), "\nQUANTILES DISPLAYED ON SCALES: ", paste(quantiles.selection, collapse = ", ")) -}else{ -corner.text <- paste0(corner.text, "\nMULTIPLYING FACTOR DISPLAYED (MEAN +/- SD) ON SCALES: ", paste(formatC(round(qnorm(quantiles.selection), 2))[-(1:(length(quantiles.selection) - 1) / 2)], collapse = ", "), "\nQUANTILES DISPLAYED ON SCALES: ", paste(quantiles.selection, collapse = ", ")) -} -} -output.tempo <- fun.add.cut(data.f = data, return.f = TRUE) # to recover real.trim.cutoffs -if(trim.return == TRUE){ -output <- output.tempo -} -par(xpd = NA) -if(trim.method != ""){ -if(corner.text == ""){ -corner.text <- paste0("SELECTED CUT-OFFS (PROPORTION): ", paste(trim.cutoffs, collapse = ", "), "\nSELECTED CUT-OFFS: ", paste(output.tempo$real.trim.cutoffs, collapse = ", ")) -}else{ -corner.text <- paste0(corner.text, "\nSELECTED CUT-OFFS (PROPORTION): ", paste(trim.cutoffs, collapse = ", "), "\nSELECTED CUT-OFFS: ", paste(output.tempo$real.trim.cutoffs, collapse = ", ")) -} -if(interval.scale.disp == TRUE){ -legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- 1.96sd", paste0("Trimming interval: ", paste0(trim.cutoffs, collapse = " , ")), "Mean +/- sd multiplying factor", "Quantile"), yjust = 0, lty=1, col=c(col.box, "red", color.cut, col.mean, col.quantile), bty="n", cex = amplif.legend) -}else{ -legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- 1.96sd", paste0("Trimming interval: ", paste0(trim.cutoffs, collapse = " , "))), yjust = 0, lty=1, col=c(col.box, "red", color.cut), bty="n", cex = amplif.legend, y.intersp=1.25) -} -}else{ -if(interval.scale.disp == TRUE){ -legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- sd", "Mean +/- sd multiplying factor", "Quantile"), yjust = 0, lty=1, col=c(col.box, "red", col.mean, col.quantile), bty="n", cex = amplif.legend) -}else{ -legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- sd"), yjust = 0, lty=1, col=c(col.box, "red"), bty="n", cex = amplif.legend, y.intersp=1.25) -} -} -par(xpd = FALSE, xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) -hist(as.vector(data), main = "", xlim = graph.xlim, xlab = "Value", ylab="Density", col = grey(0.25)) # removed: breaks = seq(min(as.vector(data), na.rm = TRUE), max(as.vector(data), na.rm = TRUE), length.out = length(as.vector(data)) / 10) -abline(h = par()$usr[3]) -fun.rug() -if(interval.scale.disp == TRUE){ -fun.interval.scale.display(data.f = data) -} -fun.add.cut(data.f = data) -par(xaxs = ifelse(std.x.range, "i", "r")) -stripchart(rank(sampled.data), method="stack", vertical=FALSE, ylim=c(0.99, 1.3), group.names = "", xlab = "Rank of values", ylab="", pch=1, cex = cex.pt / 0.2) -fun.rug(y.nb.inter.tick.f = 0) -x.text <- par("usr")[2] + (par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1]) * (1 - par("plt")[2]) / 2 -y.text <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par()$omd[4] / 2) * ((par("plt")[4] - par("plt")[3])))) * (1 - par("omd")[4])) # BEWARE. Here in "(par()$omd[4] / 2", division by two because there are 2 graphs staked on the y axis, and not one -par(xpd=NA) -text(x = x.text, y = y.text, paste0(corner.text), adj=c(1, 1.1), cex = corner.text.size) # text at the topright corner -par(xpd=FALSE) -par(xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) -qqnorm(as.vector(sampled.data), main = "", datax = TRUE, ylab = "Value", pch = 1, col = "red", cex = cex.pt / 0.2) -fun.rug() -if(diff(quantile(as.vector(data), probs = c(0.25, 0.75), na.rm = TRUE)) != 0){ # otherwise, error generated -qqline(as.vector(data), datax = TRUE) -} -if(interval.scale.disp == TRUE){ -fun.interval.scale.display(data.f = data) -} -fun.add.cut(data.f = data) -} -if(trim.return == TRUE){ -return(output) -} -} - - -######## fun_segmentation() #### segment a dot cloud on a scatterplot and define the dots from another cloud outside the segmentation - - -fun_segmentation <- function( -data1, -x1, -y1, -x.range.split = NULL, -x.step.factor = 10, -y.range.split = NULL, -y.step.factor = 10, -error = 0, -data2 = NULL, -x2, -y2, -data2.pb.dot = "unknown", -xy.cross.kind = "&", -plot = FALSE, -graph.in.file = FALSE, -raster = TRUE, -warn.print = FALSE, -lib.path = NULL -){ -# AIM -# if data1 is a data frame corresponding to the data set of a scatterplot (with a x column for x-axis values and a y column for the y-axis column), then fun_segmentation() delimits a frame around the dots cloud using a sliding window set by x.range.split and x.step.factor to frame the top and bottom part of the cloud, and set by y.range.split and y.step.factor to frame the left and right part of the cloud -# if a second data frame is provided, corresponding to the data set of a scatterplot (with a x column for x-axis values and a y column for the y-axis column), then fun_segmentation() defines the dots of this data frame, outside of the frame of the first data frame -# WARNINGS -# if dots from data2 look significant on the graph (outside the frame) but are not (not black on the last figure), this is probably because the frame is flat on the zero coordinate (no volume inside the frame at this position). Thus, no way to conclude that data2 dots here are significant. These dots are refered to as "unknown". The pb.dot argument deals with such dots -# dots that are sometimes inside and outside the frame, depending on the sliding window, are treated differently: they are removed. Such dots are neither classified as "signif", "non signif" or "unknown", but as "inconsistent" -# unknown dots are treated as finally significant, not significant, or unknown (data2.pb.dot argument) for each x-axis and y-axis separately. Then, the union or intersection of significant dots is performed (argument xy.cross.kind). See the example section -# ARGUMENTS -# data1: a data frame containing a column of x-axis values and a column of y-axis values -# x1: character string of the data1 column name for x-axis (first column of data1 by default) -# y1: character string of the data1 column name for y-axis (second column of data1 by default) -# x.range.split: positive non null numeric value giving the number of interval on the x value range. if x.range is the range of the dots on the x-axis, then abs(diff(x.range) / x.range.split) gives the window size. Window size decreases when range.split increases. In unit of x-axis. Write NULL if not required. At least one of the x.range.split and y.range.split must be non NULL -# x.step.factor: positive non null numeric value giving the shift step of the window. If x.step.factor = 1, no overlap during the sliding (when the window slides from position n to position n+1, no overlap between the two positions). If x.step.factor = 2, 50% of overlap (when the window slides from position n to position n+1, the window on position n+1 overlap 50% of the window when it was on position n) -# y.range.split: same as x.range.split for the y-axis. At least one of the x.range.split and y.range.split must be non NULL -# y.step.factor: same as x.step.factor for the y-axis -# error: proportion (from 0 to 1) of false positives (i.e., proportion of dots from data1 outside of the frame). 0.05 means 5% of the dots from data1 outside of the frame -# data2: a data frame containing a column of x-axis values and a column of y-axis values, for which outside dots of the data1 cloud has to be determined. Write NULL if not required -# x2: character string of the data1 column name for x-axis (first column of data1 by default) -# y2: character string of the data1 column name for y-axis (second column of data1 by default) -# data2.pb.dot: unknown dots are explain in the warning section above. If "signif", then the unknown dots are finally considered as significant (outside the frame). If "not.signif", then the unknown dots are finally considered as non significant (inside the frame). If "unknown", no conclusion are drawn from these dots. See the examples below -# xy.cross.kind: if data2 is non null and if both x.range.split and y.range.split are non null, which dots are finally significants? Write "&" for intersection of outside dots on x and on y. Write "|" for union of outside dots on x and on y. See the examples below -# plot: logical. Print graphs that check the frame? -# graph.in.file: logical. Graphs sent into a graphic device already opened? If FALSE, GUI are opened for each graph. If TRUE, no GUI are opended. The graphs are displayed on the current active graphic device. Ignored if plot is FALSE -# raster: logical. Dots in raster mode? If FALSE, dots from each geom_point from geom argument are in vectorial mode (bigger pdf and long to display if millions of dots). If TRUE, dots from each geom_point from geom argument are in matricial mode (smaller pdf and easy display if millions of dots, but long to generate the layer). If TRUE, the region plot will be square to avoid a bug in fun_gg_point_rast(). If TRUE, solve the transparency problem with some GUI. Not considered if plot is FALSE -# warn.print: logical. Print warnings at the end of the execution? No print if no warning messages -# lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL. Ignored if plot is FALSE -# RETURN -# several graphs if plot is TRUE -# a list containing: -# $data1.removed.row.nb: which rows have been removed due to NA; NaN, -Inf or Inf detection in x1 or y1 columns (NULL if no row removed) -# $data1.removed.rows: removed rows (NULL if no row removed) -# $data2.removed.row.nb: which rows have been removed due to NA; NaN, -Inf or Inf detection in x2 or y2 columns (NULL if no row removed) -# $data2.removed.rows: removed rows (NULL if no row removed) -# $hframe: x and y coordinates of the bottom and top frames for frame plotting (frame1 for the left step and frame2 for the right step) -# $vframe: x and y coordinates of the left and right frames for frame plotting (frame1 for the down step and frame2 for the top step) -# $data1.signif.dot: the significant dots of data1 (i.e., dots outside the frame). A good segmentation should not have any data1.signif.dot -# $data1.non.signif.dot: the non significant dots of data1 (i.e., dots inside the frame) -# $data1.inconsistent.dot: see the warning section above -# $data2.signif.dot: the significant dots of data2 if non NULL (i.e., dots outside the frame) -# $data2.non.signif.dot: the non significant dots of data2 (i.e., dots inside the frame) -# $data2.unknown.dot: the problematic dots of data2 (i.e., data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots). Is systematically NULL except if argument data2.pb.dot = "unknown" and some data2 dots are in such situation. Modifying the segmentation x.range.split, x.step.factor, y.range.split, y.step.factor arguments can solve this problem -# $data2.inconsistent.dot: see the warning section above -# $axes: the x-axis and y-axis info -# $warn: the warning messages. Use cat() for proper display. NULL if no warning -# REQUIRED PACKAGES -# ggplot2 if plot is TRUE -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# if plot is TRUE: -# fun_pack() -# fun_open() -# fun_gg_palette() -# fun_gg_scatter() -# fun_gg_empty_graph() -# fun_close() -# EXAMPLES -# example explaining the unknown and inconsistent dots, and the cross - -# set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data1[5:7, 2] <- NA ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; data2[11:13, 1] <- Inf ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = 20, x.step.factor = 10, y.range.split = 23, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "not.signif", xy.cross.kind = "|", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) -# set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = NULL, x.step.factor = 10, y.range.split = 23, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "unknown", xy.cross.kind = "|", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) -# set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = 20, x.step.factor = 10, y.range.split = NULL, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "unknown", xy.cross.kind = "&", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) -# DEBUGGING -# set.seed(1) ; data1 = data.frame(x = rnorm(50), y = rnorm(50), stringsAsFactors = TRUE) ; data1[5:7, 2] <- NA ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 5 ; x.step.factor = 10 ; y.range.split = 5 ; y.step.factor = 10 ; error = 0 ; data2 = data.frame(x = rnorm(50, 0, 2), y = rnorm(50, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "unknown" ; xy.cross.kind = "|" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL -# set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 20 ; x.step.factor = 10 ; y.range.split = 23 ; y.step.factor = 10 ; error = 0 ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "not.signif" ; xy.cross.kind = "|" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL -# set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 20 ; x.step.factor = 10 ; y.range.split = NULL ; y.step.factor = 10 ; error = 0 ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "unknown" ; xy.cross.kind = "&" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -ini.warning.length <- options()$warning.length -warn <- NULL -warn.count <- 0 -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & length(data1) < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OF AT LEAST 2 COLUMNS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = x1, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (x1 %in% names(data1))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x1 ARGUMENT MUST BE A COLUMN NAME OF data1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & x1 %in% names(data1)){ -tempo <- fun_check(data = data1[, x1], data.name = "x1 COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = y1, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (y1 %in% names(data1))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y1 ARGUMENT MUST BE A COLUMN NAME OF data1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & y1 %in% names(data1)){ -tempo <- fun_check(data = data1[, y1], data.name = "y1 COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -if(is.null(x.range.split) & is.null(y.range.split)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE OF THE x.range.split AND y.range.split ARGUMENTS MUST BE NON NULL") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(x.range.split)){ -tempo <- fun_check(data = x.range.split, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & x.range.split < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.range.split ARGUMENT CANNOT BE LOWER THAN 1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -if( ! is.null(y.range.split)){ -tempo <- fun_check(data = y.range.split, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & y.range.split < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.range.split ARGUMENT CANNOT BE LOWER THAN 1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = x.step.factor, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & x.step.factor < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.step.factor ARGUMENT CANNOT BE LOWER THAN 1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = y.step.factor, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & y.step.factor < 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.step.factor ARGUMENT CANNOT BE LOWER THAN 1") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = error, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(data2)){ -if(is.null(x2) | is.null(y2)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x2 AND y2 ARGUMENTS CANNOT BE NULL IF data2 ARGUMENT IS NON NULL") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = data2, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & length(data2) < 2){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data2 ARGUMENT MUST BE A DATA FRAME OF AT LEAST 2 COLUMNS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(x2)){ -tempo <- fun_check(data = x2, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (x2 %in% names(data2))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x2 ARGUMENT MUST BE A COLUMN NAME OF data2") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & x2 %in% names(data2)){ -tempo <- fun_check(data = data2[, x2], data.name = "x2 COLUMN OF data2", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -} -if( ! is.null(y2)){ -tempo <- fun_check(data = y2, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & ! (y2 %in% names(data2))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y2 ARGUMENT MUST BE A COLUMN NAME OF data2") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & y2 %in% names(data2)){ -tempo <- fun_check(data = data2[, y2], data.name = "y2 COLUMN OF data2", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) -} -} -} -if( ! is.null(data2)){ -tempo <- fun_check(data = data2.pb.dot, options = c("signif", "not.signif", "unknown"), length = 1, fun.name = function.name) ; eval(ee) -} -if( ! (is.null(x.range.split)) & ! (is.null(y.range.split))){ -tempo <- fun_check(data = xy.cross.kind, options = c("&", "|"), length = 1, fun.name = function.name) ; eval(ee) -} -tempo <- fun_check(data = plot, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & plot == TRUE){ -tempo <- fun_check(data = raster, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = graph.in.file, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & graph.in.file == TRUE & is.null(dev.list())){ -tempo.cat <- paste0("ERROR IN ", function.name, ": \ngraph.in.file PARAMETER SET TO TRUE BUT NO ACTIVE GRAPHIC DEVICE DETECTED") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo$problem == FALSE & graph.in.file == TRUE & ! is.null(dev.list())){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHS PRINTED IN THE CURRENT DEVICE (TYPE ", toupper(names(dev.cur())), ")") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# other required function checking -if(plot == TRUE){ -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_open", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_open() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_gg_palette", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_palette() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_gg_empty_graph", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_empty_graph() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_gg_scatter", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_scatter() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_close", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_close() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end other required function checking -# package checking -if(plot == TRUE){ -fun_pack(req.package = c("ggplot2"), lib.path = lib.path) -} -# end package checking -# main code -# na and Inf detection and removal (done now to be sure of the correct length of categ) -data1.removed.row.nb <- NULL -data1.removed.rows <- NULL -data2.removed.row.nb <- NULL -data2.removed.rows <- NULL -if(any(is.na(data1[, c(x1, y1)])) | any(is.infinite(data1[, x1])) | any(is.infinite(data1[, y1]))){ -tempo.na <- unlist(lapply(lapply(c(data1[c(x1, y1)]), FUN = is.na), FUN = which)) -tempo.inf <- unlist(lapply(lapply(c(data1[c(x1, y1)]), FUN = is.infinite), FUN = which)) -data1.removed.row.nb <- sort(unique(c(tempo.na, tempo.inf))) -if(length(data1.removed.row.nb) > 0){ -data1.removed.rows <- data1[data1.removed.row.nb, ] -} -if(length(data1.removed.row.nb) == nrow(data1)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE NA, NaN, -Inf OR Inf DETECTED IN EACH ROW OF data1. FUNCTION CANNOT BE USED ON EMPTY DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(data1.removed.row.nb) > 0){ -data1 <- data1[-data1.removed.row.nb, ] -} -if(nrow(data1) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x1, y1), collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $data1.removed.row.nb AND $data1.removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x1, y1), collapse = " "), " OF data1. NO ROW REMOVED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data2)){ -if(any(is.na(data2[, c(x2, y2)])) | any(is.infinite(data2[, x2])) | any(is.infinite(data2[, y2]))){ -tempo.na <- unlist(lapply(lapply(c(data2[c(x2, y2)]), FUN = is.na), FUN = which)) -tempo.inf <- unlist(lapply(lapply(c(data2[c(x2, y2)]), FUN = is.infinite), FUN = which)) -data2.removed.row.nb <- sort(unique(c(tempo.na, tempo.inf))) -if(length(data2.removed.row.nb) > 0){ -data2.removed.rows <- data2[data2.removed.row.nb, ] -} -if(length(data2.removed.row.nb) == nrow(data2)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE NA, NaN, -Inf OR Inf DETECTED IN EACH ROW OF data2. FUNCTION CANNOT BE USED ON EMPTY DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(data2.removed.row.nb) > 0){ -data2 <- data2[-data2.removed.row.nb, ] -} -if(nrow(data2) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x2, y2), collapse = " "), " OF data2 AND CORRESPONDING ROWS REMOVED (SEE $data2.removed.row.nb AND $data2.removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NO NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x2, y2), collapse = " "), " OF data2. NO ROW REMOVED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end na and Inf detection and removal (done now to be sure of the correct length of categ) -# row annotation (dot number) -# data1 <- data1[ ! duplicated(data1[, c(x1, y1)]), ] # do not remove the dots that have same x and y values, because they will have different dot number -> not the same position on the matrices (so true for symmetric matrices) -data1 <- cbind(data1, DOT_NB = 1:nrow(data1), stringsAsFactors = TRUE) -if( ! is.null(data2)){ -# data2 <- data2[ ! duplicated(data2[, c(x2, y2)]), ] # do not remove the dots that have same x and y values, because they will have different dot number -> not the same position on the matrices (so true for symmetric matrices) -data2 <- cbind(data2, DOT_NB = 1:nrow(data2), stringsAsFactors = TRUE) -} -# end row annotation (dot number) - - - - -# Method using x unit interval -# may be create vector of each column to increase speed -x.data1.l <- NULL # x coord of the y upper and lower limits defined on the data1 cloud for left step line -x.data1.r <- NULL # x coord of the y upper and lower limits defined on the data1 cloud for right step line -y.data1.down.limit.l <- NULL # lower limit of the data1 cloud for left step line -y.data1.top.limit.l <- NULL # upper limit of the data1 cloud for left step line -y.data1.down.limit.r <- NULL # lower limit of the data1 cloud for right step line -y.data1.top.limit.r <- NULL # upper limit of the data1 cloud for left step line -if(any(data1[, x1] %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE x1 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -x.range <- range(data1[, x1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -if(suppressWarnings(any(x.range %in% c(Inf, -Inf)))){ -tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED x.range CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1[, y1] %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE y1 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -y.range <- range(data1[, y1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -if(suppressWarnings(any(x.range %in% c(Inf, -Inf)))){ -tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED y.range CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -x.range.plot <- range(data1[, x1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -y.range.plot <- range(data1[, y1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -if( ! is.null(data2)){ -if(any(data2[, x2] %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE data2 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE x2 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -x.range.plot <- range(data1[, x1], data2[, x2], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -if(any(data2[, y2] %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE data2 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE y2 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -y.range.plot <- range(data1[, y1], data2[, y2], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -} -if(suppressWarnings(any(x.range.plot %in% c(Inf, -Inf)))){ -tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED x.range.plot CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 (AND data2?) ARGUMENTS ARE NA OR Inf ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(suppressWarnings(any(y.range.plot %in% c(Inf, -Inf)))){ -tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED y.range.plot CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 (AND data2?) ARGUMENTS ARE NA OR Inf ONLY") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(x.range.split)){ -# data.frame ordering to slide the window from small to big values + sliding window definition -data1 <- data1[order(data1[, x1], na.last = TRUE), ] -if( ! is.null(data2)){ -data2 <- data2[order(data2[, x2], na.last = TRUE), ] -} -x.win.size <- abs(diff(x.range) / x.range.split) # in unit of x-axis -step <- x.win.size / x.step.factor -# end data.frame ordering to slide the window from small to big values + sliding window definition -# x-axis sliding and y-axis limits of the data1 cloud -> y significant data2 -loop.nb <- ceiling((diff(x.range) - x.win.size) / step) # x.win.size + n * step covers the x range if x.win.size + n * step >= diff(x.range), thus if n >= (diff(x.range) - x.win.size) / step -y.outside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are upper or lower than the frame -y.inside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are not upper or lower than the frame -y.data1.median <- median(data1[, y1], na.rm = TRUE) # will be used for sliding windows without data1 in it -if( ! is.null(data2)){ -y.outside.data2.dot.nb <- integer() # vector that will contain the selected 1D coordinates (i.e., dots) of data2 that are upper or lower than the data1 frame -y.inside.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are not upper or lower than the data1 frame -y.unknown.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are problematic: data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots -# recover data2 dots outside the range of data1 -if(any(data2[, x2] < x.range[1])){ -y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[data2[, x2] < x.range[1]]) -#tempo.warn & indicate the interval -} -if(any(data2[, x2] > x.range[2])){ -y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[data2[, x2] > x.range[2]]) -#tempo.warn & indicate the interval -} -# end recover data2 dots outside the range of data1 -} -# loop.ini.time <- as.numeric(Sys.time()) -for(i1 in 0:(loop.nb + 1)){ -min.pos <- x.range[1] + step * i1 # lower position of the sliding window in data1 -max.pos <- min.pos + x.win.size # upper position of the sliding window in data1 -x.data1.l <- c(x.data1.l, min.pos, min.pos + step) # min.pos + step to make the steps -x.data1.r <- c(x.data1.r, max.pos, max.pos + step) # max.pos + step to make the steps -x.data1.dot.here <- data1[, x1] >= min.pos & data1[, x1] < max.pos # is there data1 dot present in the sliding window, considering the x axis? -if( ! is.null(data2)){ -x.data2.dot.here <- data2[, x2] >= min.pos & data2[, x2] < max.pos # is there data2 dot present in the sliding window, considering the x axis? -} -# recover the data1 dots outside the frame -if(any(x.data1.dot.here == TRUE)){ -tempo.y.data1.top.limit <- quantile(data1[x.data1.dot.here, y1], probs = 1 - error, na.rm = TRUE) -tempo.y.data1.down.limit <- quantile(data1[x.data1.dot.here, y1], probs = 0 + error, na.rm = TRUE) -y.data1.top.limit.l <- c(y.data1.top.limit.l, tempo.y.data1.top.limit, tempo.y.data1.top.limit) -y.data1.down.limit.l <- c(y.data1.down.limit.l, tempo.y.data1.down.limit, tempo.y.data1.down.limit) -y.data1.top.limit.r <- c(y.data1.top.limit.r, tempo.y.data1.top.limit, tempo.y.data1.top.limit) -y.data1.down.limit.r <- c(y.data1.down.limit.r, tempo.y.data1.down.limit, tempo.y.data1.down.limit) -y.data1.dot.signif <- ( ! ((data1[, y1] <= tempo.y.data1.top.limit) & (data1[, y1] >= tempo.y.data1.down.limit))) & x.data1.dot.here # is there data1 dot present in the sliding window, above or below the data1 limits, considering the y axis? -y.data1.dot.not.signif <- x.data1.dot.here & ! y.data1.dot.signif -y.outside.data1.dot.nb <- c(y.outside.data1.dot.nb, data1$DOT_NB[y.data1.dot.signif]) # recover the row number of data1 -y.outside.data1.dot.nb <- unique(y.outside.data1.dot.nb) -y.inside.data1.dot.nb <- c(y.inside.data1.dot.nb, data1$DOT_NB[y.data1.dot.not.signif]) -y.inside.data1.dot.nb <- unique(y.inside.data1.dot.nb) -}else{ -y.data1.top.limit.l <- c(y.data1.top.limit.l, y.data1.median, y.data1.median) -y.data1.down.limit.l <- c(y.data1.down.limit.l, y.data1.median, y.data1.median) -y.data1.top.limit.r <- c(y.data1.top.limit.r, y.data1.median, y.data1.median) -y.data1.down.limit.r <- c(y.data1.down.limit.r, y.data1.median, y.data1.median) -} -# end recover the data1 dots outside the frame -# recover the data2 dots outside the frame -if( ! is.null(data2)){ -if(any(x.data1.dot.here == TRUE) & any(x.data2.dot.here == TRUE)){ -y.data2.dot.signif <- ( ! ((data2[, y2] <= tempo.y.data1.top.limit) & (data2[, y2] >= tempo.y.data1.down.limit))) & x.data2.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the y axis? -y.data2.dot.not.signif <- x.data2.dot.here & ! y.data2.dot.signif -y.outside.data2.dot.nb <- c(y.outside.data2.dot.nb, data2$DOT_NB[y.data2.dot.signif]) -y.outside.data2.dot.nb <- unique(y.outside.data2.dot.nb) -y.inside.data2.dot.nb <- c(y.inside.data2.dot.nb, data2$DOT_NB[y.data2.dot.not.signif]) -y.inside.data2.dot.nb <- unique(y.inside.data2.dot.nb) -}else if(any(x.data1.dot.here == FALSE) & any(x.data2.dot.here == TRUE)){ # problem: data2 dots in the the window but no data1 dots to generates the quantiles -y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[x.data2.dot.here]) -y.unknown.data2.dot.nb <- unique(y.unknown.data2.dot.nb) -#tempo.warn & indicate the interval - - - - -# tempo.warn <- paste0("FROM FUNCTION ", function.name, ": THE [", round(min.pos, 3), " ; ", round(max.pos, 3), "] INTERVAL DOES NOT CONTAIN data1 X VALUES BUT CONTAINS data2 X VALUES WHICH CANNOT BE EVALUATED.\nTHE CONCERNED data2 ROW NUMBERS ARE:\n", paste(which(x.data1.dot.here == FALSE & x.data2.dot.here == TRUE), collapse = "\n")) -# warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end recover the data2 dots outside the frame -# if(any(i1 == seq(1, loop.nb, 500))){ -# loop.fin.time <- as.numeric(Sys.time()) # time of process end -# cat(paste0("COMPUTATION TIME OF LOOP ", i1, " / ", loop.nb, ": ", as.character(lubridate::seconds_to_period(round(loop.fin.time - loop.ini.time))), "\n")) -# } -} -if(max.pos < x.range[2]){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE SLIDING WINDOW HAS NOT REACHED THE MAX VALUE OF data1 ON THE X-AXIS: ", max.pos, " VERSUS ", x.range[2]) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -y.incon.data1.dot.nb.final <- unique(c(y.outside.data1.dot.nb[y.outside.data1.dot.nb %in% y.inside.data1.dot.nb], y.inside.data1.dot.nb[y.inside.data1.dot.nb %in% y.outside.data1.dot.nb])) # inconsistent dots: if a row number of y.inside.data1.dot.nb is present in y.outside.data1.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list -y.outside.data1.dot.nb.final <- y.outside.data1.dot.nb[ ! (y.outside.data1.dot.nb %in% y.incon.data1.dot.nb.final)] # inconsistent dots removed from the outside list -y.inside.data1.dot.nb.final <- y.inside.data1.dot.nb[ ! (y.inside.data1.dot.nb %in% y.incon.data1.dot.nb.final)] # inconsistent dots removed from the inside list -if( ! is.null(data2)){ -# if some unknown dots are also inside, and/or outside, they are put in the inside and/or outside. Ok, because then the intersection between inside and outside is treated -> inconsistent dots -tempo.unknown.out <- y.unknown.data2.dot.nb[y.unknown.data2.dot.nb %in% y.outside.data2.dot.nb] -y.outside.data2.dot.nb <- unique(c(y.outside.data2.dot.nb, tempo.unknown.out)) # if a row number of y.unknown.data2.dot.nb is present in y.outside.data2.dot.nb, it is put into outside -tempo.unknown.in <- y.unknown.data2.dot.nb[y.unknown.data2.dot.nb %in% y.inside.data2.dot.nb] -y.inside.data2.dot.nb <- unique(c(y.inside.data2.dot.nb, tempo.unknown.in)) # if a row number of y.unknown.data2.dot.nb is present in y.inside.data2.dot.nb, it is put into inside -y.unknown.data2.dot.nb.final <- y.unknown.data2.dot.nb[ ! (y.unknown.data2.dot.nb %in% c(y.outside.data2.dot.nb, y.inside.data2.dot.nb))] # then dots also in inside and outside are remove from unknown -y.incon.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb[y.outside.data2.dot.nb %in% y.inside.data2.dot.nb], y.inside.data2.dot.nb[y.inside.data2.dot.nb %in% y.outside.data2.dot.nb])) # inconsistent dots: if a row number of y.inside.data2.dot.nb is present in y.outside.data2.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list -y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb[ ! (y.outside.data2.dot.nb %in% y.incon.data2.dot.nb.final)] # inconsistent dots removed from the outside list -y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb[ ! (y.inside.data2.dot.nb %in% y.incon.data2.dot.nb.final)] # inconsistent dots removed from the inside list -} -# end x-axis sliding and y-axis limits of the data1 cloud -> y significant data2 -} -# end Method using x unit interval - - - - -# Method using y unit interval -y.data1.d <- NULL # y coord of the x upper and lower limits defined on the data1 cloud for down step line -y.data1.t <- NULL # y coord of the x upper and lower limits defined on the data1 cloud for top step line -x.data1.left.limit.d <- NULL # left limit of the data1 cloud for down step line -x.data1.right.limit.d <- NULL # right limit of the data1 cloud for down step line -x.data1.left.limit.t <- NULL # left limit of the data1 cloud for top step line -x.data1.right.limit.t <- NULL # right limit of the data1 cloud for top step line -if( ! is.null(y.range.split)){ -# data.frame ordering to slide the window from small to big values + sliding window definition -data1 <- data1[order(data1[, y1], na.last = TRUE), ] -if( ! is.null(data2)){ -data2 <- data2[order(data2[, y2], na.last = TRUE), ] -} -y.win.size <- abs(diff(y.range) / y.range.split) # in unit of y-axis -step <- y.win.size / y.step.factor -# end data.frame ordering to slide the window from small to big values + sliding window definition -# y-axis sliding and x-axis limits of the data1 cloud -> x significant data2 -loop.nb <- ceiling((diff(y.range) - y.win.size) / step) # y.win.size + n * step covers the y range if y.win.size + n * step >= diff(y.range), thus if n >= (diff(y.range) - y.win.size) / step -x.outside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are upper or lower than the frame -x.inside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are not upper or lower than the frame -x.data1.median <- median(data1[, x1], na.rm = TRUE) # will be used for sliding window without data1 in it -if( ! is.null(data2)){ -x.outside.data2.dot.nb <- integer() # vector that will contain the selected 1D coordinates (i.e., dots) of data2 that are upper or lower than the data1 frame -x.inside.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are not upper or lower than the data1 frame -x.unknown.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are problematic: data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots -# recover data2 dots outside the range of data1 -if(any(data2[, y2] < y.range[1])){ -x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[data2[, y2] < y.range[1]]) -} -if(any(data2[, y2] > y.range[2])){ -x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[data2[, y2] > y.range[2]]) -} -# end recover data2 dots outside the range of data1 -} -# loop.ini.time <- as.numeric(Sys.time()) -for(i1 in 0:(loop.nb + 1)){ -min.pos <- y.range[1] + step * i1 # lower position of the sliding window in data1 -max.pos <- min.pos + y.win.size # upper position of the sliding window in data1 -y.data1.d <- c(y.data1.d, min.pos, min.pos + step) # min.pos + step to make the steps -y.data1.t <- c(y.data1.t, max.pos, max.pos + step) # max.pos + step to make the steps -y.data1.dot.here <- data1[, y1] >= min.pos & data1[, y1] < max.pos # is there data1 dot present in the sliding window, considering the y axis? -if( ! is.null(data2)){ -y.data2.dot.here <- data2[, y2] >= min.pos & data2[, y2] < max.pos # is there data2 dot present in the sliding window, considering the y axis? -} -# recover the data1 dots outside the frame -if(any(y.data1.dot.here == TRUE)){ -tempo.x.data1.right.limit <- quantile(data1[y.data1.dot.here, x1], probs = 1 - error, na.rm = TRUE) -tempo.x.data1.left.limit <- quantile(data1[y.data1.dot.here, x1], probs = 0 + error, na.rm = TRUE) -x.data1.right.limit.d <- c(x.data1.right.limit.d, tempo.x.data1.right.limit, tempo.x.data1.right.limit) -x.data1.left.limit.d <- c(x.data1.left.limit.d, tempo.x.data1.left.limit, tempo.x.data1.left.limit) -x.data1.right.limit.t <- c(x.data1.right.limit.t, tempo.x.data1.right.limit, tempo.x.data1.right.limit) -x.data1.left.limit.t <- c(x.data1.left.limit.t, tempo.x.data1.left.limit, tempo.x.data1.left.limit) -x.data1.dot.signif <- ( ! ((data1[, x1] <= tempo.x.data1.right.limit) & (data1[, x1] >= tempo.x.data1.left.limit))) & y.data1.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the x axis? -x.data1.dot.not.signif <- y.data1.dot.here & ! x.data1.dot.signif -x.outside.data1.dot.nb <- c(x.outside.data1.dot.nb, data1$DOT_NB[x.data1.dot.signif]) # recover the row number of data1 -x.outside.data1.dot.nb <- unique(x.outside.data1.dot.nb) -x.inside.data1.dot.nb <- c(x.inside.data1.dot.nb, data1$DOT_NB[x.data1.dot.not.signif]) -x.inside.data1.dot.nb <- unique(x.inside.data1.dot.nb) -}else{ -x.data1.right.limit.d <- c(x.data1.right.limit.d, x.data1.median, x.data1.median) -x.data1.left.limit.d <- c(x.data1.left.limit.d, x.data1.median, x.data1.median) -x.data1.right.limit.t <- c(x.data1.right.limit.t, x.data1.median, x.data1.median) -x.data1.left.limit.t <- c(x.data1.left.limit.t, x.data1.median, x.data1.median) -} -# end recover the data1 dots outside the frame -# recover the data2 dots outside the frame -if( ! is.null(data2)){ -if(any(y.data1.dot.here == TRUE) & any(y.data2.dot.here == TRUE)){ -x.data2.dot.signif <- ( ! ((data2[, x2] <= tempo.x.data1.right.limit) & (data2[, x2] >= tempo.x.data1.left.limit))) & y.data2.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the x axis? -x.data2.dot.not.signif <- y.data2.dot.here & ! x.data2.dot.signif -x.outside.data2.dot.nb <- c(x.outside.data2.dot.nb, data2$DOT_NB[x.data2.dot.signif]) -x.outside.data2.dot.nb <- unique(x.outside.data2.dot.nb) -x.inside.data2.dot.nb <- c(x.inside.data2.dot.nb, data2$DOT_NB[x.data2.dot.not.signif]) -x.inside.data2.dot.nb <- unique(x.inside.data2.dot.nb) -}else if(any(y.data1.dot.here == FALSE) & any(y.data2.dot.here == TRUE)){ # recover the data2 dots outside the range of the data1 cloud -x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[y.data2.dot.here]) -x.unknown.data2.dot.nb <- unique(x.unknown.data2.dot.nb) - - - -# tempo.warn <- paste0("FROM FUNCTION ", function.name, ": THE [", round(min.pos, 3), " ; ", round(max.pos, 3), "] INTERVAL DOES NOT CONTAIN data1 Y VALUES BUT CONTAINS data2 Y VALUES WHICH CANNOT BE EVALUATED.\nTHE CONCERNED data2 ROW NUMBERS ARE:\n", paste(which(y.data1.dot.here == FALSE & y.data2.dot.here == TRUE), collapse = "\n")) -# warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end recover the data2 dots outside the frame -# if(any(i1 == seq(1, loop.nb, 500))){ -# loop.fin.time <- as.numeric(Sys.time()) # time of process end -# cat(paste0("COMPUTATION TIME OF LOOP ", i1, " / ", loop.nb, ": ", as.character(lubridate::seconds_to_period(round(loop.fin.time - loop.ini.time))), "\n")) -# } -} -if(max.pos < y.range[2]){ -tempo.cat <- paste0("ERROR IN ", function.name, ": THE SLIDING WINDOW HAS NOT REACHED THE MAX VALUE OF data1 ON THE Y-AXIS: ", max.pos, " VERSUS ", y.range[2]) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -x.incon.data1.dot.nb.final <- unique(c(x.outside.data1.dot.nb[x.outside.data1.dot.nb %in% x.inside.data1.dot.nb], x.inside.data1.dot.nb[x.inside.data1.dot.nb %in% x.outside.data1.dot.nb])) # inconsistent dots: if a row number of x.inside.data1.dot.nb is present in x.outside.data1.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list -x.outside.data1.dot.nb.final <- x.outside.data1.dot.nb[ ! (x.outside.data1.dot.nb %in% x.incon.data1.dot.nb.final)] # inconsistent dots removed from the outside list -x.inside.data1.dot.nb.final <- x.inside.data1.dot.nb[ ! (x.inside.data1.dot.nb %in% x.incon.data1.dot.nb.final)] # inconsistent dots removed from the inside list -if( ! is.null(data2)){ -# if some unknown dots are also inside, and/or outside, they are put in the inside and/or outside. Ok, because then the intersection between inside and outside is treated -> inconsistent dots -tempo.unknown.out <- x.unknown.data2.dot.nb[x.unknown.data2.dot.nb %in% x.outside.data2.dot.nb] -x.outside.data2.dot.nb <- unique(c(x.outside.data2.dot.nb, tempo.unknown.out)) # if a row number of x.unknown.data2.dot.nb is present in x.outside.data2.dot.nb, it is put into outside -tempo.unknown.in <- x.unknown.data2.dot.nb[x.unknown.data2.dot.nb %in% x.inside.data2.dot.nb] -x.inside.data2.dot.nb <- unique(c(x.inside.data2.dot.nb, tempo.unknown.in)) # if a row number of x.unknown.data2.dot.nb is present in x.inside.data2.dot.nb, it is put into inside -x.unknown.data2.dot.nb.final <- x.unknown.data2.dot.nb[ ! (x.unknown.data2.dot.nb %in% c(x.outside.data2.dot.nb, x.inside.data2.dot.nb))] # then dots also in inside and outside are remove from unknown -x.incon.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb[x.outside.data2.dot.nb %in% x.inside.data2.dot.nb], x.inside.data2.dot.nb[x.inside.data2.dot.nb %in% x.outside.data2.dot.nb])) # inconsistent dots: if a row number of x.inside.data2.dot.nb is present in x.outside.data2.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list -x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb[ ! (x.outside.data2.dot.nb %in% x.incon.data2.dot.nb.final)] # inconsistent dots removed from the outside list -x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb[ ! (x.inside.data2.dot.nb %in% x.incon.data2.dot.nb.final)] # inconsistent dots removed from the inside list -} -# end y-axis sliding and x-axis limits of the data1 cloud -> x significant data2 -} -# end Method using y unit interval - - - -# recovering the frame coordinates -hframe = rbind( -data.frame( -x = if(is.null(x.data1.l)){NULL}else{x.data1.l}, -y = if(is.null(x.data1.l)){NULL}else{y.data1.down.limit.l}, -kind = if(is.null(x.data1.l)){NULL}else{"down.frame1"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(x.data1.r)){NULL}else{x.data1.r}, -y = if(is.null(x.data1.r)){NULL}else{y.data1.down.limit.r}, -kind = if(is.null(x.data1.r)){NULL}else{"down.frame2"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(x.data1.l)){NULL}else{x.data1.l}, -y = if(is.null(x.data1.l)){NULL}else{y.data1.top.limit.l}, -kind = if(is.null(x.data1.l)){NULL}else{"top.frame1"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(x.data1.r)){NULL}else{x.data1.r}, -y = if(is.null(x.data1.r)){NULL}else{y.data1.top.limit.r}, -kind = if(is.null(x.data1.r)){NULL}else{"top.frame2"}, -stringsAsFactors = TRUE -), -stringsAsFactors = TRUE -) -vframe = rbind( -data.frame( -x = if(is.null(y.data1.d)){NULL}else{x.data1.left.limit.d}, -y = if(is.null(y.data1.d)){NULL}else{y.data1.d}, -kind = if(is.null(y.data1.d)){NULL}else{"left.frame1"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(y.data1.t)){NULL}else{x.data1.left.limit.t}, -y = if(is.null(y.data1.t)){NULL}else{y.data1.t}, -kind = if(is.null(y.data1.t)){NULL}else{"left.frame2"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(y.data1.d)){NULL}else{x.data1.right.limit.d}, -y = if(is.null(y.data1.d)){NULL}else{y.data1.d}, -kind = if(is.null(y.data1.d)){NULL}else{"right.frame1"}, -stringsAsFactors = TRUE -), -data.frame( -x = if(is.null(y.data1.t)){NULL}else{x.data1.right.limit.t}, -y = if(is.null(y.data1.t)){NULL}else{y.data1.t}, -kind = if(is.null(y.data1.t)){NULL}else{"right.frame2"}, -stringsAsFactors = TRUE -), -stringsAsFactors = TRUE -) -# end recovering the frame coordinates -# recovering the dot coordinates -data1.signif.dot <- NULL -data1.non.signif.dot <- NULL -data1.incon.dot <- NULL -data2.signif.dot <- NULL -data2.non.signif.dot <- NULL -data2.unknown.dot <- NULL -data2.incon.dot <- NULL -if(( ! is.null(x.range.split)) & ( ! is.null(y.range.split))){ -# inconsistent dots recovery -if(length(unique(c(x.incon.data1.dot.nb.final, y.incon.data1.dot.nb.final))) > 0){ -data1.incon.dot <- data1[data1$DOT_NB %in% unique(c(x.incon.data1.dot.nb.final, y.incon.data1.dot.nb.final)), ] # if a dot in inconsistent in x or y -> classified as inconsistent (so unique() used) -# removal of the inconsistent dot in the other classifications -x.inside.data1.dot.nb.final <- x.inside.data1.dot.nb.final[ ! x.inside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -y.inside.data1.dot.nb.final <- y.inside.data1.dot.nb.final[ ! y.inside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -x.outside.data1.dot.nb.final <- x.outside.data1.dot.nb.final[ ! x.outside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -y.outside.data1.dot.nb.final <- y.outside.data1.dot.nb.final[ ! y.outside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -x.unknown.data1.dot.nb.final <- x.unknown.data1.dot.nb.final[ ! x.unknown.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -y.unknown.data1.dot.nb.final <- y.unknown.data1.dot.nb.final[ ! y.unknown.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] -# end removal of the inconsistent dot in the other classifications -} -if( ! is.null(data2)){ -if(length(unique(c(x.incon.data2.dot.nb.final, y.incon.data2.dot.nb.final))) > 0){ -data2.incon.dot <- data2[data2$DOT_NB %in% unique(c(x.incon.data2.dot.nb.final, y.incon.data2.dot.nb.final)), ] -# removal of the inconsistent dot in the other classifications -x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -x.unknown.data2.dot.nb.final <- x.unknown.data2.dot.nb.final[ ! x.unknown.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -y.unknown.data2.dot.nb.final <- y.unknown.data2.dot.nb.final[ ! y.unknown.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] -# end removal of the inconsistent dot in the other classifications -} -} -# end inconsistent dots recovery -# unknown dots recovery -if( ! is.null(data2)){ -if(data2.pb.dot == "signif"){ -x.outside.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) -x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% x.unknown.data2.dot.nb.final] # remove x.unknown.data2.dot.nb.final from x.inside.data2.dot.nb.final -y.outside.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) -y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% y.unknown.data2.dot.nb.final] # remove y.unknown.data2.dot.nb.final from y.inside.data2.dot.nb.final -x.unknown.data2.dot.nb.final <- NULL -y.unknown.data2.dot.nb.final <- NULL -data2.unknown.dot <- NULL -}else if(data2.pb.dot == "not.signif"){ -x.inside.data2.dot.nb.final <- unique(c(x.inside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) -x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% x.unknown.data2.dot.nb.final] # remove x.unknown.data2.dot.nb.final from x.outside.data2.dot.nb.final -y.inside.data2.dot.nb.final <- unique(c(y.inside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) -y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% y.unknown.data2.dot.nb.final] # remove y.unknown.data2.dot.nb.final from y.outside.data2.dot.nb.final -x.unknown.data2.dot.nb.final <- NULL -y.unknown.data2.dot.nb.final <- NULL -data2.unknown.dot <- NULL -}else if(data2.pb.dot == "unknown"){ -if(length(unique(c(x.unknown.data2.dot.nb.final, y.unknown.data2.dot.nb.final))) > 0){ -data2.unknown.dot <- data2[data2$DOT_NB %in% unique(c(x.unknown.data2.dot.nb.final, y.unknown.data2.dot.nb.final)), ] # if a dot in unknown in x or y -> classified as unknown (so unique() used) -x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove x.unknown.data2.dot.nb.final from x.outside.data2.dot.nb.final -x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove x.unknown.data2.dot.nb.final from x.inside.data2.dot.nb.final -y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove y.unknown.data2.dot.nb.final from y.outside.data2.dot.nb.final -y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove y.unknown.data2.dot.nb.final from y.inside.data2.dot.nb.final -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end unknown dots recovery -# sign and non sign dot recovery -if(xy.cross.kind == "|"){ # here the problem is to deal with significant dots depending on x and y. Thus I start with that, recover dots finally non significant in outside and put them in inside (when &), and remove from inside the dots in outside -if(length(unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final))) > 0){ -tempo.outside <- unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final)) # union so unique() used -tempo.inside <- unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)) -tempo.inside <- tempo.inside[ ! tempo.inside %in% tempo.outside] -data1.signif.dot <- data1[data1$DOT_NB %in% tempo.outside, ] -data1.non.signif.dot <- data1[data1$DOT_NB %in% tempo.inside, ] -}else{ -data1.non.signif.dot <- data1[unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it -} -}else if(xy.cross.kind == "&"){ -if(sum(x.outside.data1.dot.nb.final %in% y.outside.data1.dot.nb.final) > 0){ # that is intersection -tempo.outside <- unique(x.outside.data1.dot.nb.final[x.outside.data1.dot.nb.final %in% y.outside.data1.dot.nb.final]) # intersection -tempo.outside.removed <- unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final))[ ! unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final)) %in% tempo.outside] -tempo.inside <- unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)) -data1.signif.dot <- data1[data1$DOT_NB %in% tempo.outside, ] -data1.non.signif.dot <- data1[data1$DOT_NB %in% tempo.inside, ] -}else{ -data1.non.signif.dot <- data1[unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(data2)){ -if(xy.cross.kind == "|"){ # here the problem is to deal with significant dots depending on x and y. Thus I start with that, recover dots finally non significant in outside and put them in inside (when &), and remove from inside the dots in outside -if(length(unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final))) > 0){ -tempo.outside <- unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final)) # union so unique() used -tempo.inside <- unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)) -tempo.inside <- tempo.inside[ ! tempo.inside %in% tempo.outside] -data2.signif.dot <- data2[data2$DOT_NB %in% tempo.outside, ] -data2.non.signif.dot <- data2[data2$DOT_NB %in% tempo.inside, ] -}else{ -data2.non.signif.dot <- data2[unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it -} -}else if(xy.cross.kind == "&"){ -if(sum(x.outside.data2.dot.nb.final %in% y.outside.data2.dot.nb.final) > 0){ # that is intersection -tempo.outside <- unique(x.outside.data2.dot.nb.final[x.outside.data2.dot.nb.final %in% y.outside.data2.dot.nb.final]) # intersection -tempo.outside.removed <- unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final))[ ! unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final)) %in% tempo.outside] -tempo.inside <- unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)) -data2.signif.dot <- data2[data2$DOT_NB %in% tempo.outside, ] -data2.non.signif.dot <- data2[data2$DOT_NB %in% tempo.inside, ] -}else{ -data2.non.signif.dot <- data2[unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end sign and non sign dot recovery -}else if(( ! is.null(x.range.split)) & is.null(y.range.split)){ -# inconsistent dots recovery -if(length(y.incon.data1.dot.nb.final) > 0){ -data1.incon.dot <- data1[data1$DOT_NB %in% y.incon.data1.dot.nb.final, ] -} -if( ! is.null(data2)){ -if(length(y.incon.data2.dot.nb.final) > 0){ -data2.incon.dot <- data2[data2$DOT_NB %in% y.incon.data2.dot.nb.final, ] -} -}# end inconsistent dots recovery -# unknown dots recovery -if( ! is.null(data2)){ -if(data2.pb.dot == "signif"){ -y.outside.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) -}else if(data2.pb.dot == "not.signif"){ -y.inside.data2.dot.nb.final <- unique(c(y.inside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) -}else if(data2.pb.dot == "unknown"){ -if(length(y.unknown.data2.dot.nb.final) > 0){ -data2.unknown.dot <- data2[data2$DOT_NB %in% y.unknown.data2.dot.nb.final, ] -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 6") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end unknown dots recovery -# sign and non sign dot recovery -if(length(y.outside.data1.dot.nb.final) > 0){ -data1.signif.dot <- data1[data1$DOT_NB %in% y.outside.data1.dot.nb.final, ] -} -if(length(y.inside.data1.dot.nb.final) > 0){ -data1.non.signif.dot <- data1[data1$DOT_NB %in% y.inside.data1.dot.nb.final, ] -} -if( ! is.null(data2)){ -if(length(y.outside.data2.dot.nb.final) > 0){ -data2.signif.dot <- data2[data2$DOT_NB %in% y.outside.data2.dot.nb.final, ] -} -if(length(y.inside.data2.dot.nb.final) > 0){ -data2.non.signif.dot <- data2[data2$DOT_NB %in% y.inside.data2.dot.nb.final, ] -} -} -# end sign and non sign dot recovery -}else if(is.null(x.range.split) & ( ! is.null(y.range.split))){ -# inconsistent dots recovery -if(length(x.incon.data1.dot.nb.final) > 0){ -data1.incon.dot <- data1[data1$DOT_NB %in% x.incon.data1.dot.nb.final, ] -} -if( ! is.null(data2)){ -if(length(x.incon.data2.dot.nb.final) > 0){ -data2.incon.dot <- data2[data2$DOT_NB %in% x.incon.data2.dot.nb.final, ] -} -}# end inconsistent dots recovery -# unknown dots recovery -if( ! is.null(data2)){ -if(data2.pb.dot == "signif"){ -x.outside.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) -}else if(data2.pb.dot == "not.signif"){ -x.inside.data2.dot.nb.final <- unique(c(x.inside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) -}else if(data2.pb.dot == "unknown"){ -if(length(x.unknown.data2.dot.nb.final) > 0){ -data2.unknown.dot <- data2[data2$DOT_NB %in% x.unknown.data2.dot.nb.final, ] -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 7") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end unknown dots recovery -# sign and non sign dot recovery -if(length(x.outside.data1.dot.nb.final) > 0){ -data1.signif.dot <- data1[data1$DOT_NB %in% x.outside.data1.dot.nb.final, ] -} -if(length(x.inside.data1.dot.nb.final) > 0){ -data1.non.signif.dot <- data1[data1$DOT_NB %in% x.inside.data1.dot.nb.final, ] -} -if( ! is.null(data2)){ -if(length(x.outside.data2.dot.nb.final) > 0){ -data2.signif.dot <- data2[data2$DOT_NB %in% x.outside.data2.dot.nb.final, ] -} -if(length(x.inside.data2.dot.nb.final) > 0){ -data2.non.signif.dot <- data2[data2$DOT_NB %in% x.inside.data2.dot.nb.final, ] -} -} -# end sign and non sign dot recovery -} -# end recovering the dot coordinates -# verif -if(any(data1.signif.dot$DOT_NB %in% data1.non.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", FUNCTION.NAME, ": CODE INCONSISTENCY 8") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1.non.signif.dot$DOT_NB %in% data1.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", FUNCTION.NAME, ": CODE INCONSISTENCY 9") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1.signif.dot$DOT_NB %in% data1.incon.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 10") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1.incon.dot$DOT_NB %in% data1.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 11") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1.non.signif.dot$DOT_NB %in% data1.incon.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 12") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data1.incon.dot$DOT_NB %in% data1.non.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 13") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(data2)){ -if(any(data2.signif.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 14") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.non.signif.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 15") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.signif.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 16") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.unknown.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 17") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.signif.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 18") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.incon.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 19") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.non.signif.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 20") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.unknown.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 21") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.non.signif.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 22") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.incon.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 23") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.unknown.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 24") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(data2.incon.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 25") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end verif -# plot -# recovering the axes data whatever plot or not -if(is.null(data2)){ -axes <- fun_gg_scatter(data1 = list(data1), x = list(x1), y = list(y1), categ = list(NULL), color = list(fun_gg_palette(2)[2]), geom = list("geom_point"), alpha = list(0.5), x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, plot = FALSE, return = TRUE)$axes -}else{ -axes <- fun_gg_scatter(data1 = list(data1, data2), x = list(x1, x2), y = list(y1, y2), categ = list(NULL, NULL), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1]), geom = list("geom_point", "geom_point"), alpha = list(0.5, 0.5), x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, plot = FALSE, return = TRUE)$axes -} -# end recovering the axes data whatever plot or not -if(plot == TRUE){ -# add a categ for plot legend -tempo.df.name <- c("data1", "data1.signif.dot", "data1.incon.dot", "data2", "data2.signif.dot", "data2.unknown.dot", "data2.incon.dot") -tempo.class.name <- c("data1", "data1", "data1", "data2", "data2", "data2", "data2") -for(i2 in 1:length(tempo.df.name)){ -if( ! is.null(get(tempo.df.name[i2], env = sys.nframe(), inherit = FALSE))){ -assign(tempo.df.name[i2], data.frame(get(tempo.df.name[i2], env = sys.nframe(), inherit = FALSE), kind = tempo.class.name[i2]), -stringsAsFactors = TRUE) -} -} -# end add a categ for plot legend -if(( ! is.null(x.range.split)) & ( ! is.null(y.range.split))){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe), x = list(x1, "x", "x"), y = list(y1, "y", "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data1.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe, data1.signif.dot), x = list(x1, "x", "x", x1), y = list(y1, "y", "y", y1), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") -} -if( ! is.null(data1.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe, data1.incon.dot), x = list(x1, "x", "x", x1), y = list(y1, "y", "y", y1), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") -} -if( ! is.null(data2)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, hframe , vframe), x = list(x1, x2, "x", "x"), y = list(y1, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data2.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") -} -if( ! is.null(data2.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") -} -if( ! is.null(data2.unknown.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) - -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 12, title = "DATA2 + DATA2 UNKNOWN DOTS") -} -} -}else if(( ! is.null(x.range.split)) & is.null(y.range.split)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe), x = list(x1, "x"), y = list(y1, "y"), categ = list("kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_path"), alpha = list(0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data1.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, data1.signif.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") -} -if( ! is.null(data1.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, data1.incon.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") -} -if( ! is.null(data2)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, hframe), x = list(x1, x2, "x"), y = list(y1, y2, "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data2.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") -} -if( ! is.null(data2.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") -} -if( ! is.null(data2.unknown.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 8, title = "DATA2 + DATA2 UNKNOWN DOTS") -} -} -}else if(is.null(x.range.split) & ( ! is.null(y.range.split))){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe), x = list(x1, "x"), y = list(y1, "y"), categ = list("kind", "kind"), legend.name = list("DATASET", "VERT FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_path"), alpha = list(0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data1.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe, data1.signif.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "VERT FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") -} -if( ! is.null(data1.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe, data1.incon.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "VERT FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") -} -if( ! is.null(data2)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, vframe), x = list(x1, x2, "x"), y = list(y1, y2, "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(data2.signif.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") -} -if( ! is.null(data2.incon.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") -} -if( ! is.null(data2.unknown.dot)){ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) -if( ! is.null(tempo.graph$warn)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(graph.in.file == FALSE){ -fun_open(pdf = FALSE) -} -fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 8, title = "DATA2 + DATA2 UNKNOWN DOTS") -} -} -} -} -# end plot -if(warn.print == TRUE & ! is.null(warn)){ -options(warning.length = 8170) -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -tempo.list <- list(data1.removed.row.nb = data1.removed.row.nb, data1.removed.rows = data1.removed.rows, data2.removed.row.nb = data2.removed.row.nb, data2.removed.rows = data2.removed.rows, hframe = hframe, vframe = vframe, data1.signif.dot = data1.signif.dot, data1.non.signif.dot = data1.non.signif.dot, data1.inconsistent.dot = data1.incon.dot, data2.signif.dot = data2.signif.dot, data2.non.signif.dot = data2.non.signif.dot, data2.unknown.dot = data2.unknown.dot, data2.inconsistent.dot = data2.incon.dot, axes = axes, warn = warn) -return(tempo.list) -} - - -################ Import - - -######## fun_pack() #### check if R packages are present and import into the working environment - - -fun_pack <- function( -req.package, -load = FALSE, -lib.path = NULL -){ -# AIM -# check if the specified R packages are present in the computer and import them into the working environment -# ARGUMENTS -# req.package: character vector of package names to import -# load: logical. Load the package into the environement (using library())? Interesting if packages are not in default folders or for checking the functions names of packages using search() -# lib.path: optional character vector specifying the absolute pathways of the directories containing some of the listed packages in the req.package argument, if not in the default directories. Ignored if NULL -# RETURN -# nothing -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_pack(req.package = "nopackage") -# fun_pack(req.package = "ggplot2") -# fun_pack(req.package = "ggplot2", lib.path = "blablabla") -# DEBUGGING -# req.package = "ggplot2" ; lib.path = "C:/Program Files/R/R-3.5.1/library" -# req.package = "serpentine" ; lib.path = "C:/users/gael/appdata/roaming/python/python36/site-packages" -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = req.package, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = load, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -if(is.null(lib.path)){ -lib.path <- .libPaths() # .libPaths(new = lib.path) # or .libPaths(new = c(.libPaths(), lib.path)) -}else{ -.libPaths(new = sub(x = lib.path, pattern = "/$|\\\\$", replacement = "")) # .libPaths(new = ) add path to default path. BEWARE: .libPaths() does not support / at the end of a submitted path. Thus check and replace last / or \\ in path -lib.path <- .libPaths() -} -tempo <- NULL -for(i1 in 1:length(req.package)){ -if( ! req.package[i1] %in% rownames(utils::installed.packages(lib.loc = lib.path))){ -tempo <- c(tempo, req.package[i1]) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0( -"ERROR IN ", -function.name, -": PACKAGE", -ifelse(length(tempo) == 1L, paste0("\n\n", tempo, "\n\n"), paste0("S\n", paste(tempo, collapse = "\n"), "\n")), -"MUST BE INSTALLED IN", -ifelse(length(lib.path) == 1L, "", " ONE OF THESE FOLDERS"), -":\n", -paste(lib.path, collapse = "\n") -) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(load == TRUE){ -for(i2 in 1:length(req.package)){ -suppressMessages(suppressWarnings(suppressPackageStartupMessages(library(req.package[i2], lib.loc = lib.path, quietly = TRUE, character.only = TRUE)))) -} -} -} - - -######## fun_python_pack() #### check if python packages are present - - -fun_python_pack <- function( -req.package, -python.exec.path = NULL, -lib.path = NULL, -R.lib.path = NULL -){ -# AIM -# check if the specified python packages are present in the computer (no import) -# WARNINGS -# for python 3.7. Previous versions return an error "Error in sys$stdout$flush() : attempt to apply non-function" -# ARGUMENTS -# req.package: character vector of package names to import -# python.exec.path: optional character vector specifying the absolute pathways of the executable python file to use (associated to the packages to use). If NULL, the reticulate::import_from_path() function used in fun_python_pack() seeks for an available version of python.exe, and then uses python_config(python_version, required_module, python_versions). But might not be the correct one for the lib.path parameter specified. Thus, it is recommanded to do not leave NULL, notably when using computing clusters -# lib.path: optional character vector specifying the absolute pathways of the directories containing some of the listed packages in the req.package argument, if not in the default directories -# R.lib.path: absolute path of the reticulate packages, if not in the default folders -# RETURN -# nothing -# REQUIRED PACKAGES -# reticulate -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# fun_pack() -# EXAMPLES -# example of error message -# fun_python_pack(req.package = "nopackage") -# example without error message (require the installation of the python serpentine package from https://github.com/koszullab/serpentine -# fun_python_pack(req.package = "serpentine", python.exec.path = "C:/ProgramData/Anaconda3/python.exe", lib.path = "c:/programdata/anaconda3/lib/site-packages/") -# another example of error message -# fun_python_pack(req.package = "serpentine", lib.path = "blablabla") -# DEBUGGING -# req.package = "serpentine" ; python.exec.path = "C:/ProgramData/Anaconda3/python.exe" ; lib.path = "c:/programdata/anaconda3/lib/site-packages/" ; R.lib.path = NULL -# req.package = "bad" ; lib.path = NULL ; R.lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(utils::find("fun_pack", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = req.package, class = "character", fun.name = function.name) ; eval(ee) -if( ! is.null(python.exec.path)){ -tempo <- fun_check(data = python.exec.path, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(file.exists(python.exec.path))){ # separation to avoid the problem of tempo$problem == FALSE and python.exec.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": FILE PATH INDICATED IN THE python.exec.path ARGUMENT DOES NOT EXISTS:\n", paste(python.exec.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if( ! is.null(R.lib.path)){ -tempo <- fun_check(data = R.lib.path, class = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(R.lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and R.lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE R.lib.path ARGUMENT DOES NOT EXISTS:\n", paste(R.lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# package checking -fun_pack(req.package = "reticulate", lib.path = R.lib.path) -# end package checking -# main code -if(is.null(python.exec.path)){ -python.exec.path <- reticulate::py_run_string(" -import sys ; -path_lib = sys.path -") # python string -python.exec.path <- python.exec.path$path_lib -} -if(is.null(lib.path)){ -lib.path <- reticulate::py_run_string(" -import sys ; -path_lib = sys.path -") # python string -lib.path <- lib.path$path_lib -} -reticulate::use_python(Sys.which(python.exec.path), required = TRUE) # required to avoid the use of erratic python exec by reticulate::import_from_path() -for(i1 in 1:length(req.package)){ -tempo.try <- vector("list", length = length(lib.path)) -for(i2 in 1:length(lib.path)){ -tempo.try[[i2]] <- suppressWarnings(try(reticulate::import_from_path(req.package[i1], path = lib.path[i2]), silent = TRUE)) -tempo.try[[i2]] <- suppressWarnings(try(reticulate::import_from_path(req.package[i1], path = lib.path[i2]), silent = TRUE)) # done twice to avoid the error message about flushing present the first time but not the second time. see https://stackoverflow.com/questions/57357001/reticulate-1-13-error-in-sysstdoutflush-attempt-to-apply-non-function -} -if(all(sapply(tempo.try, FUN = grepl, pattern = "[Ee]rror"))){ -print(tempo.try) -tempo.cat <- paste0("ERROR IN ", function.name, ": PACKAGE ", req.package[i1], " MUST BE INSTALLED IN THE MENTIONNED DIRECTORY:\n", paste(lib.path, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} # else{ -# suppressMessages(suppressWarnings(suppressPackageStartupMessages(assign(req.package[i1], reticulate::import(req.package[i1]))))) # not required because try() already evaluates -# } -} -} - - -################ Print / Exporting results (text & tables) - - -######## fun_report() #### print string or data object into output file - - -# Problem with 1D tables : names over the table not printed. In addition, see how the 2D tables are printed. - -fun_report <- function( -data, -output = "results.txt", -path = "C:/Users/Gael/Desktop/", -overwrite = FALSE, -rownames.kept = FALSE, -vector.cat = FALSE, -noquote = TRUE, -sep = 2 -){ -# AIM -# log file function: print a character string or a data object into a same output file -# ARGUMENTS -# data: object to print in the output file. If NULL, nothing is done, with no warning -# output: name of the output file -# path: location of the output file -# overwrite: (logical) if output file already exists, defines if the printing is appended (default FALSE) or if the output file content is erased before printing (TRUE) -# rownames.kept: (logical) defines whether row names have to be removed or not in small tables (less than length.rows rows) -# vector.cat (logical). If TRUE print a vector of length > 1 using cat() instead of capture.output(). Otherwise (default FALSE) the opposite -# noquote: (logical). If TRUE no quote are present for the characters -# sep: number of separating lines after printed data (must be integer) -# RETURN -# nothing -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_report() -# fun_report(data = 1:3, output = "results.txt", path = "C:/Users/Gael/Desktop", overwrite = TRUE, rownames.kept = FALSE, vector.cat = FALSE, noquote = FALSE, sep = 2) -# DEBUGGING -# data = 1:3 ; output = "results.txt" ; path = "C:/Users/Gael/Desktop" ; overwrite = TRUE ; rownames.kept = FALSE ; vector.cat = FALSE ; noquote = FALSE ; sep = 2 # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# argument checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = output, class = "character", length = 1, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & output == ""){ -tempo.cat <- paste0("ERROR IN ", function.name, ": output ARGUMENT AS \"\" DOES NOT CORRESPOND TO A VALID FILE NAME") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo <- fun_check(data = path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": path ARGUMENT DOES NOT CORRESPOND TO EXISTING DIRECTORY\n", paste(path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = overwrite, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = rownames.kept, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = vector.cat, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = noquote, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = sep, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# the 4 next lines are inactivated but kept because at a time, I might have a problem with data (solved with data = NULL). These 4 lines are just to know how to detect a missing argument. Important here because if data is not provided, print the code of the data function -# arg.user.list <- as.list(match.call(expand.dots = FALSE))[-1] # recover all the arguments provided by the function user (excluding the argument with defaults values not provided by the user. Thus, it is really the list indicated by the user) -# default.arg.list <- formals(fun = sys.function(sys.parent())) # list of all the arguments of the function with their default values (not the values of the user !). It seems that ls() as first line of the function provide the names of the arguments (empty, called, etc., or not) -# arg.without.default.value <- sapply(default.arg.list, is.symbol) & sapply(sapply(default.arg.list, as.character), identical, "") # logical to detect argument without default values (these are typeof "symbol" and class "name" and empty character -# if( ! all(names(default.arg.list)[arg.without.default.value] %in% names(arg.user.list))){ # test that the arguments with no null values are provided by the user -# tempo.cat <- paste0("ERROR IN ", function.name, ": VALUE REQUIRED FOR THESE ARGUMENTS WITH NO DEFAULTS VALUES: ", paste(names(default.arg.list)[arg.without.default.value][ ! names(default.arg.list)[arg.without.default.value] %in% names(arg.user.list)], collapse = " ")) -# stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -# } -# end argument checking -# main code -if( ! is.null(data)){ -if(all(class(data) == "data.frame") | all(class(data) == "table") | all(class(data) %in% c("matrix", "array"))){ # before R4.0.0, it was all(class(data) %in% c("matrix", "data.frame", "table")) -if(rownames.kept == FALSE & all(class(data) == "data.frame") & nrow(data) != 0 & nrow(data) <= 4){ # for data frames with nrows <= 4 -rownames.output.tables <- "" -length.rows <- nrow(data) -for(i in 1:length.rows){ # replace the rownames of the first 4 rows by increasing number of spaces (because identical row names not allowed in data frames). This method cannot be extended to more rows as the printed data frame is shifted on the right because of "big empty rownames" -rownames.output.tables <- c(rownames.output.tables, paste0(rownames.output.tables[i]," ", collapse="")) -} -row.names(data) <- rownames.output.tables[1:length.rows] -}else if(rownames.kept == FALSE & (all(class(data) == "table") | all(class(data) %in% c("matrix", "array")))){ # before R4.0.0, it was & all(class(data) %in% c("matrix", "table")) -rownames(data) <- rep("", nrow(data)) # identical row names allowed in matrices and tables -} -if(noquote == TRUE){ -utils::capture.output(noquote(data), file=paste0(path, "/", output), append = ! overwrite) -}else{ -utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) -} -}else if(is.vector(data) & all(class(data) != "list") & (length(data) == 1L | vector.cat == TRUE)){ -if(noquote == TRUE){ -cat(noquote(data), file= paste0(path, "/", output), append = ! overwrite) -}else{ -cat(data, file= paste0(path, "/", output), append = ! overwrite) -} -}else if(all(base::mode(data) == "character")){ # characters (array, list, factor or vector with vector.cat = FALSE) -if(noquote == TRUE){ -utils::capture.output(noquote(data), file=paste0(path, "/", output), append = ! overwrite) -}else{ -utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) -} -}else{ # other object (S4 for instance, which do not like noquote() -utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) -} -sep.final <- paste0(rep("\n", sep), collapse = "") -write(sep.final, file= paste0(path, "/", output), append = TRUE) # add a sep -} -} - - -######## fun_get_message() #### return error/warning/other messages of an expression (that can be exported) - - -fun_get_message <- function( -data, -kind = "error", -header = TRUE, -print.no = FALSE, -text = NULL, -env = NULL -){ -# AIM -# evaluate an instruction written between "" and return the first of the error, or warning or standard (non error non warning) messages if ever exist -# using argument print.no = FALSE, return NULL if no message, which is convenient in some cases -# WARNINGS -# Only the first message is returned -# Always use the env argument when fun_get_message() is used inside functions -# The function does not prevent printing if print() is used inside the instruction tested. To prevent that, use tempo <- capture.output(error <- fun_get_message(data = "fun_check(data = 'a', class = mean, neg.values = FALSE, print = TRUE)")). The return of fun_get_message() is assigned into error and the printed messages are captured by capture.output() and assigned into tempo. See the examples -# ARGUMENTS -# data: character string to evaluate -# kind: character string. Either "error" to get error messages, or "warning" to get warning messages, or "message" to get non error and non warning messages -# header: logical. Add a header in the returned message? -# print.no: logical. Print a message saying that no message reported? -# text: character string added to the output message (even if no message exists and print.no is TRUE). Inactivated if header is FALSE -# env: the name of an existing environment. NULL if not required -# RETURN -# the message or NULL if no message and print.no is FALSE -# REQUIRED PACKAGES -# none -# REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION -# fun_check() -# EXAMPLES -# fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "error", print.no = TRUE, text = "IN A") -# fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "warning", print.no = TRUE, text = "IN A") -# fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "message", print.no = TRUE, text = "IN A") -# fun_get_message(data = "wilcox.test()", kind = "error", print.no = TRUE, text = "IN A") -# fun_get_message(data = "sum(1)", kind = "error", print.no = TRUE, text = "IN A") -# fun_get_message(data = "message('ahah')", kind = "error", print.no = TRUE, text = "IN A") -# fun_get_message(data = "message('ahah')", kind = "message", print.no = TRUE, text = "IN A") -# fun_get_message(data = "ggplot2::ggplot(data = data.frame(X = 1:10, stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()", kind = "message", print.no = TRUE, text = "IN FUNCTION 1") -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_get_message(data = 'fun_gg_boxplot(data = obs1, y = "Time", categ = "Group1")', kind = "message", print.no = TRUE, text = "IN FUNCTION 1") -# DEBUGGING -# data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)" ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging -# data = "sum(1)" ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; data = 'fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Group1")' ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging -# data = "message('ahah')" ; kind = "error" ; header = TRUE ; print.no = TRUE ; text = "IN A" ; env = NULL -# data = 'ggplot2::ggplot(data = data.frame(X = "a", stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()' ; kind = "message" ; header = TRUE ; print.no = FALSE ; text = NULL # for function debugging -# data = 'ggplot2::ggplot(data = data.frame(X = "a", stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()' ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL # for function debugging -# data = "emmeans::emmeans(object = emm.rg, specs = contrast.var)" ; kind = "message" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -# end function name -# required function checking -if(length(utils::find("fun_check", mode = "function")) == 0L){ -tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# no need to use reserved words to avoid bugs, because it is local, and exists("tempo.warning", inherit = FALSE), never use the scope -# argument checking -# argument checking with fun_check() -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data, class = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = kind, options = c("error", "warning", "message"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = print.no, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = header, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(text)){ -tempo <- fun_check(data = text, class = "character", length = 1, fun.name = function.name) ; eval(ee) -} -if( ! is.null(env)){ -tempo <- fun_check(data = env, class = "environment", fun.name = function.name) ; eval(ee) # -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# end argument checking with fun_check() -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument checking -# main code -pdf(file = NULL) # send plots into a NULL file, no pdf file created -window.nb <- dev.cur() -invisible(dev.set(window.nb)) -# last warning cannot be used because suppressWarnings() does not modify last.warning present in the base evironment (created at first warning in a new R session), or warnings() # to reset the warning history : unlockBinding("last.warning", baseenv()) ; assign("last.warning", NULL, envir = baseenv()) -output <- NULL -tempo.error <- try(suppressMessages(suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))), silent = TRUE) # get error message, not warning or messages -if(any(class(tempo.error) %in% c("gg", "ggplot"))){ -tempo.error <- try(suppressMessages(suppressWarnings(ggplot2::ggplot_build(tempo.error))), silent = TRUE)[1] -} -if(exists("tempo.error", inherit = FALSE) == TRUE){ # inherit = FALSE avoid the portee lexical and thus the declared word -if( ! all(class(tempo.error) == "try-error")){ # deal with NULL and S4 objects. Old code: ! (all(class(tempo.error) == "try-error") & any(grepl(x = tempo.error, pattern = "^Error|^error|^ERROR"))) but problem with S4 objects. Old code : if((length(tempo.error) > 0 & ! any(grepl(x = tempo.error, pattern = "^Error|^error|^ERROR"))) | (length(tempo.error) == 0) ){ but problem when tempo.error is a list but added this did not work: | ! all(class(tempo.error) == "character") -tempo.error <- NULL -} -}else{ -tempo.error <- NULL -} -if(kind == "error" & ! is.null(tempo.error)){ # -if(header == TRUE){ -tempo.error[1] <- gsub(x = tempo.error[1], pattern = "^Error i|^error i|^ERROR I", replacement = "I") -output <- paste0("ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.error[1]) # -}else{ -output <- tempo.error[1] # -} -}else if(kind == "error" & is.null(tempo.error) & print.no == TRUE){ -output <- paste0("NO ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -}else if(kind != "error" & ( ! is.null(tempo.error)) & print.no == TRUE){ -output <- paste0("NO ", ifelse(kind == "warning", "WARNING", "STANDARD (NON ERROR AND NON WARNING)"), " MESSAGE BECAUSE OF ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -}else if(is.null(tempo.error)){ -fun.warning.capture <- function(expr){ -# from demo(error.catching) typed in the R console, coming from ?tryCatch -# see also http://mazamascience.com/WorkingWithData/?p=912 -# return a character string or NULL -# expr <- wilcox.test.default(c(1, 1, 3), c(1, 2, 4), paired = TRUE) -W <- NULL -w.handler <- function(w){ # warning handler -W <<- w # send to the above env, i.e., the inside of the fun.warning.capture function -invokeRestart("muffleWarning") # here w.handler() muffles all the warnings. See http://romainfrancois.blog.free.fr/index.php?post/2009/05/20/Disable-specific-warnings to muffle specific warnings and print others -} -output <- list( -value = suppressMessages(withCallingHandlers(tryCatch(expr, error = function(e){e}), warning = w.handler)), # BEWARE: w.handler is a function written without (), like in other functions with FUN argument -warning = W # processed by w.handler() -) -return(if(is.null(output$warning)){NULL}else{as.character(output$warning)}) -} -tempo.warn <- fun.warning.capture(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env})) -# warn.options.ini <- options()$warn ; options(warn = 1) ; tempo.warn <- utils::capture.output({tempo <- suppressMessages(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))}, type = "message") ; options(warn = warn.options.ini) # this recover warnings not messages and not errors but does not work in all enviroments -tempo.message <- utils::capture.output({ -tempo <- suppressMessages(suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))) -if(any(class(tempo) %in% c("gg", "ggplot"))){ -tempo <- ggplot2::ggplot_build(tempo) -}else{ -tempo <- suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env})) -} -}, type = "message") # recover messages not warnings and not errors -if(kind == "warning" & ! is.null(tempo.warn)){ -if(length(tempo.warn) > 0){ # to avoid character(0) -if( ! any(sapply(tempo.warn, FUN = "grepl", pattern = "() FUNCTION:$"))){ -tempo.warn <- paste(unique(tempo.warn), collapse = "\n") # if FALSE, means that the tested data is a special function. If TRUE, means that the data is a standard function. In that case, the output of capture.output() is two strings per warning messages: if several warning messages -> identical first string, which is removed in next messages by unique() -}else{ -tempo.warn <- paste(tempo.warn, collapse = "\n") -} -if(header == TRUE){ -if(any(grepl(x = tempo.warn[[1]], pattern = "^simpleWarning i"))){ -tempo.warn[[1]] <- gsub(x = tempo.warn[[1]], pattern = "^Warning i", replacement = "I") -} -if(any(grepl(x = tempo.warn[[1]], pattern = "^Warning i"))){ -tempo.warn[[1]] <- gsub(x = tempo.warn[[1]], pattern = "^Warning i", replacement = "I") -} -output <- paste0("WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.warn) # -}else{ -output <- tempo.warn # -} -}else{ -if(print.no == TRUE){ -output <- paste0("NO WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -} # no need else{} here because output is already NULL at first -} -}else if(kind == "warning" & is.null(tempo.warn) & print.no == TRUE){ -output <- paste0("NO WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -}else if(kind == "message" & exists("tempo.message", inherit = FALSE) == TRUE){ # inherit = FALSE avoid the portee lexical and thus the declared word -if(length(tempo.message) > 0){ # if something is returned by capture.ouptput() (only in this env) with a length more than 1 -if(header == TRUE){ -output <- paste0("STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.message) # -}else{ -output <- tempo.message # -} -}else{ -if(print.no == TRUE){ -output <- paste0("NO STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -} # no need else{} here because output is already NULL at first -} -}else if(kind == "message" & exists("tempo.message", inherit = FALSE) == FALSE & print.no == TRUE){ -output <- paste0("NO STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) -} # no need else{} here because output is already NULL at first -} # no need else{} here because output is already NULL at first -invisible(dev.off(window.nb)) # end send plots into a NULL file -return(output) # do not use cat() because the idea is to reuse the message -} - - - - - -# Error: class order not good when a class is removed due to NA -# Error: line 136 in check 20201126 with add argument -# Solve this: sometimes error messages can be more than the max display (8170). Thus, check every paste0("ERROR IN ", function.name, and trunck the message if to big. In addition, add at the begining of the warning message that it is too long and see the $warn output for complete message. Add also this into fun_scatter -# add dot.shape ? See with available aesthetic layers -# rasterise: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html -# add horizontal argument and deal any conflict with vertical argument. Start with horizontal = NULL as default. If ! is.null() -> convert vertical if required - -fun_gg_boxplot <- function( -data1, -y, -categ, -categ.class.order = NULL, -categ.color = NULL, -box.legend.name = NULL, -box.fill = FALSE, -box.width = 0.5, -box.space = 0.1, -box.line.size = 0.75, -box.notch = FALSE, -box.alpha = 1, -box.mean = TRUE, -box.whisker.kind = "std", -box.whisker.width = 0, -dot.color = grey(0.25), -dot.categ = NULL, -dot.categ.class.order = NULL, -dot.legend.name = NULL, -dot.tidy = FALSE, -dot.tidy.bin.nb = 50, -dot.jitter = 0.5, -dot.seed = 2, -dot.size = 3, -dot.alpha = 0.5, -dot.border.size = 0.5, -dot.border.color = NULL, -x.lab = NULL, -x.angle = 0, -y.lab = NULL, -y.lim = NULL, -y.log = "no", -y.tick.nb = NULL, -y.second.tick.nb = 1, -y.include.zero = FALSE, -y.top.extra.margin = 0.05, -y.bottom.extra.margin = 0.05, -stat.pos = "top", -stat.mean = FALSE, -stat.size = 4, -stat.dist = 5, -stat.angle = 0, -vertical = TRUE, -text.size = 12, -title = "", -title.text.size = 8, -legend.show = TRUE, -legend.width = 0.5, -article = TRUE, -grid = FALSE, -add = NULL, -return = FALSE, -return.ggplot = FALSE, -return.gtable = TRUE, -plot = TRUE, -warn.print = FALSE, -lib.path = NULL -){ -# AIM -# Plot ggplot2 boxplots + dots + means -# For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html -# WARNINGS -# Rows containing NA in data1[, c(y, categ)] will be removed before processing, with a warning (see below) -# Hinges are not computed like in the classical boxplot() function of R. See https://ggplot2.tidyverse.org/reference/geom_boxplot.html -# To have a single box, please create a factor column with a single class and specify the name of this column in the categ argument. For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1, knowing that categ2 must also be specified in this situation). See categ argument below -# The dot.alpha argument can alter the display of the color boxes when using pdf output -# Size arguments (box.line.size, dot.size, dot.border.size, stat.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE)) -# Display seems to be done twice on Windows devices (like a blink). However, no double plots on pdf devices. Thus, the blink remains mysterious -# To remove boxes and have only dots, use box.alpha = 0 -# ARGUMENTS -# data1: data frame containing one column of quantitative values (see the y argument below) and one or two columns of categories (see the categ argument below). Duplicated column names are not allowed -# y: character string of the data1 column name for y-axis (column containing numeric values). Numeric values will be split according to the classes of the column names indicated in the categ argument to generate the boxes and will also be used to plot the dots -# categ: vector of character strings of the data1 column name for categories (column of characters or factors). Must be either one or two column names. If a single column name (further referred to as categ1), then one box per class of categ1. If two column names (further referred to as categ1 and categ2), then one box per class of categ2, which form a group of boxes in each class of categ1. WARNING: no empty classes allowed. To have a single box, create a factor column with a single class and specify the name of this column in the categ argument (here, no categ2 in categ argument). For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1), in addition to the already used category (as categ2 in this situation) -# categ.class.order: list indicating the order of the classes of categ1 and categ2 represented on the boxplot (the first compartment for categ1 and and the second for categ2). If categ.class.order == NULL, classes are represented according to the alphabetical order. Some compartments can be NULL and others not. See the categ argument for categ1 and categ2 description -# categ.color: vector of color character string for box frames (see the categ argument for categ1 and categ2 description) -# If categ.color == NULL, default colors of ggplot2, whatever categ1 and categ2 -# If categ.color is non-null and only categ1 in categ argument, categ.color can be either: -# (1) a single color string. All the boxes will have this color, whatever the number of classes of categ1 -# (2) a vector of string colors, one for each class of categ1. Each color will be associated according to categ.class.order of categ1 -# (3) a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ1 and a single class of categ1 per color must be respected -# Color functions, like grey(), hsv(), etc., are also accepted -# Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the maximal integer value among all the integers in categ.color (see fun_gg_palette()) -# If categ.color is non-null and categ1 and categ2 are specified, all the rules described above will apply to categ2 instead of categ1 (colors will be determined for boxes inside a group of boxes) -# box.legend.name: character string of the legend title. If box.legend.name is NULL, then box.legend.name <- categ1 if only categ1 is present, and box.legend.name <- categ2 if categ1 and categ2 are present in the categ argument. Write "" if no legend required. See the categ argument for categ1 and categ2 description -# box.fill: logical. Fill the box? If TRUE, the categ.color argument will be used to generate filled boxplots (the box frames being black) as well as filled outlier dots (the dot border being controlled by the dot.border.color argument). If all the dots are plotted (argument dot.color other than NULL), they will be over the boxes. If FALSE, the categ.color argument will be used to color the box frames and the outlier dot borders. If all the dots are plotted, they will be beneath the boxes -# box.width: single numeric value (from 0 to 1) of width of either boxes or group of boxes -# When categ argument has a single categ1 element (i.e., separate boxes. See the categ argument for categ1 and categ2 description), then each class of categ1 is represented by a single box. In that case, box.width argument defines each box width, from 0 (no box width) to 1 (max box width), but also the space between boxes (the code uses 1 - box.width for the box spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each box) -# When categ argument has a two categ1 and categ2 elements (i.e., grouped boxes), box.width argument defines the width allocated for each set of grouped boxes, from 0 (no group width) to 1 (max group width), but also the space between grouped boxes (the code uses 1 - box.width for the spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each set of grouped box) -# box.space: single numeric value (from 0 to 1) indicating the box separation inside grouped boxes, when categ argument has a two categ1 and categ2 elements. 0 means no space and 1 means boxes shrunk to a vertical line. Ignored if categ argument has a single categ1 element -# box.line.size: single numeric value of line width of boxes and whiskers in mm -# box.notch: logical. Notched boxplot? It TRUE, display notched boxplot, notches corresponding approximately to the 95% confidence interval of the median (the notch interval is exactly 1.58 x Inter Quartile Range (IQR) / sqrt(n), with n the number of values that made the box). If notch intervals between two boxes do not overlap, it can be interpreted as significant median differences -# box.alpha: single numeric value (from 0 to 1) of box transparency (full transparent to full opaque, respectively). To remove boxplots, use box.alpha = 0 -# box.mean: logical. Add mean value? If TRUE, a diamond-shaped dot, with the horizontal diagonal corresponding to the mean value, is displayed over each boxplot -# box.whisker.kind: range of the whiskers. Either "no" (no whiskers), or "std" (length of each whisker equal to 1.5 x Inter Quartile Range (IQR)), or "max" (length of the whiskers up or down to the most distant dot) -# box.whisker.width: single numeric value (from 0 to 1) of the whisker width, with 0 meaning no whiskers and 1 meaning a width equal to the box width -# dot.color: vector of color character string ruling the dot colors and the dot display. See the example section below for easier understanding of the rules described here -# If NULL, no dots plotted -# If "same", the dots will have the same colors as the respective boxplots -# Otherwise, as in the rule (1), (2) or (3) described in the categ.color argument, except that in the possibility (3), the rule "a single color per class of categ and a single class of categ per color", does not have to be respected (for instance, each dot can have a different color). Colors will also depend on the dot.categ argument. If dot.categ is NULL, then colors will be applied to each class of the last column name specified in categ. If dot.categ is non-NULL, colors will be applied to each class of the column name specified in dot.categ. See examples -# dot.categ: optional single character string of a column name (further referred to as categ3) of the data1 argument. This column of data1 will be used to generate a legend for dots, in addition to the legend for boxes. See the dot.color argument for details about the way the legend is built using the two dot.categ and dot.color arguments. If NULL, no legend created and the colors of dots will depend on dot.color and categ arguments (as explained in the dot.color argument) -# dot.categ.class.order: optional vector of character strings indicating the order of the classes of categ3 (see the dot.categ argument). If dot.categ is non-NULL and dot.categ.class.order is NULL, classes are displayed in the legend according to the alphabetical order. Ignored if dot.categ is NULL -# dot.legend.name: optional character string of the legend title for categ3 (see the dot.categ argument). If dot.legend.name == NULL, dot.categ value is used (name of the column in data1). Write "" if no legend required. Ignored if dot.categ is NULL -# dot.tidy: logical. Nice dot spreading? If TRUE, use the geom_dotplot() function for a nice representation. WARNING: change the true quantitative coordinates of dots (i.e., y-axis values for vertical display) because of binning. Thus, the gain in aestheticism is associated with a loss in precision that can be very important. If FALSE, dots are randomly spread on the qualitative axis, using the dot.jitter argument (see below) keeping the true quantitative coordinates -# dot.tidy.bin.nb: positive integer indicating the number of bins (i.e., nb of separations) of the y.lim range. Each dot will then be put in one of the bin, with a diameter of the width of the bin. In other words, increase the number of bins to have smaller dots. Not considered if dot.tidy is FALSE -# dot.jitter: numeric value (from 0 to 1) of random dot horizontal dispersion (for vertical display), with 0 meaning no dispersion and 1 meaning dispersion in the corresponding box width interval. Not considered if dot.tidy is TRUE -# dot.seed: integer value that set the random seed. Using the same number will generate the same dot jittering. Write NULL to have different jittering each time the same instruction is run. Ignored if dot.tidy is TRUE -# dot.size: numeric value of dot diameter in mm. Not considered if dot.tidy is TRUE -# dot.alpha: numeric value (from 0 to 1) of dot transparency (full transparent to full opaque, respectively) -# dot.border.size: numeric value of border dot width in mm. Write zero for no dot border. If dot.tidy is TRUE, value 0 remove the border and other values leave the border without size control (geom_doplot() feature) -# dot.border.color: single character color string defining the color of the dot border (same color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette() -# x.lab: a character string or expression for x-axis legend. If NULL, character string of categ1 (see the categ argument for categ1 and categ2 description) -# x.angle: integer value of the text angle for the x-axis numbers, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. -# y.lab: a character string or expression for y-axis legend. If NULL, character string of the y argument -# y.lim: 2 numeric values indicating the range of the y-axis. Order matters (for inverted axis). If NULL, the range of the x column name of data1 will be used. -# y.log: either "no", "log2" (values in the y argument column of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y argument column of the data1 data frame will be log10 transformed and y-axis will be log10 scaled). WARNING: not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881) -# y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) -# y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks -# y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10" -# y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis -# y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis -# stat.pos: add the median number above the corresponding box. Either NULL (no number shown), "top" (at the top of the plot region) or "above" (above each box) -# stat.mean: logical. Display mean numbers instead of median numbers? Ignored if stat.pos is NULL -# stat.size: numeric value of the stat font size in mm. Ignored if stat.pos is NULL -# stat.dist: numeric value of the stat distance in percentage of the y-axis range (stat.dist = 5 means move the number displayed at 5% of the y-axis range). Ignored if stat.pos is NULL or "top" -# stat.angle: integer value of the angle of stat, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. -# vertical: logical. Vertical boxes? WARNING: will be automatically set to TRUE if y.log argument is other than "no". Indeed, not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881) -# text.size: numeric value of the font size of the (1) axis numbers, (2) axis labels and (3) texts in the graphic legend (in mm) -# title: character string of the graph title -# title.text.size: numeric value of the title font size in mm -# legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not -# legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend -# article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme -# grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present) -# add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL -# WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()" -# If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_boxplot() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments) -# Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions -# WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_boxplot() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))' -# return: logical. Return the graph parameters? -# return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_boxplot() function (e.g., a <- fun_gg_boxplot()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details -# return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details -# plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting -# warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE -# lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders -# RETURN -# A boxplot if plot argument is TRUE -# A list of the graph info if return argument is TRUE: -# $data: the initial data with graphic information added -# $stat: the graphic statistics (mostly equivalent to ggplot_build()$data[[2]]) -# $removed.row.nb: which rows have been removed due to NA/Inf detection in y and categ columns (NULL if no row removed) -# $removed.rows: removed rows (NULL if no row removed) -# $plot: the graphic box and dot coordinates -# $dots: dot coordinates -# $main.box: coordinates of boxes -# $median: median coordinates -# $sup.whisker: coordinates of top whiskers (y for base and y.end for extremities) -# $inf.whisker: coordinates of bottom whiskers (y for base and y.end for extremities) -# $sup.whisker.edge: coordinates of top whisker edges (x and xend) -# $inf.whisker.edge: coordinates of bottom whisker edges(x and xend) -# $mean: diamond mean coordinates (only if box.mean argument is TRUE) -# $stat.pos: coordinates of stat numbers (only if stat.pos argument is not NULL) -# y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no") -# y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no") -# $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0 -# $axes: the x-axis and y-axis info -# $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE -# $ggplot: ggplot object that can be used for reprint (use print(...$ggplot) or update (use ...$ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-NULL $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot -# $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot -# REQUIRED PACKAGES -# ggplot2 -# gridExtra -# lemon (in case of use in the add argument) -# scales -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# fun_check() -# fun_comp_1d() -# fun_comp_2d() -# fun_gg_just() -# fun_gg_palette() -# fun_inter_ticks() -# fun_name_change() -# fun_pack() -# fun_round() -# fun_scale() -# EXAMPLE -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(20, 100, 10), rnorm(20, 200, 50), rnorm(20, 500, 60), rnorm(20, 100, 50)), Categ1 = rep(c("CAT", "DOG"), times = 40), Categ2 = rep(c("A", "B", "C", "D"), each = 20), Color1 = rep(c("coral", "lightblue"), times = 40), Color2 = rep(c("#9F2108", "#306100", "#007479", "#8500C0"), each = 20), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Categ1") -# see http -# DEBUGGING -# set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Categ1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$Time[1:10] <- NA ; data1 = obs1 ; y = "Time" ; categ = c("Categ1") ; categ.class.order = NULL ; categ.color = NULL ; box.legend.name = NULL ; box.fill = FALSE ; box.width = 0.5 ; box.space = 0.1 ; box.line.size = 0.75 ; box.notch = FALSE ; box.alpha = 1 ; box.mean = TRUE ; box.whisker.kind = "std" ; box.whisker.width = 0 ; dot.color = grey(0.25) ; dot.categ = NULL ; dot.categ.class.order = NULL ; dot.legend.name = NULL ; dot.tidy = FALSE ; dot.tidy.bin.nb = 50 ; dot.jitter = 0.5 ; dot.seed = 2 ; dot.size = 3 ; dot.alpha = 0.5 ; dot.border.size = 0.5 ; dot.border.color = NULL ; x.lab = NULL ; x.angle = 0 ; y.lab = NULL ; y.lim = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = 1 ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; stat.pos = "top" ; stat.mean = FALSE ; stat.size = 4 ; stat.dist = 5 ; stat.angle = 0 ; vertical = TRUE ; text.size = 12 ; title = "" ; title.text.size = 8 ; legend.show = TRUE ; legend.width = 0.5 ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_comp_2d", -"fun_gg_just", -"fun_gg_palette", -"fun_name_change", -"fun_pack", -"fun_check", -"fun_round", -"fun_scale", -"fun_inter_ticks" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function")) == 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words to avoid bugs (names of dataframe columns used in this function) -reserved.words <- c("categ.check", "categ.color", "dot.color", "dot.categ", "dot.max", "dot.min", "group", "PANEL", "group.check", "MEAN", "tempo.categ1", "tempo.categ2", "text.max.pos", "text.min.pos", "x", "x.y", "y", "y.check", "y_from.dot.max", "ymax", "tidy_group", "binwidth") -# end reserved words to avoid bugs (used in this function) -# arg with no default values -mandat.args <- c( -"data1", -"y", -"categ" -) -tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")"))) -if(any(tempo)){ # normally no NA for missing() output -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = categ, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if( ! is.null(categ.class.order)){ -tempo <- fun_check(data = categ.class.order, class = "list", fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = categ.class.order, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(box.legend.name)){ -tempo <- fun_check(data = box.legend.name, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = box.legend.name, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(categ.color)){ -tempo1 <- fun_check(data = categ.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = categ.color, class = "factor", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem -if(tempo.check.color == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(any(categ.color == 0L, na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = categ.color, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = box.fill, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.space, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.notch, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.mean, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.whisker.kind, options = c("no", "std", "max"), length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = box.whisker.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(dot.color)){ -tempo1 <- fun_check(data = dot.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = dot.color, class = "factor", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem -if(tempo.check.color == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(any(dot.color == 0L, na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.color, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(dot.categ)){ -tempo <- fun_check(data = dot.categ, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.categ, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(dot.categ.class.order)){ -tempo <- fun_check(data = dot.categ.class.order, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.categ.class.order, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(dot.legend.name)){ -tempo <- fun_check(data = dot.legend.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.legend.name, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = dot.tidy, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dot.tidy.bin.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if(dot.tidy.bin.nb == 0L){ # length and NA checked above -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.tidy.bin.nb ARGUMENT MUST BE A NON-NULL AND POSITVE INTEGER VALUE") # integer possible because dealt above -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -tempo <- fun_check(data = dot.jitter, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(dot.seed)){ -tempo <- fun_check(data = dot.seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.seed, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dot.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if( ! is.null(dot.border.color)){ -tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -}else if(tempo1$problem == FALSE & tempo2$problem == TRUE){ -if( ! all(dot.border.color %in% colors() | grepl(pattern = "^#", dot.border.color), na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.border.color, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(x.lab)){ -tempo1 <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x.lab, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = x.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) -if( ! is.null(y.lab)){ -tempo1 <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.lab, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(y.lim)){ -tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if(any(is.infinite(y.lim))){ # normally no NA for is.infinite() output -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.lim, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(y.tick.nb)){ -tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if(y.tick.nb < 0){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(y.second.tick.nb)){ -tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if(y.second.tick.nb <= 0){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.second.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.second.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(stat.pos)){ -tempo <- fun_check(data = stat.pos, options = c("top", "above"), length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = stat.pos, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = stat.mean, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = stat.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = stat.dist, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = stat.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = vertical, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(legend.width)){ -tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = legend.width, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(add)){ -tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = add, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path), na.rm = TRUE)){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = lib.path, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if(any(arg.check) == TRUE){ # normally no NA -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"data1", -"y", -"categ", -"box.fill", -"box.width", -"box.space", -"box.line.size", -"box.notch", -"box.alpha", -"box.mean", -"box.whisker.kind", -"box.whisker.width", -# "dot.color", # inactivated because can be null -"dot.tidy", -"dot.tidy.bin.nb", -"dot.jitter", -# "dot.seed", # inactivated because can be null -"dot.size", -"dot.alpha", -"dot.border.size", -"x.angle", -"y.log", -# "y.second.tick.nb", # inactivated because can be null -"y.include.zero", -"y.top.extra.margin", -"y.bottom.extra.margin", -# "stat.pos", # inactivated because can be null -"stat.mean", -"stat.size", -"stat.dist", -"stat.angle", -"vertical", -"text.size", -"title", -"title.text.size", -"legend.show", -# "legend.width", # inactivated because can be null -"article", -"grid", -"return", -"return.ggplot", -"return.gtable", -"plot", -"warn.print" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){# normally no NA with is.null() -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# see also Protocol 100-rev0 Parallelization in R.docx -if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment -tempo.random.seed <- .Random.seed -on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) -}else{ -on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness -} -set.seed(dot.seed) -# end code that protects set.seed() in the global environment -# warning initiation -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -# end warning initiation -# other checkings -if(any(duplicated(names(data1)), na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! (y %in% names(data1))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE A COLUMN NAME OF data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -tempo <- fun_check(data = data1[, y], data.name = "y COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) -if(tempo$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE NUMERIC COLUMN IN data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(length(categ) > 2){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT CANNOT HAVE MORE THAN 2 COLUMN NAMES OF data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! all(categ %in% names(data1))){ # all() without na.rm -> ok because categ cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(categ, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(length(dot.categ) > 1){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT HAVE MORE THAN 1 COLUMN NAMES OF data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! all(dot.categ %in% names(data1))){ # all() without na.rm -> ok because dot.categ cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(dot.categ, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# reserved word checking -if(any(names(data1) %in% reserved.words, na.rm = TRUE)){ -if(any(duplicated(names(data1)), na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(dot.categ)){ -if(dot.categ %in% categ){ -reserved.words <- c(reserved.words, paste0(dot.categ, "_DOT")) # paste0(dot.categ, "_DOT") is added to the reserved words because in such situation, a new column will be added to data1 that is named paste0(dot.categ, "_DOT") -} -} -tempo.output <- fun_name_change(names(data1), reserved.words) -for(i2 in 1:length(tempo.output$ini)){ # a loop to be sure to take the good ones -names(data1)[names(data1) == tempo.output$ini[i2]] <- tempo.output$post[i2] -if(any(y == tempo.output$ini[i2])){ # any() without na.rm -> ok because y cannot be NA (tested above) -y[y == tempo.output$ini[i2]] <- tempo.output$post[i2] -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN y ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN y ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# WARNING: names of y argument potentially replaced -if(any(categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because categ cannot be NA (tested above) -categ[categ == tempo.output$ini[i2]] <- tempo.output$post[i2] -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# WARNING: names of categ argument potentially replaced -if( ! is.null(dot.categ)){ -if(any(dot.categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because dot.categ cannot be NA (tested above) -dot.categ[dot.categ == tempo.output$ini[i2]] <- tempo.output$post[i2] -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN dot.categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# WARNING: names of dot.categ argument potentially replaced -} -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") REGARDING COLUMN NAMES REPLACEMENT, THE NAMES\n", paste(tempo.output$ini, collapse = " "), "\nHAVE BEEN REPLACED BY\n", paste(tempo.output$post, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -if( ! (is.null(add) | is.null(tempo.output$ini))){ -if(grepl(x = add, pattern = paste(tempo.output$ini, collapse = "|"))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nCOLUMN NAMES HAVE TO BE CHANGED\nTHE PROBLEMATIC COLUMN NAMES ARE SOME OF THESE NAMES:\n", paste(tempo.output$ini, collapse = " "), "\nIN THE DATA FRAME OF data1 AND IN THE STRING OF add ARGUMENT, TRY TO REPLACE NAMES BY:\n", paste(tempo.output$post, collapse = " "), "\n\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -} -if( ! (is.null(add))){ -if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end reserved word checking -# verif of add -if( ! is.null(add)){ -if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by + -tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ # -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by ) -tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end verif of add -# management of add containing facet -facet.categ <- NULL -if( ! is.null(add)){ -facet.check <- TRUE -tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) # -tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap") -tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid") -tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep") -if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){ -tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")]))) -facet.categ <- names(tempo1$params$facets) -tempo.text <- "facet_wrap OR facet_rep_wrap" -facet.check <- FALSE -}else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){ -tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")]))) -facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols)) -tempo.text <- "facet_grid OR facet_rep_grid" -facet.check <- FALSE -} -if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end management of add containing facet -# conversion of categ columns in data1 into factors -for(i1 in 1:length(categ)){ -tempo1 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\n", paste0("categ NUMBER ", i1, " OF data1"), " MUST BE A FACTOR OR CHARACTER VECTOR") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(tempo1$problem == FALSE){ # character vector -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN categ NUMBER ", i1, " IN data1, THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR, WITH LEVELS ACCORDING TO THE ALPHABETICAL ORDER") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -data1[, categ[i1]] <- factor(data1[, categ[i1]]) # if already a factor, change nothing, if characters, levels according to alphabetical order -} -# OK: all the categ columns of data1 are factors from here -# end conversion of categ columns in data1 into factors - - - -# management of log scale and Inf removal -if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite0() and is.na() -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -data1.ini <- data1 # strictly identical to data1 except that in data1 y is log converted if and only if y.log != "no" -if(y.log != "no"){ -tempo1 <- ! is.finite(data1[, y]) # where are initial NA and Inf -data1[, y] <- suppressWarnings(get(y.log)(data1[, y]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -if(any( ! (tempo1 | is.finite(data1[, y])))){ # normally no NA with is.finite -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# Inf removal -if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite -removed.row.nb <- which(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y]))) -removed.rows <- data1.ini[removed.row.nb, ] # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation -data1 <- data1[-removed.row.nb, ] # -data1.ini <- data1.ini[-removed.row.nb, ] # -}else{ -removed.row.nb <- NULL -removed.rows <- data.frame(stringsAsFactors = FALSE) -} -# From here, data1 and data.ini have no more Inf -# end Inf removal -if(y.log != "no" & ! is.null(y.lim)){ -if(any(y.lim <= 0)){ # any() without na.rm -> ok because y.lim cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ # normally no NA with is.finite -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(y.log != "no" & y.include.zero == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -y.include.zero <- FALSE -} -if(y.log != "no" & vertical == FALSE){ -vertical <- TRUE -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") BECAUSE OF A BUG IN ggplot2, CANNOT FLIP BOXES HORIZONTALLY WITH A Y.LOG SCALE -> vertical ARGUMENT RESET TO TRUE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end management of log scale and Inf removal -# na detection and removal (done now to be sure of the correct length of categ) -column.check <- unique(c(y, categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){dot.categ}, if( ! is.null(facet.categ)){facet.categ})) # dot.categ because can be a 3rd column of data1, categ.color and dot.color will be tested later -if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -for(i2 in 1:length(column.check)){ -if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na -tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn))) -} -} -tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which))) -removed.row.nb <- c(removed.row.nb, tempo) # removed.row.nb created to remove Inf -removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance) -column.check <- column.check[ ! column.check == y] # remove y to keep quali columns -if(length(tempo) != 0){ -data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former -data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former -for(i3 in 1:length(column.check)){ -if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -} -count.categ <- 0 -for(i2 in 1:length(column.check)){ -if(column.check[i2] %in% categ){ -count.categ <- count.categ + 1 -} -if(column.check[i2] == categ[count.categ]){ -categ.class.order[count.categ] <- list(levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])]) # remove the absent color in the character vector -data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.class.order[[count.categ]])) -} -if( ! is.null(dot.color) & ! is.null(dot.categ)){ # reminder : dot.categ cannot be a column name of categ anymore (because in that case dot.categ name is changed into "..._DOT" -if(column.check[i2] == dot.categ){ -dot.categ.class.order <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector -data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.categ.class.order)) -} -} -if(column.check[i2] %in% facet.categ){ # works if facet.categ == NULL this method should keep the order of levels when removing some levels -tempo.levels <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(as.character(data1[, column.check[i2]]))] -data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = tempo.levels) -} -} -} -# end na detection and removal (done now to be sure of the correct length of categ) -# From here, data1 and data.ini have no more NA or NaN in y, categ, dot.categ (if dot.color != NULL) and facet.categ - - - -if( ! is.null(categ.class.order)){ -if(length(categ.class.order) != length(categ)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.class.order ARGUMENT MUST BE A LIST OF LENGTH EQUAL TO LENGTH OF categ\nHERE IT IS LENGTH: ", length(categ.class.order), " VERSUS ", length(categ)) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -for(i3 in 1:length(categ.class.order)){ -if(is.null(categ.class.order[[i3]])){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i3, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -data1[, categ[i3]] <- factor(as.character(data1[, categ[i3]])) # if already a factor, change nothing, if characters, levels according to alphabetical order -categ.class.order[[i3]] <- levels(data1[, categ[i3]]) # character vector that will be used later -}else{ -tempo <- fun_check(data = categ.class.order[[i3]], data.name = paste0("COMPARTMENT ", i3 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[, categ[i3]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(any(duplicated(categ.class.order[[i3]]), na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i3]], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! (all(categ.class.order[[i3]] %in% unique(data1[, categ[i3]]), na.rm = TRUE) & all(unique(data1[, categ[i3]]) %in% categ.class.order[[i3]], na.rm = TRUE))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT MUST BE CLASSES OF ELEMENT ", i3, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i3]], collapse = " "), "\nFOR COMPARTMENT ", i3, " OF categ.class.order AND IT IS:\n", paste(unique(data1[, categ[i3]]), collapse = " "), "\nFOR COLUMN ", categ[i3], " OF data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -data1[, categ[i3]] <- factor(data1[, categ[i3]], levels = categ.class.order[[i3]]) # reorder the factor - -} -names(categ.class.order)[i3] <- categ[i3] -} -} -}else{ -categ.class.order <- vector("list", length = length(categ)) -tempo.categ.class.order <- NULL -for(i2 in 1:length(categ.class.order)){ -categ.class.order[[i2]] <- levels(data1[, categ[i2]]) -names(categ.class.order)[i2] <- categ[i2] -tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]]) -} -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR BOX ORDERING:\n", paste(tempo.categ.class.order, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# categ.class.order not NULL anymore (list) -if(is.null(box.legend.name) & box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE box.legend.name SETTING IS NULL. NAMES OF categ WILL BE USED: ", paste(categ, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -box.legend.name <- categ[length(categ)] # if only categ1, then legend name of categ1, if length(categ) == 2L, then legend name of categ2 -} -# box.legend.name not NULL anymore (character string) -# management of categ.color -if( ! is.null(categ.color)){ -# check the nature of color -# integer colors into gg_palette -tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem -if(tempo.check.color == FALSE){ -# convert integers into colors -categ.color <- fun_gg_palette(max(categ.color, na.rm = TRUE))[categ.color] -} -# end integer colors into gg_palette -if( ! (all(categ.color %in% colors() | grepl(pattern = "^#", categ.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because categ.color cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors(): ", paste(unique(categ.color), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(is.na(categ.color)) & box.alpha != 0){ # normally no NA with is.na -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT CONTAINS NA") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end check the nature of color -# check the length of color -categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 -if(length(data1[, categ[categ.len]]) == length(levels(data1[, categ[categ.len]])) & length(categ.color) == length(data1[, categ[categ.len]])){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", categ[categ.len], " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE categ.color ARGUMENT ARE ALL EQUAL. BOX COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", categ[categ.len], ", NOT ACCORDING TO THE ROWS OF ", categ[categ.len]) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if(length(categ.color) == length(levels(data1[, categ[categ.len]]))){ # here length(categ.color) is equal to the different number of categ -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) # no need stringsAsFactors here for stat.nolog as factors remain factors -data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]]) -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(categ.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else if(length(categ.color) == length(data1[, categ[categ.len]])){# here length(categ.color) is equal to nrow(data1) -> Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]]))) -data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE) -tempo.check <- unique(data1[ , c(categ[categ.len], "categ.color")]) -if( ! (nrow(tempo.check) == length(unique(categ.color)) & nrow(tempo.check) == length(unique(data1[ , categ[categ.len]])))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], ":\n", paste(unique(mapply(FUN = "paste", data1[ ,categ[categ.len]], data1[ ,"categ.color"])), collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -categ.color <- unique(data1$categ.color[order(data1[, categ[categ.len]])]) # Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]]))) -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], " AS:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\n", paste(categ.color, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -}else if(length(categ.color) == 1L){ -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE) -categ.color <- rep(categ.color, length(levels(data1[, categ[categ.len]]))) -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(categ.color, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(categ.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -categ.color <- fun_gg_palette(length(levels(data1[, categ[categ.len]]))) -data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) -data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]]) -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL categ.color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", categ[categ.len], " IN data1:\n", paste(categ.color, collapse = " "), "\n", paste(levels(data1[, categ[categ.len]]), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# categ.color not NULL anymore -categ.color <- as.character(categ.color) -# categ.color is a character string representing the diff classes -data1$categ.color <- factor(data1$categ.color, levels = unique(categ.color)) # ok because if categ.color is a character string, the order make class 1, class 2, etc. unique() because no duplicates allowed -# data1$categ.color is a factor with order of levels -> categ.color -# end management of categ.color -# management of dot.color -if( ! is.null(dot.color)){ -# optional legend of dot colors -if( ! is.null(dot.categ)){ -ini.dot.categ <- dot.categ -if( ! dot.categ %in% names(data1)){ # no need to use all() because length(dot.categ) = 1 -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE A COLUMN NAME OF data1. HERE IT IS:\n", dot.categ) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(dot.categ %in% categ){ # no need to use all() because length(dot.categ) = 1. Do not use dot.categ %in% categ[length(categ)] -> error -# management of dot legend if dot.categ %in% categ (because legends with the same name are joined in ggplot2) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE COLUMN NAME OF data1 INDICATED IN THE dot.categ ARGUMENT (", dot.categ, ") HAS BEEN REPLACED BY ", paste0(dot.categ, "_DOT"), " TO AVOID MERGED LEGEND BY GGPLOT2") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -data1 <- data.frame(data1, dot.categ = data1[, dot.categ], stringsAsFactors = TRUE) # dot.categ is not a column name of data1 (checked above with reserved words) -dot.categ <- paste0(dot.categ, "_DOT") -names(data1)[names(data1) == "dot.categ"] <- dot.categ # paste0(dot.categ, "_DOT") is not a column name of data1 (checked above with reserved words) -# tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT BE A COLUMN NAME OF data1 ALREADY SPECIFIED IN THE categ ARGUMENT:\n", dot.categ, "\nINDEED, dot.categ ARGUMENT IS MADE TO HAVE MULTIPLE DOT COLORS NOT RELATED TO THE BOXPLOT CATEGORIES") -# stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -tempo1 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ COLUMN MUST BE A FACTOR OR CHARACTER VECTOR") # -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -data1[, dot.categ] <- factor(data1[, dot.categ]) # if already a factor, change nothing, if characters, levels according to alphabetical order -# dot.categ column of data1 is factor from here -if( ! is.null(dot.categ.class.order)){ -if(any(duplicated(dot.categ.class.order), na.rm = TRUE)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(dot.categ.class.order, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! (all(dot.categ.class.order %in% levels(data1[, dot.categ])) & all(levels(data1[, dot.categ]) %in% dot.categ.class.order, na.rm = TRUE))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT MUST BE CLASSES OF dot.categ ARGUMENT\nHERE IT IS:\n", paste(dot.categ.class.order, collapse = " "), "\nFOR dot.categ.class.order AND IT IS:\n", paste(levels(data1[, dot.categ]), collapse = " "), "\nFOR dot.categ COLUMN (", ini.dot.categ, ") OF data1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor -} -}else{ -if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) -dot.categ.class.order <- unlist(categ.class.order[length(categ)]) -data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL AND dot.color IS \"same\". ORDER OF categ.class.order WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -dot.categ.class.order <- sort(levels(data1[, dot.categ])) -data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# dot.categ.class.order not NULL anymore (character string) if dot.categ is not NULL -if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) -if( ! identical(ini.dot.categ, categ[length(categ)])){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\", THE COLUMN NAME IN dot.categ ARGUMENT MUST BE IDENTICAL TO THE LAST COLUMN NAME IN categ ARGUMENT. HERE IT IS:\ndot.categ: ", paste(ini.dot.categ, collapse = " "), "\ncateg: ", paste(categ, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if( ! fun_comp_1d(unlist(categ.class.order[length(categ)]), dot.categ.class.order)$identical.content){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\",\nLAST COMPARTMENT OF categ.class.order ARGUMENT AND dot.categ.class.order ARGUMENT CANNOT BE DIFFERENT:\nLAST COMPARTMENT OF categ.class.order: ", paste(unlist(categ.class.order[length(categ)]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -for(i3 in 1:length(categ)){ -if(identical(categ[i3], ini.dot.categ) & ! identical(unlist(categ.class.order[i3]), dot.categ.class.order) & identical(sort(unlist(categ.class.order[i3])), sort(dot.categ.class.order))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE dot.categ ARGUMENT SETTING IS PRESENT IN THE categ ARGUMENT SETTING, BUT ORDER OF THE CLASSES IS NOT THE SAME:\ncateg.class.order: ", paste(unlist(categ.class.order[i3]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "), "\nNOTE THAT ORDER OF categ.class.order IS THE ONE USED FOR THE AXIS REPRESENTATION") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(is.null(dot.legend.name)){ -dot.legend.name <- if(ini.dot.categ %in% categ[length(categ)]){dot.categ}else{ini.dot.categ} # -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE dot.legend.name SETTING IS NULL -> ", dot.legend.name, " WILL BE USED AS LEGEND TITLE OF DOTS") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# dot.legend.name not NULL anymore (character string) -}else{ -if( ! is.null(dot.categ.class.order)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order ARGUMENT IS NOT NULL, BUT IS THE dot.categ ARGUMENT\n-> dot.categ.class.order NOT CONSIDERED AS NO LEGEND WILL BE DRAWN") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# But dot.categ.class.order will be converted to NULL below (not now) -} -# end optional legend of dot colors -# check the nature of color -# integer colors into gg_palette -tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem -if(tempo.check.color == FALSE){ -# convert integers into colors -dot.color <- fun_gg_palette(max(dot.color, na.rm = TRUE))[dot.color] -} -# end integer colors into gg_palette -if(all(dot.color == "same") & length(dot.color)== 1L){# all() without na.rm -> ok because dot.color cannot be NA (tested above) -dot.color <- categ.color # same color of the dots as the corresponding box color -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS BEEN SET TO \"same\"\nTHUS, DOTS WILL HAVE THE SAME COLORS AS THE CORRESPONDING BOXPLOT") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else if( ! (all(dot.color %in% colors() | grepl(pattern = "^#", dot.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because dot.color cannot be NA (tested above) -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR VECTOR STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGERS, OR THE STRING \"same\"\nHERE IT IS: ", paste(unique(dot.color), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(any(is.na(dot.color))){ # normally no NA with is.finite -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT CONTAINS NA") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end check the nature of color -# check the length of color -if( ! is.null(dot.categ)){ -# optional legend of dot colors -if(length(data1[, dot.categ]) == length(levels(data1[, dot.categ])) & length(dot.color) == length(data1[, dot.categ])){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", dot.categ, " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE dot.color ARGUMENT ARE ALL EQUAL. DOT COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", dot.categ, ", NOT ACCORDING TO THE ROWS OF ", dot.categ) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if(length(dot.color) > 1 & ! (length(dot.color) == length(unique(data1[, dot.categ])) | length(dot.color) == length(data1[, dot.categ]))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN LENGTH OF THE dot.color ARGUMENT IS MORE THAN 1, IT MUST BE EQUAL TO THE NUMBER OF 1) ROWS OR 2) LEVELS OF dot.categ COLUMN (", dot.categ, "):\ndot.color: ", paste(dot.color, collapse = " "), "\ndot.categ LEVELS: ", paste(levels(data1[, dot.categ]), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else if(length(dot.color) > 1 & length(dot.color) == length(unique(data1[, dot.categ]))){ -data1 <- data.frame(data1, dot.color = data1[, dot.categ], stringsAsFactors = TRUE) -data1$dot.color <- factor(data1$dot.color, labels = dot.color) # do not use labels = unique(dot.color). Otherwise, we can have green1 green2 when dot.color is c("green", "green") -}else if(length(dot.color) > 1 & length(dot.color) == length(data1[, dot.categ])){ -data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) -}else if(length(dot.color)== 1L){ # to deal with single color. Warning: & length(dot.categ.class.order) > 1 removed because otherwise, the data1 is not with dot.color column when length(dot.categ.class.order) == 1 -data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) -} -dot.color <- as.character(unique(data1$dot.color[order(data1[, dot.categ])])) # reorder the dot.color character vector -if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color -dot.color <- rep(dot.color, length(dot.categ.class.order)) -} -tempo.check <- unique(data1[ , c(dot.categ, "dot.color")]) -if(length(unique(data1[ , "dot.color"])) > 1 & ( ! (nrow(tempo.check) == length(unique(data1[ , "dot.color"])) & nrow(tempo.check) == length(unique(data1[ , dot.categ]))))){ # length(unique(data1[ , "dot.color"])) > 1 because if only one color, can be attributed to each class of dot.categ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF dot.categ (", dot.categ, ") COLUMN:\n", paste(unique(mapply(FUN = "paste", data1[ , dot.categ], data1[ ,"dot.color"])), collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (", ini.dot.categ, "), THE FOLLOWING COLORS OF DOTS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(data1[, dot.categ]), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# dot.color is a character string representing the diff classes of dot.categ -# data1$dot.color is a factor with order of levels -> dot.categ -# end optional legend of dot colors -}else{ -categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 -if(length(dot.color) == length(levels(data1[, categ[categ.len]]))){ # here length(dot.color) is equal to the different number of categ -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -data1 <- data.frame(data1, dot.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) -data1$dot.color <- factor(data1$dot.color, labels = dot.color) -if(box.alpha != 0){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else if(length(dot.color) == length(data1[, categ[categ.len]])){# here length(dot.color) is equal to nrow(data1) -> Modif to have length(dot.color) equal to the different number of categ (length(dot.color) == length(levels(data1[, categ[categ.len]]))) -data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) -}else if(length(dot.color)== 1L & ! all(dot.color == "same")){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) -# data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor -data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) -dot.color <- rep(dot.color, length(levels(data1[, categ[categ.len]]))) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(dot.color, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(dot.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end check the length of color -dot.color <- as.character(dot.color) -# dot.color is a character string representing the diff classes -data1$dot.color <- factor(data1$dot.color, levels = unique(dot.color)) # ok because if dot.color is a character string, the order make class 1, class 2, etc. If dot.color is a column of data1, then levels will be created, without incidence, except if dot.categ specified (see below). unique() because no duplicates allowed -# data1$dot.color is a factor with order of levels -> dot.color -} -# end optional legend of dot colors -}else if(is.null(dot.color) & ! (is.null(dot.categ) & is.null(dot.categ.class.order) & is.null(dot.legend.name))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") dot.categ OR dot.categ.class.order OR dot.legend.name ARGUMENT HAS BEEN SPECIFIED BUT dot.color ARGUMENT IS NULL (NO DOT PLOTTED)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# dot.color either NULL (no dot plotted) or character string (potentially representing the diff classes of dot.categ) -# data1$dot.color is either NA or a factor (with order of levels -> depending on dot.categ or categ[length(categ)], or other -if(is.null(dot.categ)){ -dot.categ.class.order <- NULL # because not used anyway -} -# dot.categ.class.order either NULL if dot.categ is NULL (no legend displayed) or character string (potentially representing the diff classes of dot.categ) -# end management of dot.color -if(is.null(dot.color) & box.fill == FALSE & dot.alpha <= 0.025){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.alpha = ", fun_round(dot.alpha, 4), "\n-> POTENTIAL OUTLIER DOTS MIGHT NOT BE VISIBLE BECAUSE ALMOST TRANSPARENT") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if(is.null(dot.color) & box.fill == FALSE & dot.border.size == 0){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.border.size = 0 (NO BORDER FOR POTENTIAL OUTLIER DOTS)\n-> THESE SETTINGS ARE NOT ALLOWED BECAUSE THE POTENTIAL OUTLIER DOTS WILL NOT BE VISIBLE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# integer dot.border.color into gg_palette -if( ! is.null(dot.border.color)){ -tempo <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) -if(tempo$problem == FALSE){ # convert integers into colors -dot.border.color <- fun_gg_palette(max(dot.border.color, na.rm = TRUE))[dot.border.color] -} -} -# end integer dot.border.color into gg_palette -# na detection and removal (done now to be sure of the correct length of categ) -column.check <- c("categ.color", if( ! is.null(dot.color)){"dot.color"}) # -if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -for(i2 in 1:length(column.check)){ -if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na -tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn))) -} -} -tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which))) -removed.row.nb <- c(removed.row.nb, tempo) -removed.rows <- rbind(removed.rows, data1[tempo, ], stringsAsFactors = FALSE) # here data1 used because categorical columns tested -if(length(tempo) != 0){ -data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former -data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former -for(i3 in 1:length(column.check)){ -if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -} -for(i2 in 1:length(column.check)){ -if(column.check[i2] == "categ.color"){ -categ.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector -if(length(categ.color)== 1L & length(unlist(categ.class.order[length(categ)])) > 1){ # to deal with single color -categ.color <- rep(categ.color, length(unlist(categ.class.order[length(categ)]))) -} -data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.color)) -} -if(column.check[i2] == "dot.color"){ -dot.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector -if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color. If dot.categ.class.order == NULL (which is systematically the case if dot.categ == NULL), no rep(dot.color, length(dot.categ.class.order) -dot.color <- rep(dot.color, length(dot.categ.class.order)) -} -data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.color)) -} -} -} -# end na detection and removal (done now to be sure of the correct length of categ) -# From here, data1 and data.ini have no more NA or NaN -# end other checkings -# reserved word checking -#already done above -# end reserved word checking -# end second round of checking and data preparation - - -# package checking -fun_pack(req.package = c( -"ggplot2", -"gridExtra", -"lemon", -"scales" -), lib.path = lib.path) -# end package checking - - - - - -# main code -# y coordinates recovery (create ini.box.coord, dot.coord and modify data1) -if(length(categ)== 1L){ -# width commputations -box.width2 <- box.width -box.space <- 0 # to inactivate the shrink that add space between grouped boxes, because no grouped boxes here -# end width commputations -# data1 check categ order for dots coordinates recovery -data1 <- data.frame(data1, categ.check = data1[, categ[1]], stringsAsFactors = TRUE) -data1$categ.check <- as.integer(data1$categ.check) # to check that data1[, categ[1]] and dot.coord$group are similar, during merging -# end data1 check categ order for dots coordinates recovery -# per box dots coordinates recovery -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[1]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21)) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[1]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[1]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) -# end per box dots coordinates recovery -}else if(length(categ) == 2L){ -# width commputations -box.width2 <- box.width / length(unique(data1[, categ[length(categ)]])) # real width of each box in x-axis unit, among the set of grouped box. Not relevant if no grouped boxes length(categ)== 1L -# end width commputations -# data1 check categ order for dots coordinates recovery -tempo.factor <- paste0(data1[order(data1[, categ[2]], data1[, categ[1]]), categ[2]], "_", data1[order(data1[, categ[2]], data1[, categ[1]]), categ[1]]) -data1 <- data.frame(data1[order(data1[, categ[2]], data1[, categ[1]]), ], categ.check = factor(tempo.factor, levels = unique(tempo.factor)), stringsAsFactors = TRUE) -data1$categ.check <- as.integer(data1$categ.check) -# end data1 check categ order for dots coordinates recovery -# per box dots coordinates recovery -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[2]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21)) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[2]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[2]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) -# end per box dots coordinates recovery -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if( ! is.null(stat.pos)){ -stat.just <- fun_gg_just( -angle = stat.angle, -pos = ifelse( -vertical == TRUE, -ifelse(stat.pos == "top", "bottom", "top"), # "bottom" because we want justification for text that are below the ref point which is the top of the graph. The opposite for "above" -ifelse(stat.pos == "top", "left", "right") # "left" because we want justification for text that are on the left of the ref point which is the right border of the graph. The opposite for "above" -), -kind = "text" -) -} -# has in fact no interest because ggplot2 does not create room for geom_text() -tempo.data.max <- data1[which.max(data1[, y]), ] -tempo.data.max <- data.frame(tempo.data.max, label = formatC(tempo.data.max[, y], digit = 2, drop0trailing = TRUE, format = "f"), stringsAsFactors = TRUE) -# end has in fact no interest because ggplot2 does not create room for geom_text() -tempo.graph.info.ini <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if( ! is.null(stat.pos)){' + ggplot2::geom_text(data = tempo.data.max, mapping = ggplot2::aes_string(x = 1, y = y, label = "label"), size = stat.size, color = "black", angle = stat.angle, hjust = stat.just$hjust, vjust = stat.just$vjust)'})))) # added here to have room for annotation -dot.coord <- tempo.graph.info.ini$data[[1]] -dot.coord$x <- as.numeric(dot.coord$x) # because weird class -dot.coord$PANEL <- as.numeric(dot.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? -tempo.mean <- aggregate(x = dot.coord$y, by = list(dot.coord$group, dot.coord$PANEL), FUN = mean, na.rm = TRUE) -names(tempo.mean)[names(tempo.mean) == "x"] <- "MEAN" -names(tempo.mean)[names(tempo.mean) == "Group.1"] <- "BOX" -names(tempo.mean)[names(tempo.mean) == "Group.2"] <- "PANEL" -dot.coord <- data.frame( -dot.coord[order(dot.coord$group, dot.coord$y), ], # dot.coord$PANEL deals below -y.check = as.double(data1[order(data1$categ.check, data1[, y]), y]), -categ.check = data1[order(data1$categ.check, data1[, y]), "categ.check"], -dot.color = if(is.null(dot.color)){NA}else{data1[order(data1$categ.check, data1[, y]), "dot.color"]}, -data1[order(data1$categ.check, data1[, y]), ][categ], # avoid the renaming below -stringsAsFactors = TRUE -) # y.check to be sure that the order is the same between the y of data1 and the y of dot.coord -# names(dot.coord)[names(dot.coord) == "tempo.categ1"] <- categ[1] -if( ! is.null(dot.categ)){ -dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][dot.categ], stringsAsFactors = TRUE) # avoid the renaming -} -if( ! is.null(facet.categ)){ -dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][facet.categ], stringsAsFactors = TRUE) # for facet panels -tempo.test <- NULL -for(i2 in 1:length(facet.categ)){ -tempo.test <- paste0(tempo.test, ".", formatC(as.numeric(dot.coord[, facet.categ[i2]]), width = nchar(max(as.numeric(dot.coord[, facet.categ[i2]]), na.rm = TRUE)), flag = "0")) # convert factor into numeric with leading zero for proper ranking # merge the formatC() to create a new factor. The convertion to integer should recreate the correct group number. Here as.numeric is used and not as.integer in case of numeric in facet.categ (because comes from add and not checked by fun_check, contrary to categ) -} -tempo.test <- as.integer(factor(tempo.test)) -if( ! identical(as.integer(dot.coord$PANEL), tempo.test)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nas.integer(dot.coord$PANEL) AND tempo.test MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(dot.tidy == TRUE){ -if( ! is.null(dot.categ)){ -dot.coord <- data.frame(dot.coord, tidy_group = data1[order(data1$categ.check, data1[, y]), ][, dot.categ], stringsAsFactors = TRUE) # avoid the renaming -# tidy_group_coord is to be able to fuse table when creating the table for dot coordinates -if(dot.categ %in% categ){ -dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE) -}else{ -dot.coord <- data.frame(dot.coord, tidy_group_coord = as.integer(factor(paste0( -formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking -".", -if(length(categ) == 2L){formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")}, # convert factor into numeric with leading zero for proper ranking -if(length(categ) == 2L){"."}, -formatC(as.integer(dot.coord[, dot.categ]), width = nchar(max(as.integer(dot.coord[, dot.categ]), na.rm = TRUE)), flag = "0") # convert factor into numeric with leading zero for proper ranking -)), stringsAsFactors = TRUE) # merge the 2 or 3 formatC() to create a new factor. The convertion to integer should recreate the correct group number -) # for tidy dot plots -} -}else{ -dot.coord <- data.frame(dot.coord, tidy_group = if(length(categ)== 1L){ -dot.coord[, categ]}else{as.integer(factor(paste0( -formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking -".", -formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking -)), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number -}) # for tidy dot plots -# tidy_group_coord is to be able to fuse table when creating the table for dot coordinates -dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE) -} -} -if( ! (identical(dot.coord$y, dot.coord$y.check) & identical(dot.coord$group, dot.coord$categ.check))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(dot.coord$y AND dot.coord$y.check) AS WELL AS (dot.coord$group AND dot.coord$categ.check) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -if( ! identical(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ]$BOX, unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c("group", "PANEL")])$group)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(tempo.mean$BOX, tempo.mean$PANEL) AND (dot.coord$group, dot.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -tempo <- unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}), drop = FALSE]) -# names(tempo) <- paste0(names(tempo), ".mean") -tempo.mean <- data.frame(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ], tempo, stringsAsFactors = TRUE) -} -} -# at that stage, categ color and dot color are correctly attributed in data1, box.coord and dot.coord -# end y dot coordinates recovery (create ini.box.coord, dot.coord and modify data1) -# ylim range -if(is.null(y.lim)){ -y.lim <- tempo.graph.info.ini$layout$panel_params[[1]]$y.range # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -if(any(( ! is.finite(y.lim)) | is.na(y.lim)) | length(y.lim) != 2){ # kept but normally no more Inf in data1 # normally no NA with is.finite, etc. -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntempo.graph.info.ini$layout$panel_params[[1]]$y.range[1] CONTAINS NA OR Inf OR HAS LENGTH 1") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else if(y.log != "no"){ -y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -} -if(y.log != "no"){ -# normally this control is not necessary anymore -if(any( ! is.finite(y.lim))){ # normally no NA with is.finite -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # all() without na.rm -> ok because y.lim cannot be NA (tested above) -# normally this control is not necessary anymore -tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS Inf VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -if(suppressWarnings(any(is.na(y.lim)))){ # normally no NA with is.na -# normally this control is not necessary anymore -tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS NA OR NaN VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -y.lim.order <- order(y.lim) # to deal with inverse axis -y.lim <- sort(y.lim) -y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis -y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis -if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before -y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -} -y.lim <- y.lim[y.lim.order] -if(any(is.na(y.lim))){ # normally no NA with is.na -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end ylim range - - - - - - -# drawing -# constant part -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add is directly put here to deal with additional variable of data, like when using facet_grid. No problem if add is a theme, will be dealt below -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){categ[1]}else{x.lab})) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y}else{y.lab})) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) -# text angle management -axis.just <- fun_gg_just(angle = x.angle, pos = ifelse(vertical == TRUE, "bottom", "left"), kind = "axis") -# end text angle management -add.check <- TRUE -if( ! is.null(add)){ # if add is NULL, then = 0 -if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -add.check <- FALSE -} -} -if(add.check == TRUE & article == TRUE){ -# WARNING: not possible to add theme()several times. NO message but the last one overwrites the others -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) -if(grid == TRUE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_line(size = 0.5), -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis -axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis -panel.grid.major.x = if(vertical == TRUE){NULL}else{ggplot2::element_line(colour = "grey85", size = 0.75)}, -panel.grid.major.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey85", size = 0.75)}else{NULL}, -panel.grid.minor.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey90", size = 0.25)}else{NULL}, -axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, -axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}, -strip.background = ggplot2::element_rect(fill = NA, colour = NA) # for facet background -)) -}else{ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_line(size = 0.5), -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -axis.line.y.left = ggplot2::element_line(colour = "black"), -axis.line.x.bottom = ggplot2::element_line(colour = "black"), -axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, -axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}, -strip.background = ggplot2::element_rect(fill = NA, colour = NA) -)) -} -}else if(add.check == TRUE & article == FALSE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_line(size = 0.5), -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -panel.background = ggplot2::element_rect(fill = "grey95"), -axis.line.y.left = ggplot2::element_line(colour = "black"), -axis.line.x.bottom = ggplot2::element_line(colour = "black"), -panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.minor.x = ggplot2::element_blank(), -panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), -strip.background = ggplot2::element_rect(fill = NA, colour = NA), -axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, -axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)} -)) -} -# Contrary to fun_gg_bar(), cannot plot the boxplot right now, because I need the dots plotted first -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, group = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), color = NA, width = box.width, fill = NA)) # this is to set the graph (i.e., a blanck boxplot to be able to use x coordinates to plot dots before boxes) -# end constant part - - - - -# graphic info recovery (including means) -tempo.graph.info <- ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}) + ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color})')))) # will be recovered later again, when ylim will be considered -tempo.yx.ratio <- (tempo.graph.info$layout$panel_params[[1]]$y.range[2] - tempo.graph.info$layout$panel_params[[1]]$y.range[1]) / (tempo.graph.info$layout$panel_params[[1]]$x.range[2] - tempo.graph.info$layout$panel_params[[1]]$x.range[1]) -box.coord <- tempo.graph.info$data[[2]] # to have the summary statistics of the plot. Contrary to ini.box.plot, now integrates ylim Here because can be required for stat.pos when just box are plotted -box.coord$x <- as.numeric(box.coord$x) # because x is of special class that block comparison of values using identical -box.coord$PANEL <- as.numeric(box.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? -box.coord <- box.coord[order(box.coord$group, box.coord$PANEL), ] -if( ! (identical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL) DO NOT HAVE THE SAME VALUE ORDER") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -# tempo <- c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}) -if(any(names(tempo.mean) %in% names(box.coord), na.rm = TRUE)){ -names(tempo.mean)[names(tempo.mean) %in% names(box.coord)] <- paste0(names(tempo.mean)[names(tempo.mean) %in% names(box.coord)], ".mean") -} -box.coord <- data.frame(box.coord, tempo.mean, stringsAsFactors = TRUE) -} -# end graphic info recovery (including means) - - - -# stat output (will also serve for boxplot and mean display) -# x not added now (to do not have them in stat.nolog) -stat <- data.frame( -MIN = box.coord$ymin_final, -QUART1 = box.coord$lower, -MEDIAN = box.coord$middle, -MEAN = box.coord$MEAN, -QUART3 = box.coord$upper, -MAX = box.coord$ymax_final, -WHISK_INF = box.coord$ymin, -BOX_INF = box.coord$lower, -NOTCH_INF = box.coord$notchlower, -NOTCH_SUP = box.coord$notchupper, -BOX_SUP = box.coord$upper, -WHISK_SUP = box.coord$ymax, -OUTLIERS = box.coord["outliers"], -tempo.mean[colnames(tempo.mean) != "MEAN"], -COLOR = box.coord$fill, -stringsAsFactors = TRUE -) # box.coord["outliers"] written like this because it is a list. X coordinates not put now because several features to set -names(stat)[names(stat) == "outliers"] <- "OUTLIERS" -stat.nolog <- stat # stat.nolog ini will serve for outputs -if(y.log != "no"){ -stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")] <- ifelse(y.log == "log2", 2, 10)^(stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")]) -stat.nolog$OUTLIERS <- lapply(stat.nolog$OUTLIERS, FUN = function(X){ifelse(y.log == "log2", 2, 10)^X}) -} -# end stat output (will also serve for boxplot and mean display) - - - - - - -# x coordinates management (for random plotting and for stat display) -# width commputations -width.ini <- c(box.coord$xmax - box.coord$xmin)[1] # all the box widths are equal here. Only the first one taken -width.correct <- width.ini * box.space / 2 -if( ! (identical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -stat <- data.frame( -stat, -X = box.coord$x, -X_BOX_INF = box.coord$xmin + width.correct, -X_BOX_SUP = box.coord$xmax - width.correct, -X_NOTCH_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) / 2, -X_NOTCH_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) / 2, -X_WHISK_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, -X_WHISK_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, -# tempo.mean[colnames(tempo.mean) != "MEAN"], # already added above -stringsAsFactors = TRUE -) -stat$COLOR <- factor(stat$COLOR, levels = unique(categ.color)) -if( ! all(stat$NOTCH_SUP < stat$BOX_SUP & stat$NOTCH_INF > stat$BOX_INF, na.rm = TRUE) & box.notch == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") SOME NOTCHES ARE BEYOND BOX HINGES. TRY ARGUMENT box.notch = FALSE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -dot.jitter <- c((box.coord$xmax - width.correct) - (box.coord$xmin + width.correct))[1] * dot.jitter # real dot.jitter. (box.coord$xmin + width.correct) - (box.coord$xmax - width.correct))[1] is the width of the box. Is equivalent to (box.coord$x - (box.coord$xmin + width.correct))[1] * 2 -# end width commputations -if( ! is.null(dot.color)){ -# random dots -if(dot.tidy == FALSE){ -dot.coord.rd1 <- merge(dot.coord, box.coord[c("fill", "PANEL", "group", "x")], by = c("PANEL", "group"), sort = FALSE) # rd for random. Send the coord of the boxes into the coord data.frame of the dots (in the column x.y). WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column -if(nrow(dot.coord.rd1) != nrow(dot.coord)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd1 DATA FRAME. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -sampled.dot.jitter <- if(nrow(dot.coord.rd1)== 1L){runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2)}else{sample(x = runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2), size = nrow(dot.coord.rd1), replace = FALSE)} -dot.coord.rd2 <- data.frame(dot.coord.rd1, dot.x = dot.coord.rd1$x.y + sampled.dot.jitter, stringsAsFactors = TRUE) # set the dot.jitter thanks to runif and dot.jitter range. Then, send the coord of the boxes into the coord data.frame of the dots (in the column x.y) -if(length(categ)== 1L){ -tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor -names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") -verif <- paste0(categ[1], ".check") -}else if(length(categ) == 2L){ -tempo.data1 <- unique( -data.frame( -data1[c(categ[1], categ[2])], -group = as.integer(factor(paste0( -formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking -".", -formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking -)), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number -) -) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis -names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") -names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check") -verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check")) -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -dot.coord.rd3 <- merge(dot.coord.rd2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have replaced by = "group" by intersect("group", "group") because of an error due to wrong group group merging in dot.coord.rd3 -if(nrow(dot.coord.rd3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.rd3[categ], dot.coord.rd3[verif])$identical.content)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd3 DATA FRAME. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end random dots -} -# tidy dots -# coordinates are recovered during plotting (see dot.coord.tidy1 below) -# end tidy dots -} -# end x coordinates management (for random plotting and for stat display) - - - - - -# boxplot display before dot display if box.fill = TRUE -coord.names <- NULL -# creation of the data frame for (main box + legend) and data frame for means -if(box.notch == FALSE){ -for(i3 in 1:length(categ)){ -if(i3== 1L){ -tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE) -}else{ -tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE) -} -} -names(tempo.polygon) <- categ -tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "BOX_SUP", "BOX_SUP", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE) -if( ! is.null(facet.categ)){ -for(i4 in 1:length(facet.categ)){ -tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE) -names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4] -} -} -}else{ -for(i3 in 1:length(categ)){ -if(i3== 1L){ -tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE) -}else{ -tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE) -} -} -names(tempo.polygon) <- categ -tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_NOTCH_SUP", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF", "X_NOTCH_INF", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "NOTCH_INF", "MEDIAN", "NOTCH_SUP", "BOX_SUP", "BOX_SUP", "NOTCH_SUP", "MEDIAN", "NOTCH_INF", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE) -if( ! is.null(facet.categ)){ -for(i4 in 1:length(facet.categ)){ -tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE) -names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4] -} -} -} -tempo.polygon$COLOR <- factor(tempo.polygon$COLOR, levels = unique(categ.color)) -if( ! is.null(categ.class.order)){ -for(i3 in 1:length(categ)){ -tempo.polygon[, categ[i3]] <- factor(tempo.polygon[, categ[i3]], levels = categ.class.order[[i3]]) -} -} -# modified name of dot.categ column (e.g., "Categ1_DOT") must be included for boxplot using ridy dots -if( ! is.null(dot.color) & ! is.null(dot.categ)){ -if(dot.categ != ini.dot.categ){ -tempo.polygon <- data.frame(tempo.polygon, GROUPX = tempo.polygon[, ini.dot.categ], stringsAsFactors = TRUE) -names(tempo.polygon)[names(tempo.polygon) == "GROUPX"] <- dot.categ - -} -} -tempo.diamon.mean <- data.frame(X = c(t(stat[, c("X", "X_NOTCH_INF", "X", "X_NOTCH_SUP", "X")])), Y = c(t(cbind(stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] + (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stringsAsFactors = TRUE))), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), GROUP = c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")])), stringsAsFactors = TRUE) # stringsAsFactors = TRUE for cbind() because stat["MEAN"] is a data frame. Otherwise, stringsAsFactors is not an argument for cbind() on vectors -if( ! is.null(facet.categ)){ -for(i3 in 1:length(facet.categ)){ -tempo.diamon.mean <- data.frame(tempo.diamon.mean, c(t(stat[, c(facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3])])), stringsAsFactors = TRUE) -names(tempo.diamon.mean)[length(names(tempo.diamon.mean))] <- facet.categ[i3] -} -} -tempo.diamon.mean$COLOR <- factor(tempo.diamon.mean$COLOR, levels = unique(categ.color)) -# end creation of the data frame for (main box + legend) and data frame for means -if(box.fill == TRUE){ -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, alpha = box.alpha, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{dot.border.color}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted. Finally, boxplot redrawn (see below) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon( -data = tempo.polygon, -mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", fill = categ[length(categ)], color = categ[length(categ)]), -size = box.line.size, -alpha = box.alpha # works only for fill, not for color -)) -coord.names <- c(coord.names, "main.box") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # -coord.names <- c(coord.names, "sup.whisker") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # -coord.names <- c(coord.names, "inf.whisker") -if(box.whisker.width > 0){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # -coord.names <- c(coord.names, "sup.whisker.edge") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # -coord.names <- c(coord.names, "inf.whisker.edge") -} -if(box.mean == TRUE){ -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, fill = stat$COLOR, size = box.mean.size, color = "black", alpha = box.alpha)) # group used in aesthetic to do not have it in the legend -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon( -data = tempo.diamon.mean, -mapping = ggplot2::aes(x = X, y = Y, group = GROUP), -fill = tempo.diamon.mean[, "COLOR"], -color = hsv(0, 0, 0, alpha = box.alpha), # outline of the polygon in black but with alpha -size = box.line.size, -alpha = box.alpha -)) -coord.names <- c(coord.names, "mean") -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = "black", size = box.line.size * 2, alpha = box.alpha)) # -coord.names <- c(coord.names, "median") -} -# end boxplot display before dot display if box.fill = TRUE - - - - - - -# dot display -if( ! is.null(dot.color)){ -if(dot.tidy == FALSE){ -if(is.null(dot.categ)){ -if(dot.border.size == 0){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( -data = dot.coord.rd3, -mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), -size = dot.size, -shape = 19, -color = dot.coord.rd3$dot.color, -alpha = dot.alpha -)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic -}else{ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( -data = dot.coord.rd3, -mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), -shape = 21, -stroke = dot.border.size, -color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, -size = dot.size, -fill = dot.coord.rd3$dot.color, -alpha = dot.alpha -)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic -} -}else{ -if(dot.border.size == 0){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( -data = dot.coord.rd3, -mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), -size = dot.size, -shape = 19, -color = dot.coord.rd3$dot.color -)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic -}else{ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( -data = dot.coord.rd3, -mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), -size = dot.size, -shape = 21, -stroke = dot.border.size, -color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, -fill = dot.coord.rd3$dot.color -)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), guide = ggplot2::guide_legend(override.aes = list(fill = dot.color, color = if(is.null(dot.border.color)){dot.color}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -} -coord.names <- c(coord.names, "dots") -}else if(dot.tidy == TRUE){ -# here plot using group -> no scale -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot( -data = dot.coord, -mapping = ggplot2::aes_string(x = categ[1], y = "y", group = "group"), # not dot.categ here because the classes of dot.categ create new separations -position = ggplot2::position_dodge(width = box.width), -binpositions = "all", -binaxis = "y", -stackdir = "center", -alpha = dot.alpha, -fill = dot.coord$dot.color, -stroke = dot.border.size, -color = if(is.null(dot.border.color)){dot.coord$dot.color}else{rep(dot.border.color, nrow(dot.coord))}, -show.legend = FALSE, # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual() -binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb -)) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) -coord.names <- c(coord.names, "dots") -if( ! is.null(dot.categ)){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot( -data = dot.coord, -mapping = ggplot2::aes_string(x = categ[1], y = "y", alpha = dot.categ), # not dot.categ here because the classes of dot.categ create new separations -position = ggplot2::position_dodge(width = box.width), -binpositions = "all", -binaxis = "y", -stackdir = "center", -fill = NA, -stroke = NA, -color = NA, -# WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual() -binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb -)) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = dot.legend.name, values = rep(1, length(categ.color)))) # values = rep("black", length(categ.color)) are the values of color (which is the border color of dots), and this modify the border color on the plot. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -coord.names <- c(coord.names, "bad_remove") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), labels = dot.categ.class.order, guide = ggplot2::guide_legend(title = if(ini.dot.categ == categ[length(categ)]){dot.categ}else{ini.dot.categ}, override.aes = list(fill = levels(dot.coord$dot.color), color = if(is.null(dot.border.color)){levels(dot.coord$dot.color)}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -} -# coordinates of tidy dots -tempo.coord <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))$data # to have the tidy dot coordinates -if(length(which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))) != 1){ # detect the compartment of tempo.coord which is the binned data frame -# if(length(which(sapply(tempo.coord, FUN = nrow) == nrow(data1))) > if(is.null(dot.categ)){1}else{2}){ # this does not work if only one dot per class, thus replaced by above # if(is.null(dot.categ)){1}else{2} because 1 dotplot if dot.categ is NULL and 2 dotplots if not, with the second being a blank dotplot with wrong coordinates. Thus take the first in that situation -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nEITHER MORE THAN 1 OR NO COMPARTMENT HAVING A DATA FRAME WITH binwidth AS COLUMN NAME IN THE tempo.coord LIST (FOR TIDY DOT COORDINATES). CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -# dot.coord.tidy1 <- tempo.coord[[which(sapply(tempo.coord, FUN = nrow) == nrow(data1))[1]]] # this does not work if only one dot per class, thus replaced by above # the second being a blank dotplot with wrong coordinates. Thus take the first whatever situation -dot.coord.tidy1 <- tempo.coord[[which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))]] # detect the compartment of tempo.coord which is the binned data frame -dot.coord.tidy1$x <- as.numeric(dot.coord.tidy1$x) # because weird class -dot.coord.tidy1$PANEL <- as.numeric(dot.coord.tidy1$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? -} -# tempo.box.coord <- merge(box.coord, unique(dot.coord[, c("PANEL", "group", categ)]), by = c("PANEL", "group"), sort = FALSE) # not required anymore because box.coord already contains categ do not add dot.categ and tidy_group_coord here because the coordinates are for stats. Add the categ in box.coord. WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column -# below inactivated because not true when dealing with dot.categ different from categ -# if(nrow(tempo.box.coord) != nrow(box.coord)){ -# tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT tempo.box.coord DATA FRAME. CODE HAS TO BE MODIFIED") -# stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -# } -dot.coord.tidy2 <- merge(dot.coord.tidy1, box.coord[c("fill", "PANEL", "group", "x", categ)], by = c("PANEL", "group"), sort = FALSE) # send the coord of the boxes into the coord data.frame of the dots (in the column x.y).WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in tempo.box.coord. Thus, no need to consider fill colum # DANGER: from here the fill.y and x.y (from tempo.box.coord) are not good in dot.coord.tidy2. It is ok because Categ1 Categ2 from tempo.box.coord are ok with the group column from dot.coord.tidy1. This is due to the fact that dot.coord.tidy resulting from geom_dotplot does not make the same groups as the other functions -if(nrow(dot.coord.tidy2) != nrow(dot.coord)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy2 DATA FRAME. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# From here, check for dot.coord.tidy3 which wil be important for stat over the plot. WARNING: dot.categ has nothing to do here for stat coordinates. Thus, not in tempo.data1 -if(length(categ)== 1L){ -tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor -names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") -verif <- paste0(categ[1], ".check") -}else if(length(categ) == 2L){ -tempo.data1 <- unique( -data.frame( -data1[c(categ[1], categ[2])], -group = as.integer(factor(paste0( -formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking -".", -formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking -)), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number -) -) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis -names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") -names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check") -verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check")) -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -dot.coord.tidy3 <- merge(dot.coord.tidy2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have tested intersect("group", "group") instead of by = "group". May be come back to by = "group" in case of error. But I did this because of an error in dot.coord.rd3 above -if(nrow(dot.coord.tidy3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.tidy3[categ], dot.coord.tidy3[verif])$identical.content)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy3 DATA FRAME. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end coordinates of tidy dots -} -} -# end dot display - - - -# boxplot display (if box.fill = FALSE, otherwise, already plotted above) -if(box.fill == TRUE){ -# overcome "work only for the filling of boxes, not for the frame. See https://github.com/tidyverse/ggplot2/issues/252" -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(order = 1))) #, guide = ggplot2::guide_legend(override.aes = list(fill = levels(tempo.polygon$COLOR), color = "black")))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = rep(hsv(0, 0, 0, alpha = box.alpha), length(unique(data1[, categ[length(categ)]]))), guide = ggplot2::guide_legend(order = 1))) # , guide = ggplot2::guide_legend(override.aes = list(color = "black", alpha = box.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor # outline of the polygon in black but with alpha -}else{ -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, alpha = box.alpha, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{if(dot.border.size == 0){NA}else{dot.border.color}}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path( -data = tempo.polygon, -mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", color = categ[length(categ)]), -size = box.line.size, -alpha = box.alpha, -lineend = "round", -linejoin = "round" -)) -coord.names <- c(coord.names, "main.box") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size * 2, alpha = box.alpha)) # -coord.names <- c(coord.names, "median") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # -coord.names <- c(coord.names, "sup.whisker") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # -coord.names <- c(coord.names, "inf.whisker") -if(box.whisker.width > 0){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # -coord.names <- c(coord.names, "sup.whisker.edge") -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # -coord.names <- c(coord.names, "inf.whisker.edge") -} -if(box.mean == TRUE){ -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, color = stat$COLOR, size = box.mean.size, fill = NA, alpha = box.alpha)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path( -data = tempo.diamon.mean, -mapping = ggplot2::aes(x = X, y = Y, group = GROUP), -color = tempo.diamon.mean[, "COLOR"], -size = box.line.size, -alpha = box.alpha, -lineend = "round", -linejoin = "round" -)) -coord.names <- c(coord.names, "mean") -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = rep(NA, length(unique(data1[, categ[length(categ)]]))))) #, guide = ggplot2::guide_legend(override.aes = list(color = categ.color)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(override.aes = list(alpha = if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){1}else{box.alpha})))) # , guide = ggplot2::guide_legend(override.aes = list(color = as.character(categ.color))))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor -if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -# to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(box.alpha == 0){ # remove box legend because no boxes drawn -# add this after the scale_xxx_manual() for boxplots -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none")) # inactivate the legend -} -# end boxplot display (if box.fill = FALSE, otherwise, already plotted above) - - - - -# stat display -# layer after dots but ok, behind dots on the plot -if( ! is.null(stat.pos)){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NUMBERS DISPLAYED ARE ", ifelse(stat.mean == FALSE, "MEDIANS", "MEANS")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -if(stat.pos == "top"){ -tempo.stat <- data.frame(stat, Y = y.lim[2], stringsAsFactors = TRUE) # I had to create a data frame for geom_tex() so that facet is taken into account, (ggplot2::annotate() does not deal with facet because no data and mapping arguments). Of note, facet.categ is in tempo.stat, via tempo.mean, via dot.coord -if(stat.mean == FALSE){tempo.stat$MEDIAN <- formatC(stat.nolog$MEDIAN, digit = 2, drop0trailing = TRUE, format = "f")}else{tempo.stat$MEAN <- formatC(stat.nolog$MEAN, digit = 2, drop0trailing = TRUE, format = "f")} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( -data = tempo.stat, -mapping = ggplot2::aes_string(x = "X", y = "Y", label = ifelse(stat.mean == FALSE, "MEDIAN", "MEAN")), -size = stat.size, -color = "black", -angle = stat.angle, -hjust = stat.just$hjust, -vjust = stat.just$vjust -)) # stat$X used here because identical to stat.nolog but has the X. WARNING: no need of order() for labels because box.coord$x set the order. For justification, see https://stackoverflow.com/questions/7263849/what-do-hjust-and-vjust-do-when-making-a-plot-using-ggplot -coord.names <- c(coord.names, "stat.pos") -}else if(stat.pos == "above"){ -# stat coordinates -if( ! is.null(dot.color)){ # for text just above max dot -if(dot.tidy == FALSE){ -tempo.stat.ini <- dot.coord.rd3 -}else if(dot.tidy == TRUE){ -tempo.stat.ini <- dot.coord.tidy3 -tempo.stat.ini$x.y <- tempo.stat.ini$x.x # this is just to be able to use tempo.stat.ini$x.y for untidy or tidy dots (remember that dot.coord.tidy3$x.y is not good, see above) -} -stat.coord1 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = min, na.rm = TRUE) -names(stat.coord1)[names(stat.coord1) == "y"] <- "dot.min" -stat.coord2 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = max, na.rm = TRUE) -names(stat.coord2) <- paste0(names(stat.coord2), "_from.dot.max") -names(stat.coord2)[names(stat.coord2) == "y_from.dot.max"] <- "dot.max" -stat.coord3 <- cbind(box.coord[order(box.coord$group, box.coord$PANEL), ], stat.coord1[order(stat.coord1$group, stat.coord1$x.y), ], stat.coord2[order(stat.coord2$group, stat.coord2$x.y), ], stringsAsFactors = TRUE) # -if( ! all(identical(round(stat.coord3$x, 9), round(as.numeric(stat.coord3$x.y), 9)), na.rm = TRUE)){ # as.numeric() because stat.coord3$x is class "mapped_discrete" "numeric" -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nFUSION OF box.coord, stat.coord1 AND stat.coord2 ACCORDING TO box.coord$x, stat.coord1$x.y AND stat.coord2$x.y IS NOT CORRECT. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -# text.coord <- stat.coord3[, c("x", "group", "dot.min", "dot.max")] -# names(text.coord)[names(text.coord) == "dot.min"] <- "text.min.pos" -#names(text.coord)[names(text.coord) == "dot.max"] <- "text.max.pos" -box.coord <- box.coord[order(box.coord$x, box.coord$group, box.coord$PANEL), ] -# text.coord <- text.coord[order(text.coord$x), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same -stat.coord3 <- stat.coord3[order(stat.coord3$x, stat.coord3$group, stat.coord3$PANEL), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same -if( ! (identical(box.coord$x, stat.coord3$x) & identical(box.coord$group, stat.coord3$group) & identical(box.coord$PANEL, stat.coord3$PANEL))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntext.coord AND box.coord DO NOT HAVE THE SAME x, group AND PANEL COLUMN CONTENT") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -}else{ -stat.coord3 <- box.coord -} -stat.coord3 <- data.frame( -stat.coord3, -Y = stat.coord3[, ifelse( -is.null(dot.color), -ifelse(diff(y.lim) > 0, "ymax", "ymin"), -ifelse(diff(y.lim) > 0, "ymax_final", "ymin_final") -)], -stringsAsFactors = TRUE -) # ymax is top whisker, ymax_final is top dot -# stat.coord3 <- data.frame(stat.coord3, Y = vector("numeric", length = nrow(stat.coord3)), stringsAsFactors = TRUE) -# check.Y <- as.logical(stat.coord3$Y) # convert everything in Y into FALSE (because Y is full of zero) -# end stat coordinates -# stat display -# performed twice: first for y values >=0, then y values < 0, because only a single value allowed for hjust anf vjust -if(stat.mean == FALSE){ -tempo.center.ref <- "middle" -}else{ -tempo.center.ref <- "MEAN" -} -# if(is.null(dot.color)){ -# tempo.low.ref <- "ymin" -# tempo.high.ref <- "ymax" -# }else{ -# tempo.low.ref <- "ymin_final" -# tempo.high.ref <- "ymax_final" -# } -# tempo.log.high <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] >= 0}else{stat.coord3[, tempo.center.ref] < 0} -# tempo.log.low <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] < 0}else{stat.coord3[, tempo.center.ref] >= 0} -# stat.coord3$Y[tempo.log.high] <- stat.coord3[tempo.log.high, tempo.high.ref] -# stat.coord3$Y[tempo.log.low] <- stat.coord3[tempo.log.low, tempo.low.ref] -# add distance -stat.coord3$Y <- stat.coord3$Y + diff(y.lim) * stat.dist / 100 -# end add distance -# correct median or mean text format -if(y.log != "no"){ -stat.coord3[, tempo.center.ref] <- ifelse(y.log == "log2", 2, 10)^(stat.coord3[, tempo.center.ref]) -} -stat.coord3[, tempo.center.ref] <- formatC(stat.coord3[, tempo.center.ref], digit = 2, drop0trailing = TRUE, format = "f") -# end correct median or mean text format -# if(any(tempo.log.high) == TRUE){ -# tempo.stat <- stat.coord3[tempo.log.high,] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( -data = stat.coord3, -mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref), -size = stat.size, -color = "black", -angle = stat.angle, -hjust = stat.just$hjust, -vjust = stat.just$vjust -)) # WARNING: no need of order() for labels because box.coord$x set the order -coord.names <- c(coord.names, "stat.pos") -# } -# if(any(tempo.log.low) == TRUE){ -# tempo.stat <- stat.coord3[tempo.log.low,] -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( -# data = tempo.stat, -# mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref), -# size = stat.size, -# color = "black", -# hjust = ifelse(vertical == TRUE, 0.5, 0.5 + stat.dist), -# vjust = ifelse(vertical == TRUE, 0.5 + stat.dist, 0.5) -# )) # WARNING: no need of order() for labels because box.coord$x set the order -# coord.names <- c(coord.names, "stat.pos.negative") -# } -# end stat display -}else{ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -} -# end stat display -# legend management -if(legend.show == FALSE){ # must be here because must be before bef.final.plot <- -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend -} -# end legend management - - - -# y scale management (cannot be before dot plot management) -# the rescaling aspect is complicated and not intuitive. See: -# explaination: https://github.com/tidyverse/ggplot2/issues/3948 -# the oob argument of scale_y_continuous() https://ggplot2.tidyverse.org/reference/scale_continuous.html -# see also https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf -# secondary ticks -bef.final.plot <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + if(vertical == TRUE){ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)}else{ggplot2::coord_flip(ylim = y.lim)}')))) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks and the legend. I DI NOT UNDERSTAND THE COMMENT HERE BECAUSE WE NEED COORD_FLiP -tempo.coord <- bef.final.plot$layout$panel_params[[1]] -# y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non NULL or if y.log argument is different from "no") -if(y.log != "no"){ # integer main ticks for log2 and log10 -tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1) -}else{ -tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))} -if(all(is.na(tempo))){# all() without na.rm -> ok because is.na() cannot be NA -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -} -tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb)) -} -y.second.tick.values <- NULL -y.second.tick.pos <- NULL -if(y.log != "no"){ -tempo <- fun_inter_ticks(lim = y.lim, log = y.log) -y.second.tick.values <- tempo$values -y.second.tick.pos <- tempo$coordinates -# if(vertical == TRUE){ # do not remove in case the bug is fixed -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = y.second.tick.pos, yend = y.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80)) -# }else{ # not working because of the ggplot2 bug -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80)) -# } -coord.names <- c(coord.names, "y.second.tick.positions") -}else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){ -# if(y.second.tick.nb > 0){ #inactivated because already checked before -if(length(tempo.scale) < 2){ -tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb") -tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ") -tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb) -} -y.second.tick.values <- tempo$values -y.second.tick.pos <- tempo$coordinates -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( -geom = "segment", -y = y.second.tick.pos, -yend = y.second.tick.pos, -x = if(vertical == TRUE){tempo.coord$x.range[1]}else{tempo.coord$y.range[1]}, -xend = if(vertical == TRUE){tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80}else{tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80} -)) -coord.names <- c(coord.names, "y.second.tick.positions") -} -# end y.second.tick.positions -# for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous"))) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous( -breaks = tempo.scale, -minor_breaks = y.second.tick.pos, -labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 6") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, # == in stop() to be able to add several messages between == -expand = c(0, 0), # remove space after after axis limits -limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used -oob = scales::rescale_none, -trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this -)) -if(vertical == TRUE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(ylim = y.lim)) # problem of ggplot2::ylim() is that it redraws new breaks # coord_cartesian(ylim = y.lim)) not used because bug -> y-axis label disappearance with y.lim decreasing I DO NOT UNDERSTAND THIS MESSAGE WHILE I USE COORD_CARTESIAN # clip = "off" to have secondary ticks outside plot region does not work -}else{ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_flip(ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region does not work # create the problem of y-axis label disappearance with y.lim decreasing. IDEM ABOVE - -} -# end y scale management (cannot be before dot plot management) - - -# legend management -if( ! is.null(legend.width)){ -legend.final <- fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path) # get legend -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend -if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE -legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end legend management - - -# drawing -fin.plot <- suppressMessages(suppressWarnings(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))) -grob.save <- NULL -if(plot == TRUE){ -# following lines inactivated because of problem in warn.recov and message.recov -# assign("env_fun_get_message", new.env()) -# assign("tempo.gg.name", tempo.gg.name, envir = env_fun_get_message) -# assign("tempo.gg.count", tempo.gg.count, envir = env_fun_get_message) -# assign("add", add, envir = env_fun_get_message) -# two next line: for the moment, I cannot prevent the warning printing -# warn.recov <- fun_get_message(paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}), kind = "warning", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering warnings printed by ggplot() functions -# message.recov <- fun_get_message('print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))', kind = "message", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering messages printed by ggplot() functions -# if( ! (return == TRUE & return.ggplot == TRUE)){ # because return() plots when return.ggplot is TRUE # finally not used -> see return.ggplot description -if(is.null(legend.width)){ -grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot))) -}else{ -grob.save <-suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width)))) -} -# } -# suppressMessages(suppressWarnings(print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add})))))) -}else{ -# following lines inactivated because of problem in warn.recov and message.recov -# message.recov <- NULL -# warn.recov <- NULL -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end drawing - - - -# output -# following lines inactivated because of problem in warn.recov and message.recov -# if( ! (is.null(warn) & is.null(warn.recov) & is.null(message.recov))){ -# warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0 | length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", ifelse( ! is.null(warn.recov), unique(message.recov), ""), ifelse( ! is.null(message.recov), unique(message.recov), ""), collapse = "\n\n"), "\n\n")}) -# }else if( ! (is.null(warn) & is.null(warn.recov)) & is.null(message.recov)){ -# warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(warn.recov), collapse = "\n\n"), "\n\n")}) -# }else if( ! (is.null(warn) & is.null(message.recov)) & is.null(warn.recov)){ -# warn <- paste0(warn, "\n\n", if(length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(message.recov), collapse = "\n\n"), "\n\n")}) -# } -if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) -} -on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) -if(return == TRUE){ -tempo.output <- ggplot2::ggplot_build(fin.plot) -tempo.output$data <- tempo.output$data[-1] # remove the first data because corresponds to the initial empty boxplot -if(length(tempo.output$data) != length(coord.names)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nlength(tempo.output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -names(tempo.output$data) <- coord.names -tempo.output$data <- tempo.output$data[coord.names != "bad_remove"] -} -tempo <- tempo.output$layout$panel_params[[1]] -output <- list( -data = data1.ini, -stat = stat.nolog, -removed.row.nb = removed.row.nb, -removed.rows = removed.rows, -plot = c(tempo.output$data, y.second.tick.values = list(y.second.tick.values)), -panel = facet.categ, -axes = list( -x.range = tempo$x.range, -x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE) -x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, -y.range = tempo$y.range, -y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, -y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))} -), -warn = paste0("\n", warn, "\n\n"), -ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE -gtable = if(return.gtable == TRUE){grob.save}else{NULL} -) -return(output) # this plots the graph if return.ggplot is TRUE and if no assignment -} -# end output -# end main code -} - - - - - - -# add density -# rasterise all kind: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html - - -fun_gg_scatter <- function( -data1, -x, -y, -categ = NULL, -categ.class.order = NULL, -color = NULL, -geom = "geom_point", -geom.step.dir = "hv", -geom.stick.base = NULL, -alpha = 0.5, -dot.size = 2, -dot.shape = 21, -dot.border.size = 0.5, -dot.border.color = NULL, -line.size = 0.5, -line.type = "solid", -x.lim = NULL, -x.lab = NULL, -x.log = "no", -x.tick.nb = NULL, -x.second.tick.nb = NULL, -x.include.zero = FALSE, -x.left.extra.margin = 0.05, -x.right.extra.margin = 0.05, -x.text.angle = 0, -y.lim = NULL, -y.lab = NULL, -y.log = "no", -y.tick.nb = NULL, -y.second.tick.nb = NULL, -y.include.zero = FALSE, -y.top.extra.margin = 0.05, -y.bottom.extra.margin = 0.05, -y.text.angle = 0, -raster = FALSE, -raster.ratio = 1, -raster.threshold = NULL, -text.size = 12, -title = "", -title.text.size = 12, -legend.show = TRUE, -legend.width = 0.5, -legend.name = NULL, -article = TRUE, -grid = FALSE, -add = NULL, -return = FALSE, -return.ggplot = FALSE, -return.gtable = TRUE, -plot = TRUE, -warn.print = FALSE, -lib.path = NULL -){ -# AIM -# Plot ggplot2 scatterplot with the possibility to overlay dots from up to 3 different data frames (-> three different legends) and lines from up to 3 different data frames (-> three different legends) -> up to 6 overlays totally -# For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html -# WARNINGS -# Rows containing NA in data1[, c(x, y, categ)] will be removed before processing, with a warning (see below) -# Size arguments (dot.size, dot.border.size, line.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE)) -# ARGUMENTS -# data1: a dataframe compatible with ggplot2, or a list of data frames. Order matters for the order of the legend and for the layer staking (starting from below to top) -# x: single character string of the data1 column name for x-axis coordinates. If data1 is a list, then x must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_hline" in geom argument -# y: single character string of the data1 column name for y-axis coordinates. If data1 is a list, then y must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_vline" in geom argument -# categ: either NULL or a single character string or a list of single character strings, indicating the data1 column names to use for categories which creates legend display -# If categ == NULL, no categories -> no legend displayed -# If data1 is a data frame, categ must be a single character string of the data1 column name for categories -# If data1 is a list, then categ must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (no legend display for these compartments), and other not -# categ.class.order: either (1) NULL or (2) a vector of character strings or (3) a list of these vectors, setting the order of the classes of categ in the legend display -# If categ.class.order is NULL, classes are represented according to the alphabetical order -# If data1 is a data frame, categ.class.order must be a vector of character strings specifying the different classes in the categ column name of data1 -# If data1 is a list, then categ.class.order must be a list of vector of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (alphabetical order for these compartments), and other not -# color: either (1) NULL, or (2) a vector of character strings or integers, or (3) a list of vectors of character strings or integers -# If color is NULL, default colors of ggplot2 -# If data1 is a data frame, color argument can be either: -# (1) a single color string. All the dots of the corresponding data1 will have this color, whatever the categ value (NULL or not) -# (2) if categ is non-null, a vector of string colors, one for each class of categ. Each color will be associated according to the categ.class.order argument if specified, or to the alphabetical order of categ classes otherwise -# (3) if categ is non-null, a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ and a single class of categ per color must be respected -# Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the max integer value among all the integers in color (see fun_gg_palette()) -# If data1 is a list, then color argument must be either: -# (1) a list of character strings or integers, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. -# (2) a single character string or a single integer -# With a list (first possibility), the rules described for when data1 is a data frame apply to each compartment of the list. Some of the compartments can be NULL. In that case, a different grey color will be used for each NULL compartment. With a single value (second possibility), the same color will be used for all the dots and lines, whatever the data1 list -# geom: single character string of the kind of plot, or a list of single character strings -# Either: -# "geom_point" (scatterplot) -# "geom_line" (coordinates plotted then line connection, from the lowest to highest x coordinates first and from the lowest to highest y coordinates thenafter) -# "geom_path" (coordinates plotted then line connection respecting the row order in data1) -# "geom_step" coordinates plotted then line connection respecting the row order in data1 but drawn in steps). See the geom.step.dir argument -# "geom_hline" (horizontal line, no x value provided) -# "geom_vline" (vertical line, no y value provided) -# "geom_stick" (dots as vertical bars) -# If data1 is a list, then geom must be either: -# (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. -# (2) a single character string. In that case the same kind of plot will apply for the different compartments of the data1 list -# WARNING concerning "geom_hline" or "geom_vline": -# (1) x or y argument must be NULL, respectively -# (2) x.lim or y.lim argument must NOT be NULL, respectively, if only these kind of lines are drawn (if other geom present, then x.lim = NULL and y.lim = NULL will generate x.lim and y.lim defined by these other geom, which is not possible with "geom_hline" or "geom_vline" alone) -# (3) the function will draw n lines for n values in the x argument column name of the data1 data frame. If several colors required, the categ argument must be specified and the corresponding categ column name must exist in the data1 data frame with a different class name for each row -# geom.step.dir: single character string indicating the direction when using "geom_step" of the geom argument, or a list of single character strings -# Either: -# "vh" (vertical then horizontal) -# "hv" (horizontal then vertical) -# "mid" (step half-way between adjacent x-values) -# See https://ggplot2.tidyverse.org/reference/geom_path.html -# If data1 is a list, then geom.step.dir must be either: -# (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. The value in compartments related to other geom values than "geom_step" will be ignored -# (2) a single character string, which will be used for all the "geom_step" values of the geom argument, whatever the data1 list -# geom.stick.base: either (1) NULL or (2) a single numeric value or (3) a list of single numeric values, setting the base of the sticks when using "geom_stick" of the geom argument -# If geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base -# If data1 is a list, then geom.stick.base must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the values in compartments related to other geom values than "geom_stick" will be ignored. With a single value (latter possibility), the same base will be used for all the sticks, whatever the data1 list -# Warning: the y-axis limits are not modified by the value of geom.stick.base, meaning that this value can be outside of the range of y.lim. Add the value of geom.stick.base also in the y.lim argument if required -# Warning: if geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base. Thus, be careful with inverted y-axis -# alpha: single numeric value (from 0 to 1) of transparency. If data1 is a list, then alpha must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. In that case the same transparency will apply for the different compartments of the data1 list -# dot.size: single numeric value of dot shape radius? in mm. If data1 is a list, then dot.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.size will be used for all the dots, whatever the data1 list -# dot.shape: value indicating the shape of the dots (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then dot.shape must be either (1) a list of single shape values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single shape value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.shape will be used for all the dots, whatever the data1 list -# dot.border.size: single numeric value of border dot width in mm. Write zero for no dot border. If data1 is a list, then dot.border.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.border.size will be used for all the dots, whatever the data1 list -# dot.border.color: single character color string defining the color of the dot border (same border color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette() -# line.size: single numeric value of line width in mm. If data1 is a list, then line.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.size will be used for all the lines, whatever the data1 list -# line.type: value indicating the kind of lines (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then line.type must be either (1) a list of single line kind values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single line kind value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.type will be used for all the lines, whatever the data1 list -# x.lim: 2 numeric values setting the x-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the x column name of data1 will be used -# x.lab: a character string or expression for x-axis label. If NULL, will use the first value of x (x column name of the first data frame in data1). Warning message if the elements in x are different between data frames in data1 -# x.log: either "no", "log2" (values in the x column name of the data1 data frame will be log2 transformed and x-axis will be log2 scaled) or "log10" (values in the x column name of the data1 data frame will be log10 transformed and x-axis will be log10 scaled) -# x.tick.nb: approximate number of desired values labeling the x-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if x.log is "no", then the number of labeling values is set by ggplot2. If NULL and if x.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the x.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for x.lim = c(9, 1200)). WARNING: if non-NULL and if x.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) -# x.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if x.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$x.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks -# x.include.zero: logical. Does x.lim range include 0? Ignored if x.log is "log2" or "log10" -# x.left.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to x.lim. If different from 0, add the range of the axis multiplied by x.left.extra.margin (e.g., abs(x.lim[2] - x.lim[1]) * x.left.extra.margin) to the left of x-axis -# x.right.extra.margin: idem as x.left.extra.margin but to the right of x-axis -# x.text.angle: integer value of the text angle for the x-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. -# y.lim: 2 numeric values setting the y-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the y column name of data1 will be used -# y.lab: a character string or expression for y-axis label. If NULL, will use the first value of y (y column name of the first data frame in data1). Warning message if the elements in y are different between data frames in data1 -# y.log: either "no", "log2" (values in the y column name of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y column name of the data1 data frame will be log10 transformed and y-axis will be log10 scaled) -# y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) -# y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks -# y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10" -# y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis -# y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis -# y.text.angle: integer value of the text angle for the y-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. -# raster: logical. Dots in raster mode? If FALSE, dots from each "geom_point" from geom argument are plotted in vectorial mode (bigger pdf and long to display if lots of dots). If TRUE, dots from each "geom_point" from geom argument are plotted in matricial mode (smaller pdf and easy display if lots of dots, but it takes time to generate the layer). If TRUE, the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If TRUE, solve the transparency problem with some GUI. Overriden by the non-NULL raster.threshold argument -# raster.ratio: single numeric value indicating the height / width ratio of the graphic device used (for instance provided by the $dim compartment in the output of the fun_open() function). The default value is 1 because by default R opens a square graphic device. But this argument has to be set when using other device dimensions. Ignored if raster == FALSE -# raster.threshold: positive integer value indicating the limit of the dot number above which "geom_point" layers from the geom argument switch from vectorial mode to matricial mode (see the raster argument). If any layer is matricial, then the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If non-NULL, it overrides the raster argument -# text.size: numeric value of the font size of the (1) axis numbers and axis legends and (2) texts in the graphic legend (in mm) -# title: character string of the graph title -# title.text.size: numeric value of the title font size in mm -# legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not -# legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend -# legend.name: character string of the legend title. If legend.name is NULL and categ argument is not NULL, then legend.name <- categ. If data1 is a list, then legend.name must be a list of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL, and other not -# article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme -# grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present) -# add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL -# WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()" -# If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_scatter() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments) -# Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions -# WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_scatter() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))' -# return: logical. Return the graph parameters? -# return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_scatter() function (e.g., a <- fun_gg_scatter()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details -# return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details -# plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting -# warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE -# lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders -# RETURN -# a scatter plot if plot argument is TRUE -# a list of the graph info if return argument is TRUE: -# $data: the initial data with graphic information added. WARNING: if the x.log or y.log argument is not "no", x or y argument column of the data1 data frame are log2 or log10 converted in $data, respectively. Use 2^values or 10^$values to recover the initial values -# $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed -# $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed -# $plot: the graphic box and dot coordinates -# $dots: dot coordinates -# y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no") -# y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-null or if y.log argument is different from "no") -# $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0 -# $axes: the x-axis and y-axis info -# $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE -# $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot -# $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot -# REQUIRED PACKAGES -# ggplot2 -# gridExtra -# lemon (in case of use in the add argument) -# scales -# if raster plots are drawn (see the raster and raster.threshold arguments): -# Cairo -# grid -# REQUIRED FUNCTIONS FROM THE cute PACKAGE -# fun_gg_empty_graph() -# fun_gg_palette() -# fun_gg_point_rast() -# fun_pack() -# fun_check() -# fun_round() -# fun_scale() -# fun_inter_ticks() -# EXAMPLES -# set.seed(1) ; obs1 <- data.frame(Km = c(2, 1, 6, 5, 4, 7), Time = c(2, 1, 6, 5, 4, 7)^2, Car = c("TUUT", "TUUT", "TUUT", "WIIM", "WIIM", "WIIM"), Color1 = rep(c("coral", "lightblue"), each = 3), stringsAsFactors = TRUE) ; fun_gg_scatter(data1 = obs1, x = "Km", y = "Time") -# DEBUGGING -# set.seed(1) ; obs1 <- data.frame(km = rnorm(1000, 10, 3), time = rnorm(1000, 10, 3), group1 = rep(c("A1", "A2"), 500), stringsAsFactors = TRUE) ; obs2 <-data.frame(km = rnorm(1000, 15, 3), time = rnorm(1000, 15, 3), group2 = rep(c("G1", "G2"), 500), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$km[2:3] <- NA ; data1 = list(L1 = obs1, L2 = obs2) ; x = list(L1 = "km", L2 = "km") ; y = list(L1 = "time", L2 = "time") ; categ = list(L1 = "group1", L2 = "group2") ; categ = NULL ; categ.class.order = NULL ; color = NULL ; geom = "geom_point" ; geom.step.dir = "hv" ; geom.stick.base = NULL ; alpha = 0.5 ; dot.size = 2 ; dot.shape = 21 ; dot.border.size = 0.5 ; dot.border.color = NULL ; line.size = 0.5 ; line.type = "solid" ; x.lim = NULL ; x.lab = NULL ; x.log = "no" ; x.tick.nb = NULL ; x.second.tick.nb = NULL ; x.include.zero = FALSE ; x.left.extra.margin = 0.05 ; x.right.extra.margin = 0.05 ; x.text.angle = 0 ; y.lim = NULL ; y.lab = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = NULL ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; y.text.angle = 0 ; raster = FALSE ; raster.ratio = 1 ; raster.threshold = NULL ; text.size = 12 ; title = "" ; title.text.size = 12 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL -# function name -function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()") -arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments -arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) -# end function name -# required function checking -req.function <- c( -"fun_check", -"fun_gg_just", -"fun_gg_empty_graph", -"fun_gg_palette", -"fun_gg_point_rast", -"fun_round", -"fun_pack", -"fun_scale", -"fun_inter_ticks" -) -tempo <- NULL -for(i1 in req.function){ -if(length(find(i1, mode = "function"))== 0L){ -tempo <- c(tempo, i1) -} -} -if( ! is.null(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end required function checking -# reserved words to avoid bugs (used in this function) -reserved.words <- c("fake_x", "fake_y", "fake_categ") -# end reserved words to avoid bugs (used in this function) -# arg with no default values -mandat.args <- c( -"data1", -"x", -"y" -) -tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")"))) -if(any(tempo)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end arg with no default values -# argument primary checking -arg.check <- NULL # -text.check <- NULL # -checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools -ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) -tempo1 <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = data1, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(x)){ -tempo1 <- fun_check(data = x, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = x, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(y)){ -tempo1 <- fun_check(data = y, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = y, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(categ)){ -tempo1 <- fun_check(data = categ, class = "vector", mode = "character", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = categ, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = categ, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(categ.class.order)){ -if(is.null(categ)){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT IS NOT NULL, BUT categ IS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = categ.class.order, class = "vector", mode = "character", fun.name = function.name) -tempo2 <- fun_check(data = categ.class.order, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A VECTOR OF CHARACTER STRINGS OR A LIST OF VECTOR OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = categ.class.order, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(legend.name)){ -tempo1 <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = legend.name, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = legend.name, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(color)){ -tempo1 <- fun_check(data = color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = color, class = "factor", na.contain = TRUE, fun.name = function.name) -tempo3 <- fun_check(data = color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name) -tempo4 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo4$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE & tempo4$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A VECTOR (OF CHARACTER STRINGS OR INTEGERS) OR A FACTOR OR A LIST OF THESE POSSIBILITIES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = color, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo1 <- fun_check(data = geom, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = geom, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = geom.step.dir, options = c("vh", "hv", "mid"), na.contain = FALSE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = geom.step.dir, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A SINGLE CHARACTER STRING (\"vh\" OR \"hv\" OR \"mid\") OR A LIST OF THESE CHARACTER STRINGS") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(geom.stick.base)){ -tempo1 <- fun_check(data = geom.stick.base, class = "vector", mode = "numeric", na.contain = FALSE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = geom.stick.base, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo1 <- fun_check(data = alpha, prop = TRUE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = alpha, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A SINGLE NUMERIC VALUE BETWEEN 0 AND 1 OR A LIST OF SUCH VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) -tempo2 <- fun_check(data = dot.size, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = dot.shape, class = "vector", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = dot.shape, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A SINGLE SHAPE VALUE OR A LIST OF SINGLE SHAPE VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) -tempo2 <- fun_check(data = dot.border.size, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(dot.border.color)){ -tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name) -tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -# integer colors -> gg_palette -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color MUST BE A SINGLE CHARACTER STRING OF COLOR OR A SINGLE INTEGER VALUE") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = dot.border.color, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo1 <- fun_check(data = line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) -tempo2 <- fun_check(data = line.size, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo2$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -tempo1 <- fun_check(data = line.type, class = "vector", typeof = "integer", double.as.integer.allowed = FALSE, length = 1, fun.name = function.name) -tempo2 <- fun_check(data = line.type, class = "vector", mode = "character", length = 1, fun.name = function.name) -tempo3 <- fun_check(data = line.type, class = "list", na.contain = TRUE, fun.name = function.name) -checked.arg.names <- c(checked.arg.names, tempo3$object.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A SINGLE LINE KIND VALUE OR A LIST OF SINGLE LINE KIND VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -if( ! is.null(x.lim)){ -tempo <- fun_check(data = x.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & any(x.lim %in% c(Inf, -Inf))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x.lim, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(x.lab)){ -if(all(class(x.lab) %in% "expression")){ # to deal with math symbols -tempo <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee) -}else{ -tempo <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x.lab, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = x.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(x.tick.nb)){ -tempo <- fun_check(data = x.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & x.tick.nb < 0){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(x.second.tick.nb)){ -tempo <- fun_check(data = x.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & x.second.tick.nb <= 0){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = x.second.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = x.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.left.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.right.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = x.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) -if( ! is.null(y.lim)){ -tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & any(y.lim %in% c(Inf, -Inf))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.lim, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(y.lab)){ -if(all(class(y.lab) %in% "expression")){ # to deal with math symbols -tempo <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee) -}else{ -tempo <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.lab, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(y.tick.nb)){ -tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & y.tick.nb < 0){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if( ! is.null(y.second.tick.nb)){ -tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE & y.second.tick.nb <= 0){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = y.second.tick.nb, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = y.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = raster, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = raster.ratio, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -if( ! is.null(raster.threshold)){ -tempo <- fun_check(data = raster.threshold, class = "vector", typeof = "integer", neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = raster.threshold, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(legend.width)){ -tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = legend.width, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(add)){ -tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = add, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) -if( ! is.null(lib.path)){ -tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) -if(tempo$problem == FALSE){ -if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA -tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) -text.check <- c(text.check, tempo.cat) -arg.check <- c(arg.check, TRUE) -} -} -}else{ -# no fun_check test here, it is just for checked.arg.names -tempo <- fun_check(data = lib.path, class = "vector") -checked.arg.names <- c(checked.arg.names, tempo$object.name) -} -if(any(arg.check) == TRUE){ -stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # -} -# source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() -# end argument primary checking - -# second round of checking and data preparation -# management of NA arguments -tempo.arg <- names(arg.user.setting) # values provided by the user -tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NA arguments -# management of NULL arguments -tempo.arg <-c( -"data1", -# "x", # inactivated because of hline or vline -# "y", # inactivated because of hline or vline -"geom", -"geom.step.dir", -# "geom.stick.base", # inactivated because can be null -"alpha", -"dot.size", -"dot.shape", -"dot.border.size", -"line.size", -"line.type", -"x.log", -"x.include.zero", -"x.left.extra.margin", -"x.right.extra.margin", -"x.text.angle", -"y.log", -"y.include.zero", -"y.top.extra.margin", -"y.bottom.extra.margin", -"y.text.angle", -"raster", -"raster.ratio", -"text.size", -"title", -"title.text.size", -"legend.show", -# "legend.width", # inactivated because can be null -"article", -"grid", -"return", -"return.ggplot", -"return.gtable", -"plot", -"warn.print" -) -tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) -if(any(tempo.log) == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == -} -# end management of NULL arguments -# code that protects set.seed() in the global environment -# end code that protects set.seed() in the global environment -# warning initiation -ini.warning.length <- options()$warning.length -options(warning.length = 8170) -warn <- NULL -warn.count <- 0 -# end warning initiation -# other checkings -# check list lengths (and names of data1 compartments if present) -list.color <- NULL -list.geom <- NULL -list.geom.step.dir <- NULL -list.geom.stick.base <- NULL -list.alpha <- NULL -list.dot.size <- NULL -list.dot.shape <- NULL -list.dot.border.size <- NULL -list.dot.border.color <- NULL -list.line.size <- NULL -list.line.type <- NULL -if(all(class(data1) == "list")){ -if(length(data1) > 6){ -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A LIST OF 6 DATA FRAMES MAXIMUM (6 OVERLAYS MAX)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(is.null(names(data1))){ -names(data1) <- paste0("L", 1:length(data1)) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL NAME COMPARTMENT OF data1 LIST -> NAMES RESPECTIVELY ATTRIBUTED TO EACH COMPARTMENT:\n", paste(names(data1), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -if( ! is.null(x)){ -if( ! (all(class(x) == "list") & length(data1) == length(x))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -}else{ -x <- vector("list", length(data1)) -} -if( ! is.null(y)){ -if( ! (all(class(y) == "list") & length(data1) == length(y))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -}else{ -y <- vector("list", length(data1)) -} -if( ! is.null(categ)){ -if( ! (all(class(categ) == "list") & length(data1) == length(categ))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if( ! is.null(categ.class.order)){ -if( ! (all(class(categ.class.order) == "list") & length(data1) == length(categ.class.order))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if( ! is.null(color)){ -if( ! ((all(class(color) == "list") & length(data1) == length(color)) | ((all(mode(color) == "character") | all(mode(color) == "numeric")) & length(color)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER STRING OR INTEGER") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if((all(mode(color) == "character") | all(mode(color) == "numeric")) & length(color)== 1L){ # convert the single value into a list of single value -list.color <- vector(mode = "list", length = length(data1)) -list.color[] <- color -} -} -if( ! ((all(class(geom) == "list") & length(data1) == length(geom)) | (all(mode(geom) == "character") & length(geom)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(geom) == "character") & length(geom)== 1L){ # convert the single value into a list of single value -list.geom <- vector(mode = "list", length = length(data1)) -list.geom[] <- geom -} -if( ! ((all(class(geom.step.dir) == "list") & length(data1) == length(geom.step.dir)) | (all(mode(geom.step.dir) == "character") & length(geom.step.dir)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(geom.step.dir) == "character") & length(geom.step.dir)== 1L){ # convert the single value into a list of single value -list.geom.step.dir <- vector(mode = "list", length = length(data1)) -list.geom.step.dir[] <- geom.step.dir -} -if( ! is.null(geom.stick.base)){ -if( ! ((all(class(geom.stick.base) == "list") & length(data1) == length(geom.stick.base)) | (all(mode(geom.stick.base) == "numeric") & length(geom.stick.base)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(geom.stick.base) == "numeric") & length(geom.stick.base)== 1L){ # convert the single value into a list of single value -list.geom.stick.base <- vector(mode = "list", length = length(data1)) -list.geom.stick.base[] <- geom.stick.base -} -} -if( ! ((all(class(alpha) == "list") & length(data1) == length(alpha)) | (all(mode(alpha) == "numeric") & length(alpha)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(alpha) == "numeric") & length(alpha)== 1L){ # convert the single value into a list of single value -list.alpha <- vector(mode = "list", length = length(data1)) -list.alpha[] <- alpha -} -if( ! ((all(class(dot.size) == "list") & length(data1) == length(dot.size)) | (all(mode(dot.size) == "numeric") & length(dot.size)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(dot.size) == "numeric") & length(dot.size)== 1L){ # convert the single value into a list of single value -list.dot.size <- vector(mode = "list", length = length(data1)) -list.dot.size[] <- dot.size -} -if( ! ((all(class(dot.shape) == "list") & length(data1) == length(dot.shape)) | (all(mode(dot.shape) != "list") & length(dot.shape)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE SHAPE VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(dot.shape) != "list") & length(dot.shape)== 1L){ # convert the single value into a list of single value -list.dot.shape <- vector(mode = "list", length = length(data1)) -list.dot.shape[] <- dot.shape -} -if( ! ((all(class(dot.border.size) == "list") & length(data1) == length(dot.border.size)) | (all(mode(dot.border.size) == "numeric") & length(dot.border.size)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(dot.border.size) == "numeric") & length(dot.border.size)== 1L){ # convert the single value into a list of single value -list.dot.border.size <- vector(mode = "list", length = length(data1)) -list.dot.border.size[] <- dot.border.size -} -if( ! is.null(dot.border.color)){ -if( ! ((all(class(dot.border.color) == "list") & length(data1) == length(dot.border.color)) | ((all(mode(dot.border.color) == "character") | all(mode(dot.border.color) == "numeric")) & length(dot.border.color)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER STRING OR INTEGER") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if((all(mode(dot.border.color) == "character") | all(mode(dot.border.color) == "numeric")) & length(dot.border.color)== 1L){ # convert the single value into a list of single value -list.dot.border.color <- vector(mode = "list", length = length(data1)) -list.dot.border.color[] <- dot.border.color -} -} -if( ! ((all(class(line.size) == "list") & length(data1) == length(line.size)) | (all(mode(line.size) == "numeric") & length(line.size)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(line.size) == "numeric") & length(line.size)== 1L){ # convert the single value into a list of single value -list.line.size <- vector(mode = "list", length = length(data1)) -list.line.size[] <- line.size -} -if( ! ((all(class(line.type) == "list") & length(data1) == length(line.type)) | (all(mode(line.type) != "list") & length(line.type)== 1L))){ # list of same length as data1 or single value -tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE LINE KIND VALUE") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(all(mode(line.type) != "list") & length(line.type)== 1L){ # convert the single value into a list of single value -list.line.type <- vector(mode = "list", length = length(data1)) -list.line.type[] <- line.type -} -if( ! is.null(legend.name)){ -if( ! (all(class(legend.name) == "list") & length(data1) == length(legend.name))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -} -# end check list lengths (and names of data1 compartments if present) -# conversion into lists -if(all(is.data.frame(data1))){ -data1 <- list(L1 = data1) -if(all(class(x) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -x <- list(L1 = x) -} -if(all(class(y) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -y <- list(L1 = y) -} -if( ! is.null(categ)){ -if(all(class(categ) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -categ <- list(L1 = categ) -} -} -if( ! is.null(categ.class.order)){ -if(all(class(categ.class.order) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -categ.class.order <- list(L1 = categ.class.order) -} -} -if( ! is.null(color)){ -if(all(class(color) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -color <- list(L1 = color) -} -} -if(all(class(geom) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -geom <- list(L1 = geom) -} -if(all(class(geom.step.dir) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -geom.step.dir <- list(L1 = geom.step.dir) -} -if( ! is.null(geom.stick.base)){ -if(all(class(geom.stick.base) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -geom.stick.base <- list(L1 = geom.stick.base) -} -} -if(all(class(alpha) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -alpha <- list(L1 = alpha) -} -if(all(class(dot.size) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -dot.size <- list(L1 = dot.size) -} -if(all(class(dot.shape) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -dot.shape <- list(L1 = dot.shape) -} -if(all(class(dot.border.size) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -dot.border.size <- list(L1 = dot.border.size) -} -if( ! is.null(dot.border.color)){ -if(all(class(dot.border.color) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -dot.border.color <- list(L1 = dot.border.color) -} -} -if(all(class(line.size) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -line.size <- list(L1 = line.size) -} -if(all(class(line.type) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -line.type <- list(L1 = line.type) -} -if( ! is.null(legend.name)){ -if(all(class(legend.name) == "list")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -legend.name <- list(L1 = legend.name) -} -} -}else if( ! all(sapply(data1, FUN = "class") == "data.frame")){ # if not a data frame, data1 can only be a list, as tested above -tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -# single value converted into list now reattributed to the argument name -if( ! is.null(color)){ -if( ! is.null(list.color)){ -color <- list.color -} -} -if( ! is.null(list.geom)){ -geom <- list.geom -} -if( ! is.null(list.geom.step.dir)){ -geom.step.dir <- list.geom.step.dir -} -if( ! is.null(geom.stick.base)){ -if( ! is.null(list.geom.stick.base)){ -geom.stick.base <- list.geom.stick.base -} -} -if( ! is.null(list.alpha)){ -alpha <- list.alpha -} -if( ! is.null(list.dot.size)){ -dot.size <- list.dot.size -} -if( ! is.null(list.dot.shape)){ -dot.shape <- list.dot.shape -} -if( ! is.null(list.dot.border.size)){ -dot.border.size <- list.dot.border.size -} -if( ! is.null(dot.border.color)){ -if( ! is.null(list.dot.border.color)){ -dot.border.color <- list.dot.border.color -} -} -if( ! is.null(list.line.size)){ -line.size <- list.line.size -} -if( ! is.null(list.line.type)){ -line.type <- list.line.type -} -# end single value converted into list now reattributed to the argument name -# data, x, y, geom, alpha, dot.size, shape, dot.border.size, line.size, line.type, legend.name are list now -# if non-null, categ, categ.class.order, legend.name, color, dot.border.color are list now -# end conversion into lists -# verif of add -if( ! is.null(add)){ -if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by + -tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) - -}else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ # -tempo.cat <- paste0("ERROR IN ", function.name, ": FOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by ) -tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -# end verif of add -# management of add containing facet -facet.categ <- NULL -if( ! is.null(add)){ -facet.check <- TRUE -tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) # -tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap") -tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid") -tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep") -if(length(data1) > 1 & (any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")) | grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid"))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nfacet PANELS CANNOT BE USED IF MORE THAN ONE DATA FRAME IN THE data1 ARGUMENT\nPLEASE REWRITE THE add STRING AND RERUN") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"))){ -tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")]))) -facet.categ <- list(names(tempo1$params$facets)) # list of length 1 -tempo.text <- "facet_wrap OR facet_rep_wrap" -facet.check <- FALSE -}else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){ -tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")]))) -facet.categ <- list(c(names(tempo1$params$rows), names(tempo1$params$cols))) # list of length 1 -tempo.text <- "facet_grid OR facet_rep_grid" -facet.check <- FALSE -} -if(facet.check == FALSE & ! all(facet.categ %in% names(data1[[1]]))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL -tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1[[1]]), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -} -# if facet.categ is not NULL, it is a list of length 1 now -# end management of add containing facet -# legend name filling -if(is.null(legend.name) & ! is.null(categ)){ -legend.name <- categ -}else if(is.null(legend.name) & is.null(categ)){ -legend.name <- vector("list", length(data1)) # null list -} -# legend.name not NULL anymore (list) -# end legend name filling -# ini categ for legend display -fin.lg.disp <- vector("list", 6) # will be used at the end to display or not legends -fin.lg.disp[] <- FALSE -legend.disp <- vector("list", length(data1)) -if(is.null(categ) | legend.show == FALSE){ -legend.disp[] <- FALSE -}else{ -for(i2 in 1:length(data1)){ -if(is.null(categ[[i2]])){ -legend.disp[[i2]] <- FALSE -}else{ -legend.disp[[i2]] <- TRUE -} -} -} -# end ini categ for legend display -# integer colors into gg_palette -tempo.check.color <- NULL -for(i1 in 1:length(data1)){ -if(any(is.na(color[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), ": color ARGUMENT CANNOT CONTAIN NA") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo.check.color <- c(tempo.check.color, fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem) -} -tempo.check.color <- ! tempo.check.color # invert TRUE and FALSE because if integer, then problem = FALSE -if(any(tempo.check.color == TRUE)){ # convert integers into colors -tempo.integer <- unlist(color[tempo.check.color]) -tempo.color <- fun_gg_palette(max(tempo.integer, na.rm = TRUE)) -for(i1 in 1:length(data1)){ -if(tempo.check.color[i1] == TRUE){ -color[[i1]] <-tempo.color[color[[i1]]] -} -} -} -# end integer colors into gg_palette -# loop (checking inside list compartment) -compart.null.color <- 0 # will be used to attribute a color when color is non-null but a compartment of color is NULL -data1.ini <- data1 # to report NA removal -removed.row.nb <- vector("list", length = length(data1)) # to report NA removal. Contains NULL -removed.rows <- vector("list", length = length(data1)) # to report NA removal. Contains NULL -for(i1 in 1:length(data1)){ -tempo <- fun_check(data = data1[[i1]], data.name = ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "data.frame", na.contain = TRUE, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -# reserved word checking -if(any(names(data1[[i1]]) %in% reserved.words)){ # I do not use fun_name_change() because cannot control y before creating "fake_y". But ok because reserved are not that common -tempo.cat <- paste0("ERROR IN ", function.name, ": COLUMN NAMES OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " ARGUMENT CANNOT BE ONE OF THESE WORDS\n", paste(reserved.words, collapse = " "), "\nTHESE ARE RESERVED FOR THE ", function.name, " FUNCTION") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if( ! (is.null(add))){ -if(any(sapply(X = reserved.words, FUN = grepl, x = add))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nFOLLOWING COLUMN NAMES HAVE TO BE CHANGED:\n", paste(arg.names[sapply(X = reserved.words, FUN = grepl, x = add)], collapse = "\n"), "\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(any(sapply(X = arg.names, FUN = grepl, x = add))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end reserved word checking -# check of geom now because required for y argument -tempo <- fun_check(data = geom[[i1]], data.name = ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), options = c("geom_point", "geom_line", "geom_path", "geom_step", "geom_hline", "geom_vline", "geom_stick"), length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(geom[[i1]] == "geom_step" & is.null(geom.step.dir[[i1]])){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(geom.step.dir)== 1L, "geom.step.dir", paste0("ELEMENT ", i1, " OF geom.step.dir ARGUMENT")), ": geom.step.dir ARGUMENT CANNOT BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("ELEMENT ", i1, " OF geom")), " ARGUMENT IS \"geom_step\"\nHERE geom.step.dir ARGUMENT IS: ", paste(geom.step.dir[[i1]], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(geom[[i1]] == "geom_step" & ! is.null(geom.step.dir[[i1]])){ -tempo <- fun_check(data = geom.step.dir[[i1]], data.name = ifelse(length(geom.step.dir)== 1L, "geom.step.dir", paste0("geom.step.dir NUMBER ", i1)), options = c("vh", "hv", "mid"), length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if( ! (is.null(geom.stick.base))){ -if(geom[[i1]] == "geom_stick" & ! is.null(geom.stick.base[[i1]])){ -tempo <- fun_check(data = geom.stick.base[[i1]], data.name = ifelse(length(geom.stick.base)== 1L, "geom.stick.base", paste0("geom.stick.base NUMBER ", i1)), mode = "numeric", length = 1, na.contain = FALSE, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -} -# end check of geom now because required for y argument -if(is.null(x[[i1]])){ -if(all(geom[[i1]] != "geom_hline")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": x ARGUMENT CANNOT BE NULL EXCEPT IF ", ifelse(length(geom)== 1L, "x", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_hline\"\nHERE geom ARGUMENT IS: ", paste(geom[[i1]], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -x[[i1]] <- "fake_x" -data1[[i1]] <- cbind(data1[[i1]], fake_x = NA, stringsAsFactors = TRUE) -data1[[i1]][, "fake_x"] <- as.numeric(data1[[i1]][, "fake_x"]) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT ASSOCIATED TO ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT ", geom[[i1]], " -> FAKE COLUMN ADDED TO DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", NAMED \"fake_x\" FOR FINAL DRAWING") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(all(geom[[i1]] == "geom_hline")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": x ARGUMENT MUST BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_hline\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo <- fun_check(data = x[[i1]], data.name = ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), class = "vector", mode = "character", length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(is.null(y[[i1]])){ -if(all(geom[[i1]] != "geom_vline")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": y ARGUMENT CANNOT BE NULL EXCEPT IF ", ifelse(length(geom)== 1L, "y", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_vline\"\nHERE geom ARGUMENT IS: ", paste(geom[[i1]], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -y[[i1]] <- "fake_y" -data1[[i1]] <- cbind(data1[[i1]], fake_y = NA, stringsAsFactors = TRUE) -data1[[i1]][, "fake_y"] <- as.numeric(data1[[i1]][, "fake_y"]) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT ASSOCIATED TO ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT ", geom[[i1]], " -> FAKE COLUMN ADDED TO DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", NAMED \"fake_y\" FOR FINAL DRAWING") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else{ -if(all(geom[[i1]] == "geom_vline")){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": y ARGUMENT MUST BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_vline\"") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo <- fun_check(data = y[[i1]], data.name = ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), class = "vector", mode = "character", length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -# x[[i1]] and y[[i1]] not NULL anymore -if( ! (x[[i1]] %in% names(data1[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(x[[i1]], collapse = " ")))) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if( ! (y[[i1]] %in% names(data1[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(y[[i1]], collapse = " ")))) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo <- fun_check(data = data1[[i1]][, x[[i1]]], data.name = ifelse(length(x)== 1L, "x ARGUMENT (AS COLUMN NAME OF data1 DATA FRAME)", paste0("ELEMENT ", i1, " OF x ARGUMENT", " (AS COLUMN NAME OF data1 DATA FRAME NUMBER ", i1, ")")), class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo <- fun_check(data = data1[[i1]][, y[[i1]]], data.name = ifelse(length(y)== 1L, "y ARGUMENT (AS COLUMN NAME OF data1 DATA FRAME)", paste0("ELEMENT ", i1, " OF y ARGUMENT", " (AS COLUMN NAME OF data1 DATA FRAME NUMBER ", i1, ")")), class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(x[[i1]] == "fake_x" & y[[i1]] == "fake_y"){ # because the code cannot accept to be both "fake_x" and "fake_y" at the same time -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2\nTHE CODE CANNOT ACCEPT x AND y TO BE \"fake_x\" AND \"fake_y\" IN THE SAME DATA FRAME ", i1, " ") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(( ! is.null(categ)) & ( ! is.null(categ[[i1]]))){ # is.null(categ[[i1]]) works even if categ is NULL # is.null(categ[[i1]]) works even if categ is NULL # if categ[[i1]] = NULL, fake_categ will be created later on -tempo <- fun_check(data = categ[[i1]], data.name = ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")),, class = "vector", mode = "character", length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if( ! (categ[[i1]] %in% names(data1[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(categ[[i1]], collapse = " ")))) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -tempo1 <- fun_check(data = data1[[i1]][, categ[[i1]]], data.name = ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) -tempo2 <- fun_check(data = data1[[i1]][, categ[[i1]]], data.name = ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "factor", na.contain = TRUE, fun.name = function.name) -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " MUST BE A FACTOR OR CHARACTER VECTOR") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(tempo1$problem == FALSE){ -data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) +######## fun_open() #### open a GUI or pdf graphic window -} -if(geom[[i1]] == "geom_vline" | geom[[i1]] == "geom_hline"){ -if(length(unique(data1[[i1]][, categ[[i1]]])) != nrow(data1[[i1]])){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(geom)== 1L, "geom OF data1 ARGUMENT", paste0("geom NUMBER ", i1, " OF DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " ARGUMENT IS ", geom[[i1]], ", MEANING THAT ", ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " MUST HAVE A DIFFERENT CLASS PER LINE OF data1 (ONE x VALUE PER CLASS)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -}else if(( ! is.null(categ)) & is.null(categ[[i1]])){ # is.null(categ[[i1]]) works even if categ is NULL # if categ[[i1]] = NULL, fake_categ will be created. WARNING: is.null(categ[[i1]]) means no legend display (see above), because categ has not been precised. This also means a single color for data1[[i1]] -if(length(color[[i1]]) > 1){ # 0 means is.null(color[[i1]]) or is.null(color) and 1 is ok -> single color for data1[[i1]] -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT BUT CORRESPONDING COLORS IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " HAS LENGTH OVER 1\n", paste(color[[i1]], collapse = " "), "\nWHICH IS NOT COMPATIBLE WITH NULL CATEG -> COLOR RESET TO A SINGLE COLOR") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -color[i1] <- list(NULL) # will provide a single color below # Warning color[[i1]] <- NULL removes the compartment -} -categ[[i1]] <- "fake_categ" -data1[[i1]] <- cbind(data1[[i1]], fake_categ = "", stringsAsFactors = TRUE) -# inactivated because give a different color to different "Line_" categ while a single color for all the data1[[i1]] required. Thus, put back after the color management -# if(geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline"){ -# data1[[i1]][, "fake_categ"] <- paste0("Line_", 1:nrow(data1[[i1]])) -# }else{ -data1[[i1]][, "fake_categ"] <- data1[[i1]][, "fake_categ"] # as.numeric("") create a vector of NA but class numeric -# } -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT -> FOR DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT:", paste0("NUMBER ", i1, " OF data1 ARGUMENT:")), "\n- FAKE \"fake_categ\" COLUMN ADDED FILLED WITH \"\"(OR WITH \"Line_...\" FOR LINES)\n- SINGLE COLOR USED FOR PLOTTING\n- NO LEGEND DISPLAYED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# OK: if categ is not NULL, all the non-null categ columns of data1 are factors from here - -# management of log scale and Inf removal -if(x[[i1]] != "fake_x"){ -if(any(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN ", ifelse(length(categ)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(y[[i1]] != "fake_y"){ -if(any(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN ", ifelse(length(categ)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# log conversion -if(x.log != "no"){ -tempo1 <- ! is.finite(data1[[i1]][, x[[i1]]]) # where are initial NA and Inf -data1[[i1]][, x[[i1]]] <- suppressWarnings(get(x.log)(data1[[i1]][, x[[i1]]]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -if(any( ! (tempo1 | is.finite(data1[[i1]][, x[[i1]]])))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN ", ifelse(length(categ)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(y.log != "no"){ -tempo1 <- ! is.finite(data1[[i1]][, y[[i1]]]) # where are initial NA and Inf -data1[[i1]][, y[[i1]]] <- suppressWarnings(get(y.log)(data1[[i1]][, y[[i1]]]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -if(any( ! (tempo1 | is.finite(data1[[i1]][, y[[i1]]])))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN ", ifelse(length(categ)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# Inf removal -# removed.row.nb[[i1]] <- NULL # already NULL and Warning this removes the compartment -removed.rows[[i1]] <- data.frame(stringsAsFactors = FALSE) -if(any(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])) detects only Inf -removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], which(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))) -} -if(any(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])) detects only Inf -removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], which(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))) -} -if( ! is.null(removed.row.nb[[i1]])){ -removed.row.nb[[i1]] <- unique(removed.row.nb[[i1]]) # to remove the duplicated positions (NA in both x and y) -removed.rows[[i1]] <- rbind(removed.rows[[i1]], data1.ini[[i1]][removed.row.nb[[i1]], ]) # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation -data1[[i1]] <- data1[[i1]][-removed.row.nb[[i1]], ] -data1.ini[[i1]] <- data1.ini[[i1]][-removed.row.nb[[i1]], ] # -} -# From here, data1 and data.ini have no more Inf -# end Inf removal -# x.lim and y.lim dealt later on, after the end f the loop -# end management of log scale and Inf removal -# na detection and removal -column.check <- unique(unlist(c( # unlist because creates a list -if(x[[i1]] == "fake_x"){NULL}else{x[[i1]]}, -if(y[[i1]] == "fake_y"){NULL}else{y[[i1]]}, -if( ! is.null(categ)){if(is.null(categ[[i1]])){NULL}else{categ[[i1]]}}, -if( ! is.null(facet.categ)){if(is.null(facet.categ[[i1]])){NULL}else{facet.categ[[i1]]}} -))) # dot.categ because can be a 3rd column of data1 -if(any(is.na(data1[[i1]][, column.check]))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -for(i3 in 1:length(column.check)){ -if(any(is.na(data1[[i1]][, column.check[i3]]))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i3], " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT"))) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -tempo <- unique(unlist(lapply(lapply(c(data1[[i1]][column.check]), FUN = is.na), FUN = which))) -removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], tempo) -removed.rows[[i1]] <- rbind(removed.rows[[i1]], data1.ini[[i1]][tempo, ]) # # tempo used because removed.row.nb is not empty. Here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance) -column.check <- column.check[ ! (column.check == x[[i1]] | column.check == y[[i1]])] # remove x and y to keep quali columns -if(length(tempo) != 0){ -data1[[i1]] <- data1[[i1]][-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former -data1.ini[[i1]] <- data1.ini[[i1]][-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former -for(i4 in 1:length(column.check)){ -if(any( ! unique(removed.rows[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]]))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i4], " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA REMOVAL\n(IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[[i1]][, column.check[i4]])[ ! unique(removed.rows[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]])], collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -tempo.levels <- levels(data1[[i1]][, column.check[i4]])[levels(data1[[i1]][, column.check[i4]]) %in% unique(as.character(data1[[i1]][, column.check[i4]]))] -data1[[i1]][, column.check[i4]] <- factor(as.character(data1[[i1]][, column.check[i4]]), levels = tempo.levels) -if(column.check[i4] %in% categ[[i1]] & ! is.null(categ.class.order)){ -categ.class.order[[i1]] <- levels(data1[[i1]][, column.check[i4]])[levels(data1[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]])] # remove the absent class in the categ.class.order vector -data1[[i1]][, column.check[i4]] <- factor(as.character(data1[[i1]][, column.check[i4]]), levels = unique(categ.class.order[[i1]])) -} -} -} -} -} -# end na detection and removal -# From here, data1 and data.ini have no more NA or NaN in x, y, categ (if categ != NULL) and facet.categ (if categ != NULL) -if( ! is.null(categ.class.order)){ -# the following check will be done several times but I prefer to keep it here, after the creation of categ -if(is.null(categ[[i1]]) & ! is.null(categ.class.order[[i1]])){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ ARGUMENT CANNOT BE NULL IF COMPARTMENT ", i1, " OF categ.class.order ARGUMENT IS NOT NULL: ", paste(categ.class.order, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -if(is.null(categ.class.order[[i1]])){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i1, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -data1[[i1]][, categ[[i1]]] <- factor(as.character(data1[[i1]][, categ[[i1]]])) # if already a factor, change nothing, if characters, levels according to alphabetical order -categ.class.order[[i1]] <- levels(data1[[i1]][, categ[[i1]]]) # character vector that will be used later -}else{ -tempo <- fun_check(data = categ.class.order[[i1]], data.name = paste0("COMPARTMENT ", i1 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[[i1]][, categ[[i1]]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(any(duplicated(categ.class.order[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i1]], collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if( ! (all(categ.class.order[[i1]] %in% unique(data1[[i1]][, categ[[i1]]])) & all(unique(data1[[i1]][, categ[[i1]]]) %in% categ.class.order[[i1]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ.class.order ARGUMENT MUST BE CLASSES OF COMPARTMENT ", i1, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i1]], collapse = " "), "\nFOR COMPARTMENT ", i1, " OF categ.class.order AND IT IS:\n", paste(unique(data1[[i1]][, categ[[i1]]]), collapse = " "), "\nFOR COLUMN ", categ[[i1]], " OF data1 NUMBER ", i1) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]], levels = categ.class.order[[i1]]) # reorder the factor -} -names(categ.class.order)[i1] <- categ[[i1]] -} -} -# OK: if categ.class.order is not NULL, all the NULL categ.class.order columns of data1 are character from here -if( ! is.null(legend.name[[i1]])){ -tempo <- fun_check(data = legend.name[[i1]], data.name = ifelse(length(legend.name)== 1L, "legend.name", paste0("legend.name NUMBER ", i1)),, class = "vector", mode = "character", length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if( ! is.null(color)){ # if color is NULL, will be filled later on -# check the nature of color -if(is.null(color[[i1]])){ -compart.null.color <- compart.null.color + 1 -color[[i1]] <- grey(compart.null.color / 8) # cannot be more than 7 overlays. Thus 7 different greys. 8/8 is excluded because white dots -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL COLOR IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " ASSOCIATED TO ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", SINGLE COLOR ", paste(color[[i1]], collapse = " "), " HAS BEEN ATTRIBUTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -tempo1 <- fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) # na.contain = TRUE in case of colum of data1 -tempo2 <- fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "factor", na.contain = TRUE, fun.name = function.name) # idem -if(tempo1$problem == TRUE & tempo2$problem == TRUE){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A FACTOR OR CHARACTER VECTOR OR INTEGER VECTOR") # integer possible because dealt above -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if( ! (all(color[[i1]] %in% colors() | grepl(pattern = "^#", color[[i1]])))){ # check that all strings of low.color start by # -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors(): ", paste(unique(color[[i1]]), collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(any(is.na(color[[i1]]))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), ", THE COLORS:\n", paste(unique(color[[i1]]), collapse = " "), "\nCONTAINS NA") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# end check the nature of color -# check the length of color -if(is.null(categ) & length(color[[i1]]) != 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A SINGLE COLOR IF categ IS NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if( ! is.null(categ)){ -# No problem of NA management by ggplot2 because already removed -if(categ[[i1]] == "fake_categ" & length(color[[i1]]) != 1){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A SINGLE COLOR IF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IS NULL") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(length(color[[i1]]) == length(unique(data1[[i1]][, categ[[i1]]]))){ # here length(color) is equal to the different number of categ -data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE FOLLOWING COLORS:\n", paste(color[[i1]], collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else if(length(color[[i1]]) == length(data1[[i1]][, categ[[i1]]])){# here length(color) is equal to nrow(data1[[i1]]) -> Modif to have length(color) equal to the different number of categ (length(color) == length(levels(data1[[i1]][, categ[[i1]]]))) -data1[[i1]] <- cbind(data1[[i1]], color = color[[i1]], stringsAsFactors = TRUE) -tempo.check <- unique(data1[[i1]][ , c(categ[[i1]], "color")]) -if( ! (nrow(data1[[i1]]) == length(color[[i1]]) & nrow(tempo.check) == length(unique(data1[[i1]][ , categ[[i1]]])))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color")), " ARGUMENT HAS THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF THIS categ:\n", paste(unique(mapply(FUN = "paste", data1[[i1]][ ,categ[[i1]]], data1[[i1]][ ,"color"])), collapse = "\n")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order -color[[i1]] <- unique(color[[i1]][order(data1[[i1]][, categ[[i1]]])]) # Modif to have length(color) equal to the different number of categ (length(color) == length(levels(data1[[i1]][, categ[[i1]]]))) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count, ") FROM FUNCTION ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " HAS THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " COLUMN VALUES\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ AS:\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " "), "\n", paste(color[[i1]], collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else if(length(color[[i1]])== 1L){ -data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order -color[[i1]] <- rep(color[[i1]], length(levels(data1[[i1]][, categ[[i1]]]))) -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", COLOR HAS LENGTH 1 MEANING THAT ALL THE DIFFERENT CLASSES OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), "\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(color[[i1]], collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE\n(1) LENGTH 1\nOR (2) THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " COLUMN VALUES\nOR (3) THE LENGTH OF THE CLASSES IN THIS COLUMN\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LENGTH ", length(data1[[i1]][, categ[[i1]]]), " AND CATEG CLASS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]])), "\nPRESENCE OF NA IN THE COLUMN x, y OR categ OF data1 COULD BE THE PROBLEME") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -} -if((geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline") & ! is.null(categ[[i1]])){ # add here after the color management, to deal with the different lines to plot inside any data[[i1]] -if(categ[[i1]] == "fake_categ"){ -data1[[i1]][, "fake_categ"] <- factor(paste0("Line_", formatC(1:nrow(data1[[i2]]), width = nchar(nrow(data1[[i2]])), flag = "0"))) -} -} -tempo <- fun_check(data = alpha[[i1]], data.name = ifelse(length(alpha)== 1L, "alpha", paste0("alpha NUMBER ", i1)), prop = TRUE, length = 1, fun.name = function.name) -if(tempo$problem == TRUE){ -stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -# end loop (checking inside list compartment) -if(length(data1) > 1){ -if(length(unique(unlist(x)[ ! x == "fake_x"])) > 1){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE x ARGUMENT DOES NOT CONTAIN IDENTICAL COLUMN NAMES:\n", paste(unlist(x), collapse = " "), "\nX-AXIS OVERLAYING DIFFERENT VARIABLES?") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(length(data1) > 1){ -if(length(unique(unlist(y)[ ! y == "fake_y"])) > 1){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE y ARGUMENT DOES NOT CONTAIN IDENTICAL COLUMN NAMES:\n", paste(unlist(y), collapse = " "), "\nY-AXIS OVERLAYING DIFFERENT VARIABLES?") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -if(sum(geom %in% "geom_point") > 3){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT HAVE MORE THAN THREE \"geom_point\" ELEMENTS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(length(geom) - sum(geom %in% "geom_point") > 3){ -tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT HAVE MORE THAN THREE LINE ELEMENTS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -# x.lim management before transfo by x.log -if(x.log != "no" & ! is.null(x.lim)){ -if(any(x.lim <= 0)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE x.log ARGUMENT SET TO ", x.log, ":\n", paste(x.lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(any( ! is.finite(if(x.log == "log10"){log10(x.lim)}else{log2(x.lim)}))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT RETURNS INF/NA WITH THE x.log ARGUMENT SET TO ", x.log, "\nAS SCALE COMPUTATION IS ", ifelse(x.log == "log10", "log10", "log2"), ":\n", paste(if(x.log == "log10"){log10(x.lim)}else{log2(x.lim)}, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(x.log != "no" & x.include.zero == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") x.log ARGUMENT SET TO ", x.log, " AND x.include.zero ARGUMENT SET TO TRUE -> x.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -x.include.zero <- FALSE -} -# end x.lim management before transfo by x.log -# y.lim management before transfo by y.log -if(y.log != "no" & ! is.null(y.lim)){ -if(any(y.lim <= 0)){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(y.log != "no" & y.include.zero == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -y.include.zero <- FALSE -} -# end y.lim management before transfo by y.log -# end other checkings -# reserved word checking -#already done above -# end reserved word checking -# end second round of checking and data preparation - - -# package checking -fun_pack(req.package = c( -"gridExtra", -"ggplot2", -"lemon", -"scales" -), lib.path = lib.path) -# packages Cairo and grid tested by fun_gg_point_rast() -# end package checking - - - - -# main code -# axes management -if(is.null(x.lim)){ -if(any(unlist(mapply(FUN = "[[", data1, x, SIMPLIFY = FALSE)) %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE x COLUMN IN data1 CONTAINS -Inf OR Inf VALUES THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -x.lim <- suppressWarnings(range(unlist(mapply(FUN = "[[", data1, x, SIMPLIFY = FALSE)), na.rm = TRUE, finite = TRUE)) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only. y.lim added here. If NULL, ok if y argument has values -}else if(x.log != "no"){ -x.lim <- get(x.log)(x.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -} -if(x.log != "no"){ -if(any( ! is.finite(x.lim))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE x.log ARGUMENT SET TO ", x.log, ":\n", paste(x.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(suppressWarnings(all(x.lim %in% c(Inf, -Inf)))){ # happen when x is only NULL -if(all(unlist(geom) %in% c("geom_vline", "geom_stick"))){ -tempo.cat <- paste0("ERROR IN ", function.name, " NOT POSSIBLE TO DRAW geom_vline OR geom_stick KIND OF LINES ALONE IF x.lim ARGUMENT IS SET TO NULL, SINCE NO X-AXIS DEFINED (", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT MUST BE NULL FOR THESE KIND OF LINES)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, " x.lim ARGUMENT MADE OF NA, -Inf OR Inf ONLY: ", paste(x.lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -x.lim.order <- order(x.lim) # to deal with inverse axis -# print(x.lim.order) -x.lim <- sort(x.lim) -x.lim[1] <- x.lim[1] - abs(x.lim[2] - x.lim[1]) * ifelse(diff(x.lim.order) > 0, x.right.extra.margin, x.left.extra.margin) # diff(x.lim.order) > 0 means not inversed axis -x.lim[2] <- x.lim[2] + abs(x.lim[2] - x.lim[1]) * ifelse(diff(x.lim.order) > 0, x.left.extra.margin, x.right.extra.margin) # diff(x.lim.order) > 0 means not inversed axis -if(x.include.zero == TRUE){ # no need to check x.log != "no" because done before -x.lim <- range(c(x.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -} -x.lim <- x.lim[x.lim.order] -if(any(is.na(x.lim))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(is.null(y.lim)){ -if(any(unlist(mapply(FUN = "[[", data1, y, SIMPLIFY = FALSE)) %in% c(Inf, -Inf))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE y COLUMN IN data1 CONTAINS -Inf OR Inf VALUES THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -y.lim <- suppressWarnings(range(unlist(mapply(FUN = "[[", data1, y, SIMPLIFY = FALSE)), na.rm = TRUE, finite = TRUE)) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only. y.lim added here. If NULL, ok if y argument has values -}else if(y.log != "no"){ -y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope -} -if(y.log != "no"){ -if(any( ! is.finite(y.lim))){ -tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # happen when y is only NULL -if(all(unlist(geom) == "geom_vline")){ -tempo.cat <- paste0("ERROR IN ", function.name, " NOT POSSIBLE TO DRAW geom_vline KIND OF LINES ALONE IF y.lim ARGUMENT IS SET TO NULL, SINCE NO Y-AXIS DEFINED (", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT MUST BE NULL FOR THESE KIND OF LINES)") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, " y.lim ARGUMENT MADE OF NA, -Inf OR Inf ONLY: ", paste(y.lim, collapse = " ")) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} -y.lim.order <- order(y.lim) # to deal with inverse axis -y.lim <- sort(y.lim) -y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 means not inversed axis -y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 means not inversed axis -if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before -y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only -} -y.lim <- y.lim[y.lim.order] -if(any(is.na(y.lim))){ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) +fun_open <- function( + pdf = TRUE, + pdf.path = "working.dir", + pdf.name = "graph", + width = 7, + height = 7, + paper = "special", + pdf.overwrite = FALSE, + rescale = "fixed", + remove.read.only = TRUE, + return.output = FALSE +){ + # AIM + # open a pdf or screen (GUI) graphic window and return initial graphic parameters + # this order can be used: + # fun_width() + # fun_open() + # fun_prior_plot() # not for ggplot2 + # plot() or any other plotting + # fun_post_plot() if fun_prior_plot() has been used # not for ggplot2 + # fun_close() + # WARNINGS + # On Linux, use pdf = TRUE, if (GUI) graphic window is not always available, meaning that X is not installed (clusters for instance). Use X11() in R to test if available + # ARGUMENTS: + # pdf: logical. Use pdf display? If FALSE, a GUI is opened + # pdf.path: where the pdf is saved (do not terminate by / or \\). Write "working.dir" if working directory is required (default). Ignored if pdf == FALSE + # pdf.name: name of the pdf file containing the graphs (the .pdf extension is added by the function, if not detected in the name end). Ignored if pdf == FALSE + # width: width of the window (in inches) + # height: height of the window (in inches) + # paper: paper argument of the pdf function (paper format). Only used for pdf(). Either "a4", "letter", "legal", "us", "executive", "a4r", "USr" or "special". If "special", means that the paper dimension will be width and height. With another paper format, if width or height is over the size of the paper, width or height will be modified such that the plot is adjusted to the paper dimension (see $dim in the returned list below to see the modified dimensions). Ignored if pdf == FALSE + # pdf.overwrite: logical. Existing pdf can be overwritten? . Ignored if pdf == FALSE + # rescale: kind of GUI. Either "R", "fit", or "fixed". Ignored on Mac and Linux OS. See ?windows for details + # remove.read.only: logical. remove the read only (R.O.) graphical parameters? If TRUE, the graphical parameters are returned without the R.O. parameters. The returned $ini.par list can be used to set the par() of a new graphical device. If FALSE, graphical parameters are returned with the R.O. parameters, which provides information like text dimension (see ?par() ). The returned $ini.par list can be used to set the par() of a new graphical device, but generate a warning message. Ignored if return.output == FALSE. + # return.output: logical. Return output ? If TRUE the output list is displayed + # RETURN + # a list containing: + # $pdf.loc: path of the pdf created + # $ini.par: initial par() parameters + # $zone.ini: initial window spliting + # $dim: dimension of the graphical device (in inches) + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_open(pdf = FALSE, pdf.path = "C:/Users/Gael/Desktop", pdf.name = "graph", width = 7, height = 7, paper = "special", pdf.overwrite = FALSE, return.output = TRUE) + # DEBUGGING + # pdf = TRUE ; pdf.path = "C:/Users/Gael/Desktop" ; pdf.name = "graphs" ; width = 7 ; height = 7 ; paper = "special" ; pdf.overwrite = FALSE ; rescale = "fixed" ; remove.read.only = TRUE ; return.output = TRUE # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = pdf, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = pdf.path, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = pdf.name, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = height, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = paper, options = c("a4", "letter", "legal", "us", "executive", "a4r", "USr", "special", "A4", "LETTER", "LEGAL", "US"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data =pdf.overwrite, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = rescale, options = c("R", "fit", "fixed"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = remove.read.only, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.output, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if(pdf.path == "working.dir"){ + pdf.path <- getwd() + }else{ + if(grepl(x = pdf.path, pattern = ".+/$")){ + pdf.path <- sub(x = pdf.path, pattern = "/$", replacement = "") # remove the last / + }else if(grepl(x = pdf.path, pattern = ".+[\\]$")){ # or ".+\\\\$" # cannot be ".+\$" because \$ does not exist contrary to \n + pdf.path <- sub(x = pdf.path, pattern = "[\\]$", replacement = "") # remove the last / + } + if(dir.exists(pdf.path) == FALSE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\npdf.path ARGUMENT DOES NOT CORRESPOND TO EXISTING DIRECTORY\n", pdf.path) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # par.ini recovery + # cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt + if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows + open.fail <- NULL + grDevices::windows() + ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + }else if(Sys.info()["sysname"] == "Linux"){ + if(pdf == TRUE){# cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt + if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment + tempo.random.seed <- .Random.seed + on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) + }else{ + on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness + } + set.seed(NULL) + tempo.code <- sample(x = 1:1e7, size = 1) + while(file.exists(paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf")) == TRUE){ + tempo.code <- tempo.code + 1 + } + grDevices::pdf(width = width, height = height, file=paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf"), paper = paper) + ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the pdf window + file.remove(paste0(pdf.path, "/recover_ini_par", tempo.code, ".pdf")) # remove the pdf file + }else{ + # test if X11 can be opened + if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHIS FUNCTION CANNOT BE USED ON LINUX IF A Rplots.pdf FILE ALREADY EXISTS HERE\n", getwd()) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + open.fail <- suppressWarnings(try(grDevices::X11(), silent = TRUE))[] # try to open a X11 window. If open.fail == NULL, no problem, meaning that the X11 window is opened. If open.fail != NULL, a pdf can be opened here paste0(getwd(), "/Rplots.pdf") + if(is.null(open.fail)){ + ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + }else if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ + file.remove(paste0(getwd(), "/Rplots.pdf")) # remove the pdf file + tempo.cat <- ("ERROR IN fun_open()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, EITHER SET THE X GRAPHIC INTERFACE OF THE SYSTEM OR SET THE pdf ARGUMENT OF THE fun_open() FUNCTION TO TRUE AND RERUN") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + } + }else{ + open.fail <- NULL + grDevices::quartz() + ini.par <- par(no.readonly = remove.read.only) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + } + # end par.ini recovery + zone.ini <- matrix(1, ncol=1) # to recover the initial parameters for next figure region when device region split into several figure regions + if(pdf == TRUE){ + if(grepl(x = pdf.name, pattern = "\\.pdf$")){ + pdf.name <- sub(x = pdf.name, pattern = "\\.pdf$", replacement = "") # remove the last .pdf + } + pdf.loc <- paste0(pdf.path, "/", pdf.name, ".pdf") + if(file.exists(pdf.loc) == TRUE & pdf.overwrite == FALSE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\npdf.loc FILE ALREADY EXISTS AND CANNOT BE OVERWRITTEN DUE TO pdf.overwrite ARGUMENT SET TO TRUE\n", pdf.loc) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + grDevices::pdf(width = width, height = height, file=pdf.loc, paper = paper) + } + }else if(pdf == FALSE){ + pdf.loc <- NULL + if(Sys.info()["sysname"] == "Windows"){ # .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows + grDevices::windows(width = width, height = height, rescale = rescale) + }else if(Sys.info()["sysname"] == "Linux"){ + if( ! is.null(open.fail)){ + tempo.cat <- "ERROR IN fun_open()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, EITHER SET THE X GRAPHIC INTERFACE OF THE SYSTEM OR SET THE pdf ARGUMENT OF THE fun_open() FUNCTION TO TRUE AND RERUN" + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + grDevices::X11(width = width, height = height) + } + }else{ + grDevices::quartz(width = width, height = height) + } + } + if(return.output == TRUE){ + output <- list(pdf.loc = pdf.loc, ini.par = ini.par, zone.ini = zone.ini, dim = dev.size()) + return(output) + } } -# end axes management +######## fun_prior_plot() #### set graph param before plotting (erase axes for instance) -# create a fake categ if NULL to deal with legend display -if(is.null(categ)){ -categ <- vector("list", length(data1)) -categ[] <- "fake_categ" -for(i2 in 1:length(data1)){ -data1[[i2]] <- cbind(data1[[i2]], fake_categ = "", stringsAsFactors = TRUE) -if(geom[[i2]] == "geom_hline" | geom[[i2]] == "geom_vline"){ -data1[[i2]][, "fake_categ"] <- factor(paste0("Line_", 1:nrow(data1[[i2]]))) -} -} -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL categ ARGUMENT -> FAKE \"fake_categ\" COLUMN ADDED TO EACH DATA FRAME OF data1, AND FILLED WITH \"\"") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -# categ is not NULL anymore -if(is.null(categ.class.order)){ -categ.class.order <- vector("list", length = length(data1)) -tempo.categ.class.order <- NULL -for(i2 in 1:length(categ.class.order)){ -categ.class.order[[i2]] <- levels(data1[[i2]][, categ[[i2]]]) -names(categ.class.order)[i2] <- categ[[i2]] -tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]]) -} -if(any(unlist(legend.disp))){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR CLASS ORDERING:\n", paste(tempo.categ.class.order, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end create a fake categ if NULL to deal with legend display -# categ.class.order is not NULL anymore - - -# vector of color with length as in levels(categ) of data1 -if(is.null(color)){ -color <- vector("list", length(data1)) -length.categ.list <- lapply(lapply(mapply(FUN = "[[", data1, categ, SIMPLIFY = FALSE), FUN = unique), FUN = function(x){length(x[ ! is.na(x)])}) -length.categ.list[sapply(categ, FUN = "==", "fake_categ")] <- 1 # when is.null(color), a single color for all the dots or lines of data[[i1]] that contain "fake_categ" category -total.categ.length <- sum(unlist(length.categ.list), na.rm = TRUE) -tempo.color <- fun_gg_palette(total.categ.length) -tempo.count <- 0 -for(i2 in 1:length(data1)){ -color[[i2]] <- tempo.color[(1:length.categ.list[[i2]]) + tempo.count] -tempo.count <- tempo.count + length.categ.list[[i2]] -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") NULL color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i2, " OF categ ARGUMENT")), " (", categ[[i2]], ") IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i2, " OF data1 ARGUMENT")), ":\n", paste(color[[i2]], collapse = " "), "\n", paste(if(all(levels(data1[[i2]][, categ[[i2]]]) == "")){'\"\"'}else{levels(data1[[i2]][, categ[[i2]]])}, collapse = " ")) -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} +fun_prior_plot <- function( + param.reinitial = FALSE, + xlog.scale = FALSE, + ylog.scale = FALSE, + remove.label = TRUE, + remove.x.axis = TRUE, + remove.y.axis = TRUE, + std.x.range = TRUE, + std.y.range = TRUE, + down.space = 1, + left.space = 1, + up.space = 1, + right.space = 1, + orient = 1, + dist.legend = 3.5, + tick.length = 0.5, + box.type = "n", + amplif.label = 1, + amplif.axis = 1, + display.extend = FALSE, + return.par = FALSE +){ + # AIM + # very convenient to erase the axes for post plot axis redrawing using fun_post_plot() + # reinitialize and set the graphic parameters before plotting + # CANNOT be used if no graphic device already opened + # ARGUMENTS + # param.reinitial: reinitialize graphic parameters before applying the new ones, as defined by the other arguments? Either TRUE or FALSE + # xlog.scale: Log scale for the x-axis? Either TRUE or FALSE. If TRUE, erases the x-axis, except legend, for further drawing by fun_post_plot()(xlog argument of par()) + # ylog.scale: Log scale for the y-axis? Either TRUE or FALSE. If TRUE, erases the y-axis, except legend, for further drawing by fun_post_plot()(ylog argument of par()) + # remove.label: remove labels (axis legend) of the two axes? Either TRUE or FALSE (ann argument of par()) + # remove.x.axis: remove x-axis except legend? Either TRUE or FALSE (control the xaxt argument of par()). Automately set to TRUE if xlog.scale == TRUE + # remove.y.axis: remove y-axis except legend? Either TRUE or FALSE (control the yaxt argument of par()). Automately set to TRUE if ylog.scale == TRUE + # std.x.range: standard range on the x-axis? TRUE (no range extend) or FALSE (4% range extend). Controls xaxs argument of par() (TRUE is xaxs = "i", FALSE is xaxs = "r") + # std.y.range: standard range on the y-axis? TRUE (no range extend) or FALSE (4% range extend). Controls yaxs argument of par() (TRUE is yaxs = "i", FALSE is yaxs = "r") + # down.space: lower vertical margin (in inches, mai argument of par()) + # left.space: left horizontal margin (in inches, mai argument of par()) + # up.space: upper vertical margin between plot region and grapical window (in inches, mai argument of par()) + # right.space: right horizontal margin (in inches, mai argument of par()) + # orient: scale number orientation (las argument of par()). 0, always parallel to the axis; 1, always horizontal; 2, always perpendicular to the axis; 3, always vertical + # dist.legend: numeric value that moves axis legends away in inches (first number of mgp argument of par() but in inches thus / 0.2) + # tick.length: length of the ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc. 0 means no tick + # box.type: bty argument of par(). Either "o", "l", "7", "c", "u", "]", the resulting box resembles the corresponding upper case letter. A value of "n" suppresses the box + # amplif.label: increase or decrease the size of the text in legends + # amplif.axis: increase or decrease the size of the scale numbers in axis + # display.extend: extend display beyond plotting region? Either TRUE or FALSE (xpd argument of par() without NA) + # return.par: return graphic parameter modification? + # RETURN + # return graphic parameter modification + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_prior_plot(param.reinitial = FALSE, xlog.scale = FALSE, ylog.scale = FALSE, remove.label = TRUE, remove.x.axis = TRUE, remove.y.axis = TRUE, std.x.range = TRUE, std.y.range = TRUE, down.space = 1, left.space = 1, up.space = 1, right.space = 1, orient = 1, dist.legend = 4.5, tick.length = 0.5, box.type = "n", amplif.label = 1, amplif.axis = 1, display.extend = FALSE, return.par = FALSE) + # DEBUGGING + # param.reinitial = FALSE ; xlog.scale = FALSE ; ylog.scale = FALSE ; remove.label = TRUE ; remove.x.axis = TRUE ; remove.y.axis = TRUE ; std.x.range = TRUE ; std.y.range = TRUE ; down.space = 1 ; left.space = 1 ; up.space = 1 ; right.space = 1 ; orient = 1 ; dist.legend = 4.5 ; tick.length = 0.5 ; box.type = "n" ; amplif.label = 1 ; amplif.axis = 1 ; display.extend = FALSE ; return.par = FALSE # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = param.reinitial, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = xlog.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = ylog.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = remove.label, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = remove.x.axis, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = remove.y.axis, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = std.x.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = std.y.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = down.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = up.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = orient, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.type, options = c("o", "l", "7", "c", "u", "]", "n"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = amplif.label, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = amplif.axis, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = display.extend, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.par, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if(is.null(dev.list())){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THIS FUNCTION CANNOT BE USED IF NO GRAPHIC DEVICE ALREADY OPENED (dev.list() IS CURRENTLY NULL)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # par.ini recovery + # cannot use pdf(file = NULL), because some small differences between pdf() and other devices. For instance, differences with windows() for par()$fin, par()$pin and par()$plt + if(param.reinitial == TRUE){ + if( ! all(names(dev.cur()) == "null device")){ + active.wind.nb <- dev.cur() + }else{ + active.wind.nb <- 0 + } + if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows + grDevices::windows() + ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + }else if(Sys.info()["sysname"] == "Linux"){ + if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THIS FUNCTION CANNOT BE USED ON LINUX WITH param.reinitial SET TO TRUE IF A Rplots.pdf FILE ALREADY EXISTS HERE: ", getwd()) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + open.fail <- suppressWarnings(try(grDevices::X11(), silent = TRUE))[] # try to open a X11 window. If open.fail == NULL, no problem, meaning that the X11 window is opened. If open.fail != NULL, a pdf can be opened here paste0(getwd(), "/Rplots.pdf") + if(is.null(open.fail)){ + ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + }else if(file.exists(paste0(getwd(), "/Rplots.pdf"))){ + ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + invisible(dev.off()) # close the new window + file.remove(paste0(getwd(), "/Rplots.pdf")) # remove the pdf file + }else{ + tempo.cat <- ("ERROR IN fun_prior_plot()\nTHIS FUNCTION CANNOT OPEN GUI ON LINUX OR NON MACOS UNIX SYSTEM\nTO OVERCOME THIS, PLEASE USE A PDF GRAPHIC INTERFACE AND RERUN") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + }else{ # macOS + grDevices::quartz() + ini.par <- par(no.readonly = FALSE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened) + invisible(dev.off()) # close the new window + } + if( ! all(names(dev.cur()) == "null device")){ + invisible(dev.set(active.wind.nb)) # go back to the active window if exists + par(ini.par) # apply the initial par to current window + } + } + # end par.ini recovery + if(remove.x.axis == TRUE){ + par(xaxt = "n") # suppress the y-axis label + }else{ + par(xaxt = "s") + } + if(remove.y.axis == TRUE){ + par(yaxt = "n") # suppress the y-axis label + }else{ + par(yaxt = "s") + } + if(std.x.range == TRUE){ + par(xaxs = "i") + }else{ + par(xaxs = "r") + } + if(std.y.range == TRUE){ + par(yaxs = "i") + }else{ + par(yaxs = "r") + } + par(mai = c(down.space, left.space, up.space, right.space), ann = ! remove.label, las = orient, mgp = c(dist.legend/0.2, 1, 0), xpd = display.extend, bty= box.type, cex.lab = amplif.label, cex.axis = amplif.axis) + par(tcl = -par()$mgp[2] * tick.length) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) + if(xlog.scale == TRUE){ + par(xaxt = "n", xlog = TRUE) # suppress the x-axis label + }else{ + par(xlog = FALSE) + } + if(ylog.scale == TRUE){ + par(yaxt = "n", ylog = TRUE) # suppress the y-axis label + }else{ + par(ylog = FALSE) + } + if(return.par == TRUE){ + tempo.par <- par() + return(tempo.par) + } } -# end vector of color with length as in levels(categ) of data1 -# color is not NULL anymore + + +######## fun_scale() #### select nice label numbers when setting number of ticks on an axis -# last check -for(i1 in 1:length(data1)){ -if(categ[[i1]] != "fake_categ" & length(color[[i1]]) != length(unique(data1[[i1]][, categ[[i1]]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, " LAST CHECK: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE THE LENGTH OF LEVELS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LEVELS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]])), "\nREMINDER: A SINGLE COLOR PER CLASS OF CATEG AND A SINGLE CLASS OF CATEG PER COLOR MUST BE RESPECTED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else if(categ[[i1]] == "fake_categ" & length(color[[i1]]) != 1){ -tempo.cat <- paste0("ERROR IN ", function.name, " LAST CHECK: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE LENGTH 1 WHEN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IS NULL\nHERE IT IS COLOR LENGTH ", length(color[[i1]])) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) +fun_scale <- function(n, lim, kind = "approx", lib.path = NULL){ + # AIM + # attempt to select nice scale numbers when setting n ticks on a lim axis range + # ARGUMENTS + # n: desired number of main ticks on the axis (integer above 0) + # lim: vector of 2 numbers indicating the limit range of the axis. Order of the 2 values matters (for inverted axis). Can be log transformed values + # kind: either "approx" (approximative), "strict" (strict) or "strict.cl" (strict clean). If "approx", use the scales::trans_breaks() function to provide an easy to read scale of approximately n ticks spanning the range of the lim argument. If "strict", cut the range of the lim argument into n + 1 equidistant part and return the n numbers at each boundary. This often generates numbers uneasy to read. If "strict.cl", provide an easy to read scale of exactly n ticks, but sometimes not completely spanning the range of the lim argument + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # RETURN + # a vector of numbers + # REQUIRED PACKAGES + # if kind = "approx": + # ggplot2 + # scales + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_round() + # EXAMPLES + # approximate number of main ticks + # ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "approx") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) + # strict number of main ticks + # ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "strict") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) + # strict "clean" number of main ticks + # ymin = 2 ; ymax = 3.101 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "strict.cl") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) + # approximate number of main ticks, scale inversion + # ymin = 3.101 ; ymax = 2 ; n = 5 ; scale <- fun_scale(n = n, lim = c(ymin, ymax), kind = "approx") ; scale ; par(yaxt = "n", yaxs = "i", las = 1) ; plot(ymin:ymax, ymin:ymax, xlim = range(scale, ymin, ymax)[order(c(ymin, ymax))], ylim = range(scale, ymin, ymax)[order(c(ymin, ymax))], xlab = "DEFAULT SCALE", ylab = "NEW SCALE") ; par(yaxt = "s") ; axis(side = 2, at = scale) + # DEBUGGING + # n = 9 ; lim = c(2, 3.101) ; kind = "approx" ; lib.path = NULL # for function debugging + # n = 10 ; lim = c(1e-4, 1e6) ; kind = "approx" ; lib.path = NULL # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # end initial argument checking + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_round", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_round() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & isTRUE(all.equal(n, 0))){ # isTRUE(all.equal(n, 0)) equivalent to n == 0 but deals with floats (approx ok) + tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A NON NULL AND POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) # + } + tempo <- fun_check(data = lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & all(diff(lim) == 0L)){ # isTRUE(all.equal(diff(lim), rep(0, length(diff(lim))))) not used because we strictly need zero as a result + tempo.cat <- paste0("ERROR IN ", function.name, ": lim ARGUMENT HAS A NULL RANGE (2 IDENTICAL VALUES)") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & any(lim %in% c(Inf, -Inf))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = kind, options = c("approx", "strict", "strict.cl"), length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + lim.rank <- rank(lim) # to deal with inverted axis + lim <- sort(lim) + if(kind == "approx"){ + # package checking + fun_pack(req.package = c("ggplot2"), lib.path = lib.path) + fun_pack(req.package = c("scales"), lib.path = lib.path) + # end package checking + output <- ggplot2::ggplot_build(ggplot2::ggplot() + ggplot2::scale_y_continuous( + breaks = scales::trans_breaks( + trans = "identity", + inv = "identity", + n = n + ), + limits = lim + ))$layout$panel_params[[1]]$y$breaks # pretty() alone is not appropriate: tempo.pret <- pretty(seq(lim[1] ,lim[2], length.out = n)) ; tempo.pret[tempo.pret > = lim[1] & tempo.pret < = lim[2]]. # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks + if( ! is.null(attributes(output))){ # layout$panel_params[[1]]$y$breaks can be characters (labels of the axis). In that case, it has attributes that corresponds to positions + output <- unlist(attributes(output)) + } + output <- output[ ! is.na(output)] + }else if(kind == "strict"){ + output <- fun_round(seq(lim[1] ,lim[2], length.out = n), 2) + }else if(kind == "strict.cl"){ + tempo.range <- diff(sort(lim)) + tempo.max <- max(lim) + tempo.min <- min(lim) + mid <- tempo.min + (tempo.range/2) # middle of axis + tempo.inter <- tempo.range / (n + 1) # current interval between two ticks, between 0 and Inf + if(tempo.inter == 0L){ # isTRUE(all.equal(tempo.inter, rep(0, length(tempo.inter)))) not used because we strictly need zero as a result + tempo.cat <- paste0("ERROR IN ", function.name, ": THE INTERVAL BETWEEN TWO TICKS OF THE SCALE IS NULL. MODIFY THE lim OR n ARGUMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + log10.abs.lim <- 200 + log10.range <- (-log10.abs.lim):log10.abs.lim + log10.vec <- 10^log10.range + round.vec <- c(5, 4, 3, 2.5, 2, 1.25, 1) + dec.table <- outer(log10.vec, round.vec) # table containing the scale units (row: power of ten from -201 to +199, column: the 5, 2.5, 2, 1.25, 1 notches + + + + # recover the number of leading zeros in tempo.inter + ini.scipen <- options()$scipen + options(scipen = -1000) # force scientific format + if(any(grepl(pattern = "\\+", x = tempo.inter))){ # tempo.inter > 1 + power10.exp <- as.integer(substring(text = tempo.inter, first = (regexpr(pattern = "\\+", text = tempo.inter) + 1))) # recover the power of 10. Example recover 08 from 1e+08 + mantisse <- as.numeric(substr(x = tempo.inter, start = 1, stop = (regexpr(pattern = "\\+", text = tempo.inter) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 + }else if(any(grepl(pattern = "\\-", x = tempo.inter))){ # tempo.inter < 1 + power10.exp <- as.integer(substring(text = tempo.inter, first = (regexpr(pattern = "\\-", text = tempo.inter)))) # recover the power of 10. Example recover 08 from 1e+08 + mantisse <- as.numeric(substr(x = tempo.inter, start = 1, stop = (regexpr(pattern = "\\-", text = tempo.inter) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + tempo.scale <- dec.table[log10.range == power10.exp, ] + # new interval + inter.select <- NULL + for(i1 in 1:length(tempo.scale)){ + tempo.first.tick <- trunc((tempo.min + tempo.scale[i1]) / tempo.scale[i1]) * (tempo.scale[i1]) # this would be use to have a number not multiple of tempo.scale[i1]: ceiling(tempo.min) + tempo.scale[i1] * 10^power10.exp + tempo.last.tick <- tempo.first.tick + tempo.scale[i1] * (n - 1) + if((tempo.first.tick >= tempo.min) & (tempo.last.tick <= tempo.max)){ + inter.select <- tempo.scale[i1] + break() + } + } + if(is.null(inter.select)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + options(scipen = ini.scipen) # restore the initial scientific penalty + # end new interval + # centering the new scale + tempo.mid <- trunc((mid + (-1:1) * inter.select) / inter.select) * inter.select # tempo middle tick closest to the middle axis + mid.tick <- tempo.mid[which.min(abs(tempo.mid - mid))] + if(isTRUE(all.equal(n, rep(1, length(n))))){ # isTRUE(all.equal(n, rep(1, length(n)))) is similar to n == 1L but deals with float + output <- mid.tick + }else if(isTRUE(all.equal(n, rep(2, length(n))))){ # isTRUE(all.equal(n, rep(0, length(n)))) is similar to n == 2L but deals with float + output <- mid.tick + tempo.min.dist <- mid.tick - inter.select - tempo.min + tempo.max.dist <- tempo.max - mid.tick + inter.select + if(tempo.min.dist <= tempo.max.dist){ # distance between lowest tick and bottom axis <= distance between highest tick and top axis. If yes, extra tick but at the top, otherwise at the bottom + output <- c(mid.tick, mid.tick + inter.select) + }else{ + output <- c(mid.tick - inter.select, mid.tick) + } + }else if((n / 2 - trunc(n / 2)) > 0.1){ # > 0.1 to avoid floating point. Because result can only be 0 or 0.5. Thus, > 0.1 means odd number + output <- c(mid.tick - (trunc(n / 2):1) * inter.select, mid.tick, mid.tick + (1:trunc(n / 2)) * inter.select) + }else if((n / 2 - trunc(n / 2)) < 0.1){ # < 0.1 to avoid floating point. Because result can only be 0 or 0.5. Thus, < 0.1 means even number + tempo.min.dist <- mid.tick - trunc(n / 2) * inter.select - tempo.min + tempo.max.dist <- tempo.max - mid.tick + trunc(n / 2) * inter.select + if(tempo.min.dist <= tempo.max.dist){ # distance between lowest tick and bottom axis <= distance between highest tick and top axis. If yes, extra tick but at the bottom, otherwise at the top + output <- c(mid.tick - ((trunc(n / 2) - 1):1) * inter.select, mid.tick, mid.tick + (1:trunc(n / 2)) * inter.select) + }else{ + output <- c(mid.tick - (trunc(n / 2):1) * inter.select, mid.tick, mid.tick + (1:(trunc(n / 2) - 1)) * inter.select) + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end centering the new scale + # last check + if(min(output) < tempo.min){ + output <- c(output[-1], max(output) + inter.select) # remove the lowest tick and add a tick at the top + }else if( max(output) > tempo.max){ + output <- c(min(output) - inter.select, output[-length(output)]) + } + if(min(output) < tempo.min | max(output) > tempo.max){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(is.na(output))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5 (NA GENERATION)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end last check + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 6") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(diff(lim.rank) < 0){ + output <- rev(output) + } + return(output) } + + +######## fun_inter_ticks() #### define coordinates of secondary ticks + + +fun_inter_ticks <- function( + lim, + log = "log10", + breaks = NULL, + n = NULL, + warn.print = TRUE +){ + # AIM + # define coordinates and values of secondary ticks + # ARGUMENTS + # lim: vector of 2 numbers indicating the limit range of the axis. Order of the 2 values matters (for inverted axis). If log argument is "log2" or "log10", values in lim must be already log transformed. Thus, negative or zero values are allowed + # log: either "log2" (values in the lim argument are log2 transformed) or "log10" (values in the lim argument are log10 transformed), or "no" + # breaks: mandatory vector of numbers indicating the main ticks values/positions when log argument is "no". Ignored when log argument is "log2" or "log10" + # n: number of secondary ticks between each main tick when log argument is "no". Ignored when log argument is "log2" or "log10" + # warn.print: logical. Print potential warning messages at the end of the execution? If FALSE, warning messages are never printed, but can still be recovered in the returned list + # RETURN + # a list containing + # $log: value of the log argument used + # $coordinates: the coordinates of the secondary ticks on the axis, between the lim values + # $values: the corresponding values associated to each coordinate (with log scale, 2^$values or 10^$values is equivalent to the labels of the axis) + # $warn: the potential warning messages. Use cat() for proper display. NULL if no warning + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # no log scale + # fun_inter_ticks(lim = c(-4,4), log = "no", breaks = c(-2, 0, 2), n = 3) + # fun_inter_ticks(lim = c(10, 0), log = "no", breaks = c(10, 8, 6, 4, 2, 0), n = 4) + # log2 + # fun_inter_ticks(lim = c(-4,4), log = "log2") + # log10 + # fun_inter_ticks(lim = c(-2,3), log = "log10") + # DEBUGGING + # lim = c(2, 3.101) ; log = "no" ; breaks = NULL ; n = NULL ; warn.print = TRUE # for function debugging + # lim = c(0, 26.5) ; log = "no" ; breaks = c(0, 10, 20) ; n = 3 # for function debugging + # lim = c(10, 0); log = "no"; breaks = c(10, 8, 6, 4, 2, 0); n = 4 # for function debugging + # lim = c(-10, -20); log = "no"; breaks = c(-20, -15, -10); n = 4 # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + req.function <- c( + "fun_check" + ) + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED ", i1, "() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end required function checking + # argument primary checking + # arg with no default values + mandat.args <- c( + "lim" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # using fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(breaks)){ + tempo <- fun_check(data = breaks, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) + } + if( ! is.null(n)){ + tempo <- fun_check(data = n, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = warn.print, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA + if(any(is.na(lim)) | any(is.na(log)) | any(is.na(breaks)) | any(is.na(n)) | any(is.na(warn.print))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nNO ARGUMENT CAN HAVE NA VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA + # management of NULL + if(is.null(lim) | is.null(log)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHESE ARGUMENTS\nlim\nlog\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL + if(all(diff(lim) == 0L)){ # isTRUE(all.equal(diff(lim), rep(0, length(diff(lim))))) not used because we strictly need zero as a result + tempo.cat <- paste0("ERROR IN ", function.name, "\nlim ARGUMENT HAS A NULL RANGE (2 IDENTICAL VALUES): ", paste(lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(any(lim %in% c(Inf, -Inf))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nlim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(log == "no" & is.null(breaks)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT CANNOT BE NULL IF log ARGUMENT IS \"no\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(breaks)){ + if(length(breaks) < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT MUST HAVE 2 VALUES AT LEAST (OTHERWISE, INTER TICK POSITIONS CANNOT BE COMPUTED): ", paste(breaks, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! isTRUE(all.equal(diff(sort(breaks)), rep(diff(sort(breaks))[1], length(diff(sort(breaks))))))){ # isTRUE(all.equal(n, 0)) equivalent to n == 0 but deals with floats (approx ok) + tempo.cat <- paste0("ERROR IN ", function.name, "\nbreaks ARGUMENT MUST HAVE EQUIDISTANT VALUES (OTHERWISE, EQUAL NUMBER OF INTER TICK BETWEEN MAIN TICKS CANNOT BE COMPUTED): ", paste(breaks, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if( ! is.null(n)){ + if(n <= 0){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nn ARGUMENT MUST BE A POSITIVE AND NON NULL INTEGER: ", paste(n, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end second round of checking and data preparation + # main code + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + lim.rank <- rank(lim) # to deal with inverse axis + if(log != "no"){ + ini.scipen <- options()$scipen + options(scipen = -1000) # force scientific format + power10.exp <- as.integer(substring(text = 10^lim, first = (regexpr(pattern = "\\+|\\-", text = 10^lim)))) # recover the power of 10, i.e., integer part of lim. Example recover 08 from 1e+08. Works for log2 + # mantisse <- as.numeric(substr(x = 10^lim, start = 1, stop = (regexpr(pattern = "\\+|\\-", text = 10^lim) - 2))) # recover the mantisse. Example recover 1.22 from 1.22e+08 + options(scipen = ini.scipen) # restore the initial scientific penalty + tick.pos <- unique(as.vector(outer(2:10, ifelse(log == "log2", 2, 10)^((power10.exp[1] - ifelse(diff(lim.rank) > 0, 1, -1)):(power10.exp[2] + ifelse(diff(lim.rank) > 0, 1, -1)))))) # use log10(2:10) even if log2: it is to get log values between 0 and 1 + tick.pos <- sort(tick.pos, decreasing = ifelse(diff(lim.rank) > 0, FALSE, TRUE)) + if(log == "log2"){ + tick.values <- tick.pos[tick.pos >= min(2^lim) & tick.pos <= max(2^lim)] + tick.pos <- log2(tick.values) + }else if(log == "log10"){ + tick.values <- tick.pos[tick.pos >= min(10^lim) & tick.pos <= max(10^lim)] + tick.pos <- log10(tick.values) + } + }else{ + # if(length(breaks) > 1){ # not required because already checked above + breaks.rank <- rank(c(breaks[1], breaks[length(breaks)])) + if(diff(breaks.rank) != diff(lim.rank)){ + breaks <- sort(breaks, decreasing = ifelse(diff(lim.rank) < 0, TRUE, FALSE)) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") VALUES IN breaks ARGUMENT NOT IN THE SAME ORDER AS IN lim ARGUMENT -> VALUES REORDERED AS IN lim: ", paste(breaks, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + breaks.rank <- rank(c(breaks[1], breaks[length(breaks)])) + } + # } + main.tick.dist <- mean(diff(breaks), na.rm = TRUE) + tick.dist <- main.tick.dist / (n + 1) + tempo.extra.margin <- max(abs(diff(breaks)), na.rm = TRUE) + tick.pos <- seq( + if(diff(breaks.rank) > 0){breaks[1] - tempo.extra.margin}else{breaks[1] + tempo.extra.margin}, + if(diff(breaks.rank) > 0){breaks[length(breaks)] + tempo.extra.margin}else{breaks[length(breaks)] - tempo.extra.margin}, + by = tick.dist + ) + tick.pos <- tick.pos[tick.pos >= min(lim) & tick.pos <= max(lim)] + tick.values <- tick.pos + } + if(any(is.na(tick.pos) | ! is.finite(tick.pos))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": NA or Inf GENERATED FOR THE INTER TICK POSITIONS: ", paste(tick.pos, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(tick.pos) == 0L){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO INTER TICKS COMPUTED BETWEEN THE LIMITS INDICATED: ", paste(lim, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + output <- list(log = log, coordinates = tick.pos, values = tick.values, warn = warn) + if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) # to recover the warning messages, see $warn + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + return(output) } -# end last check +######## fun_post_plot() #### set graph param after plotting (axes redesign for instance) -# conversion of geom_hline and geom_vline -for(i1 in 1:length(data1)){ -if(geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline"){ -final.data.frame <- data.frame() -for(i3 in 1:nrow(data1[[i1]])){ -tempo.data.frame <- rbind(data1[[i1]][i3, ], data1[[i1]][i3, ], stringsAsFactors = TRUE) -if(geom[[i1]] == "geom_hline"){ -tempo.data.frame[, x[[i1]]] <- x.lim -}else if(geom[[i1]] == "geom_vline"){ -tempo.data.frame[, y[[i1]]] <- y.lim -}else{ -tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -# 3 lines below inactivated because I put that above -# if(is.null(categ[[i1]])){ -# data1[, "fake_categ"] <- paste0("Line_", i3) -# } -final.data.frame <- rbind(final.data.frame, tempo.data.frame, stringsAsFactors = TRUE) -} -data1[[i1]] <- final.data.frame -geom[[i1]] <- "geom_line" -if(length(color[[i1]])== 1L){ -color[[i1]] <- rep(color[[i1]], length(unique(data1[[i1]][ , categ[[i1]]]))) -}else if(length(color[[i1]]) != length(unique(data1[[i1]][ , categ[[i1]]]))){ -tempo.cat <- paste0("ERROR IN ", function.name, " geom_hline AND geom_vline CONVERSION TO FIT THE XLIM AND YLIM LIMITS OF THE DATA: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE THE LENGTH OF LEVELS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LEVELS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]]))) -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -} + + +fun_post_plot <- function( + x.side = 0, + x.log.scale = FALSE, + x.categ = NULL, + x.categ.pos = NULL, + x.lab = "", + x.axis.size = 1.5, + x.label.size = 1.5, + x.dist.legend = 0.5, + x.nb.inter.tick = 1, + y.side = 0, + y.log.scale = FALSE, + y.categ = NULL, + y.categ.pos = NULL, + y.lab = "", + y.axis.size = 1.5, + y.label.size = 1.5, + y.dist.legend = 0.5, + y.nb.inter.tick = 1, + text.angle = 90, + tick.length = 0.5, + sec.tick.length = 0.3, + bg.color = NULL, + grid.lwd = NULL, + grid.col = "white", + corner.text = "", + corner.text.size = 1, + just.label.add = FALSE, + par.reset = FALSE, + custom.par = NULL +){ + # AIM + # redesign axis. If x.side = 0, y.side = 0, the function just adds text at topright of the graph and reset par() for next graphics and provides outputs (see below) + # provide also positions for legend or additional text on the graph + # use fun_prior_plot() before this function for initial inactivation of the axis drawings + # ARGUMENTS + # x.side: axis at the bottom (1) or top (3) of the region figure. Write 0 for no change + # x.log.scale: Log scale for the x-axis? Either TRUE or FALSE + # x.categ: character vector representing the classes (levels()) to specify when the x-axis is qualititative(stripchart, boxplot) + # x.categ.pos: position of the classes names (numeric vector of identical length than x.categ). If left NULL, this will be 1:length(levels()) + # x.lab: label of the x-axis. If x.side == 0 and x.lab != "", then x.lab is printed + # x.axis.size: positive numeric. Increase or decrease the size of the x axis numbers. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2. Also control the size of displayed categories + # x.label.size: positive numeric. Increase or decrease the size of the x axis legend text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 + # x.dist.legend: increase the number to move x-axis legends away in inches (first number of mgp argument of par() but in inches) + # x.nb.inter.tick: number of secondary ticks between main ticks on x-axis (only if not log scale). 0 means no secondary ticks + # y.side: axis at the left (2) or right (4) of the region figure. Write 0 for no change + # y.log.scale: Log scale for the y-axis? Either TRUE or FALSE + # y.categ: classes (levels()) to specify when the y-axis is qualititative(stripchart, boxplot) + # y.categ.pos: position of the classes names (numeric vector of identical length than y.categ). If left NULL, this will be 1:length(levels()) + # y.lab: label of the y-axis. If y.side == 0 and y.lab != "", then y.lab is printed + # y.axis.size: positive numeric. Increase or decrease the size of the y axis numbers. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2. Also control the size of displayed categories + # y.label.size: positive numeric. Increase or decrease the size of the y axis legend text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 + # y.dist.legend: increase the number to move y-axis legends away in inches (first number of mgp argument of par() but in inches) + # y.nb.inter.tick: number of secondary ticks between main ticks on y-axis (only if not log scale). 0 means non secondary ticks + # text.angle: angle of the text when axis is qualitative + # tick.length: length of the main ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) + # sec.tick.length: length of the secondary ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) + # bg.color: background color of the plot region. NULL for no color. BEWARE: cover/hide an existing plot ! + # grid.lwd: if non NULL, activate the grid line (specify the line width) + # grid.col: grid line color (only if grid.lwd non NULL) + # corner.text: text to add at the top right corner of the window + # corner.text.size: positive numeric. Increase or decrease the size of the text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 + # par.reset: to reset all the graphics parameters. BEWARE: TRUE can generate display problems, mainly in graphic devices with multiple figure regions + # just.label.add: just add axis labels (legend)? Either TRUE or FALSE. If TRUE, at least (x.side == 0 & x.lab != "") or (y.side == 0 & y.lab != "") must be set to display the corresponding x.lab or y.lab + # custom.par: list that provides the parameters that reset all the graphics parameters. BEWARE: if NULL and par.reset == TRUE, the default par() parameters are used + # RETURN + # a list containing: + # $x.mid.left.dev.region: middle of the left margin of the device region, in coordinates of the x-axis + # $x.left.dev.region: left side of the left margin (including the potential margin of the device region), in coordinates of the x-axis + # $x.mid.right.dev.region: middle of the right margin of the device region, in coordinates of the x-axis + # $x.right.dev.region: right side of the right margin (including the potential margin of the device region), in coordinates of the x-axis + # $x.mid.left.fig.region: middle of the left margin of the figure region, in coordinates of the x-axis + # $x.left.fig.region: left side of the left margin, in coordinates of the x-axis + # $x.mid.right.fig.region: middle of the right margin of the figure region, in coordinates of the x-axis + # $x.right.fig.region: right side of the right margin, in coordinates of the x-axis + # $x.left.plot.region: left side of the plot region, in coordinates of the x-axis + # $x.right.plot.region: right side of the plot region, in coordinates of the x-axis + # $x.mid.plot.region: middle of the plot region, in coordinates of the x-axis + # $y.mid.bottom.dev.region: middle of the bottom margin of the device region, in coordinates of the y-axis + # $y.bottom.dev.region: bottom side of the bottom margin (including the potential margin of the device region), in coordinates of the y-axis + # $y.mid.top.dev.region: middle of the top margin of the device region, in coordinates of the y-axis + # $y.top.dev.region: top side of the top margin (including the potential margin of the device region), in coordinates of the y-axis + # $y.mid.bottom.fig.region: middle of the bottom margin of the figure region, in coordinates of the y-axis + # $y.bottom.fig.region: bottom of the bottom margin of the figure region, in coordinates of the y-axis + # $y.mid.top.fig.region: middle of the top margin of the figure region, in coordinates of the y-axis + # $y.top.fig.region: top of the top margin of the figure region, in coordinates of the y-axis + # $y.top.plot.region: top of the plot region, in coordinates of the y-axis + # $y.bottom.plot.region: bottom of the plot region, in coordinates of the y-axis + # $y.mid.plot.region: middle of the plot region, in coordinates of the y-axis + # $text: warning text + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_open() to reinitialize graph parameters if par.reset = TRUE and custom.par = NULL + # EXAMPLES + # Example of log axis with log y-axis and unmodified x-axis: + # prior.par <- fun_prior_plot(param.reinitial = TRUE, xlog.scale = FALSE, ylog.scale = TRUE, remove.label = TRUE, remove.x.axis = FALSE, remove.y.axis = TRUE, down.space = 1, left.space = 1, up.space = 1, right.space = 1, orient = 1, dist.legend = 0.5, tick.length = 0.5, box.type = "n", amplif.label = 1, amplif.axis = 1, display.extend = FALSE, return.par = TRUE) ; plot(1:100, log = "y") ; fun_post_plot(y.side = 2, y.log.scale = prior.par$ylog, x.lab = "Values", y.lab = "TEST", y.axis.size = 1.25, y.label.size = 1.5, y.dist.legend = 0.7, just.label.add = ! prior.par$ann) + # Example of log axis with redrawn x-axis and y-axis: + # prior.par <- fun_prior_plot(param.reinitial = TRUE) ; plot(1:100) ; fun_post_plot(x.side = 1, x.lab = "Values", y.side = 2, y.lab = "TEST", y.axis.size = 1, y.label.size = 2, y.dist.legend = 0.6) + # Example of title easily added to a plot: + # plot(1:100) ; para <- fun_post_plot(corner.text = "TITLE ADDED") # try also: par(xpd = TRUE) ; text(x = para$x.mid.left.fig.region, y = para$y.mid.top.fig.region, labels = "TITLE ADDED", cex = 0.5) + # example with margins in the device region: + # windows(5,5) ; fun_prior_plot(box.type = "o") ; par(mai=c(0.5,0.5,0.5,0.5), omi = c(0.25,0.25,1,0.25), xaxs = "i", yaxs = "i") ; plot(0:10) ; a <- fun_post_plot(x.side = 0, y.side = 0) ; x <- c(a$x.mid.left.dev.region, a$x.left.dev.region, a$x.mid.right.dev.region, a$x.right.dev.region, a$x.mid.left.fig.region, a$x.left.fig.region, a$x.mid.right.fig.region, a$x.right.fig.region, a$x.right.plot.region, a$x.left.plot.region, a$x.mid.plot.region) ; y <- c(a$y.mid.bottom.dev.region, a$y.bottom.dev.region, a$y.mid.top.dev.region, a$y.top.dev.region, a$y.mid.bottom.fig.region, a$y.bottom.fig.region, a$y.mid.top.fig.region, a$y.top.fig.region, a$y.top.plot.region, a$y.bottom.plot.region, a$y.mid.plot.region) ; par(xpd = NA) ; points(x = rep(5, length(y)), y = y, pch = 16, col = "red") ; text(x = rep(5, length(y)), y = y, c("y.mid.bottom.dev.region", "y.bottom.dev.region", "y.mid.top.dev.region", "y.top.dev.region", "y.mid.bottom.fig.region", "y.bottom.fig.region", "y.mid.top.fig.region", "y.top.fig.region", "y.top.plot.region", "y.bottom.plot.region", "y.mid.plot.region"), cex = 0.65, col = grey(0.25)) ; points(y = rep(5, length(x)), x = x, pch = 16, col = "blue") ; text(y = rep(5, length(x)), x = x, c("x.mid.left.dev.region", "x.left.dev.region", "x.mid.right.dev.region", "x.right.dev.region", "x.mid.left.fig.region", "x.left.fig.region", "x.mid.right.fig.region", "x.right.fig.region", "x.right.plot.region", "x.left.plot.region", "x.mid.plot.region"), cex = 0.65, srt = 90, col = grey(0.25)) + # DEBUGGING + # x.side = 0 ; x.log.scale = FALSE ; x.categ = NULL ; x.categ.pos = NULL ; x.lab = "" ; x.axis.size = 1.5 ; x.label.size = 1.5 ; x.dist.legend = 1 ; x.nb.inter.tick = 1 ; y.side = 0 ; y.log.scale = FALSE ; y.categ = NULL ; y.categ.pos = NULL ; y.lab = "" ; y.axis.size = 1.5 ; y.label.size = 1.5 ; y.dist.legend = 0.7 ; y.nb.inter.tick = 1 ; text.angle = 90 ; tick.length = 0.5 ; sec.tick.length = 0.3 ; bg.color = NULL ; grid.lwd = NULL ; grid.col = "white" ; corner.text = "" ; corner.text.size = 1 ; just.label.add = FALSE ; par.reset = FALSE ; custom.par = NULL # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_open", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_open() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = x.side, options = c(0, 1, 3), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.log.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(x.categ)){ + tempo <- fun_check(data = x.categ, class = "character", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(x.categ.pos)){ + tempo <- fun_check(data = x.categ.pos, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = x.lab, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.axis.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.label.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.nb.inter.tick, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.side, options = c(0, 2, 4), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.log.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(y.categ)){ + tempo <- fun_check(data = y.categ, class = "character", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(y.categ.pos)){ + tempo <- fun_check(data = y.categ.pos, class = "vector", mode = "numeric", fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = y.lab, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.axis.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.label.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.nb.inter.tick, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = text.angle, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = sec.tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + if( ! is.null(bg.color)){ + tempo <- fun_check(data = bg.color, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if( ! (bg.color %in% colors() | grepl(pattern = "^#", bg.color))){ # check color + tempo.cat <- paste0("ERROR IN ", function.name, ": bg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # OR A COLOR NAME GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + if( ! is.null(grid.lwd)){ + tempo <- fun_check(data = grid.lwd, class = "vector", mode = "numeric", neg.values = FALSE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(grid.col)){ + tempo <- fun_check(data = grid.col, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if( ! (grid.col %in% colors() | grepl(pattern = "^#", grid.col))){ # check color + tempo.cat <- paste0("ERROR IN ", function.name, ": grid.col ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # OR A COLOR NAME GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = corner.text, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = corner.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = just.label.add, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = par.reset, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(custom.par)){ + tempo <- fun_check(data = custom.par, typeof = "list", length = 1, fun.name = function.name) ; eval(ee) + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + text <- NULL + par(tcl = -par()$mgp[2] * tick.length) + if(x.log.scale == TRUE){ + grid.coord.x <- c(10^par("usr")[1], 10^par("usr")[2]) + }else{ + grid.coord.x <- c(par("usr")[1], par("usr")[2]) + } + if(y.log.scale == TRUE){ + grid.coord.y <- c(10^par("usr")[3], 10^par("usr")[4]) + }else{ + grid.coord.y <- c(par("usr")[3], par("usr")[4]) + } + if( ! is.null(bg.color)){ + rect(grid.coord.x[1], grid.coord.y[1], grid.coord.x[2], grid.coord.y[2], col = bg.color, border = NA) + } + if( ! is.null(grid.lwd)){ + grid(nx = NA, ny = NULL, col = grid.col, lty = 1, lwd = grid.lwd) + } + if(x.log.scale == TRUE){ + x.mid.left.dev.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) + x.left.dev.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) # in x coordinates + x.mid.right.dev.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) + x.right.dev.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2])) # in x coordinates + x.mid.left.fig.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) + x.left.fig.region <- 10^(par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1]) # in x coordinates + x.mid.right.fig.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) + x.right.fig.region <- 10^(par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2])) # in x coordinates + x.left.plot.region <- 10^par("usr")[1] # in x coordinates, left of the plot region (according to x scale) + x.right.plot.region <- 10^par("usr")[2] # in x coordinates, right of the plot region (according to x scale) + x.mid.plot.region <- 10^((par("usr")[2] + par("usr")[1]) / 2) # in x coordinates, right of the plot region (according to x scale) + }else{ + x.mid.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) + x.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) # in x coordinates + x.mid.right.dev.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) + x.right.dev.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) + ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * (1 - par("omd")[2])) # in x coordinates + x.mid.left.fig.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] / 2) # in x coordinates, to position axis labeling at the bottom of the graph (according to x scale) + x.left.fig.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1]) # in x coordinates + x.mid.right.fig.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2]) / 2) # in x coordinates, to position axis labeling at the top of the graph (according to x scale) + x.right.fig.region <- (par("usr")[2] + ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * (1 - par("plt")[2])) # in x coordinates + x.left.plot.region <- par("usr")[1] # in x coordinates, left of the plot region (according to x scale) + x.right.plot.region <- par("usr")[2] # in x coordinates, right of the plot region (according to x scale) + x.mid.plot.region <- (par("usr")[2] + par("usr")[1]) / 2 # in x coordinates, right of the plot region (according to x scale) + } + if(y.log.scale == TRUE){ + y.mid.bottom.dev.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (par("omd")[3] / 2)) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space + y.bottom.dev.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * par("omd")[3]) # in y coordinates + y.mid.top.dev.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space + y.top.dev.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) # in y coordinates + y.mid.bottom.fig.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] / 2) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space + y.bottom.fig.region <- 10^(par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3]) # in y coordinates + y.mid.top.fig.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space + y.top.fig.region <- 10^(par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4])) # in y coordinates + y.top.plot.region <- 10^par("usr")[4] # in y coordinates, top of the plot region (according to y scale) + y.bottom.plot.region <- 10^par("usr")[3] # in y coordinates, bottom of the plot region (according to y scale) + y.mid.plot.region <- (par("usr")[3] + par("usr")[4]) / 2 # in x coordinates, right of the plot region (according to x scale) + }else{ + y.mid.bottom.dev.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (par("omd")[3] / 2)) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space + y.bottom.dev.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] - ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * par("omd")[3]) # in y coordinates + y.mid.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space + y.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) # in y coordinates + y.mid.bottom.fig.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3] / 2) # in y coordinates, to position axis labeling at the bottom of the graph (according to y scale). Ex mid.bottom.space + y.bottom.fig.region <- (par("usr")[3] - ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * par("plt")[3]) # in y coordinates + y.mid.top.fig.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) / 2) # in y coordinates, to position axis labeling at the top of the graph (according to y scale). Ex mid.top.space + y.top.fig.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4])) # in y coordinates + y.top.plot.region <- par("usr")[4] # in y coordinates, top of the plot region (according to y scale) + y.bottom.plot.region <- par("usr")[3] # in y coordinates, bottom of the plot region (according to y scale) + y.mid.plot.region <- ((par("usr")[3] + par("usr")[4]) / 2) # in x coordinates, right of the plot region (according to x scale) + } + if(any(sapply(FUN = all.equal, c(1, 3), x.side) == TRUE)){ + par(xpd=FALSE, xaxt="s") + if(is.null(x.categ) & x.log.scale == TRUE){ + if(any(par()$xaxp[1:2] == 0L)){ # any(sapply(FUN = all.equal, par()$xaxp[1:2], 0) == TRUE) not used because we strictly need zero as a result. Beware: write "== TRUE", because the result is otherwise character and a warning message appears using any() + if(par()$xaxp[1] == 0L){ # isTRUE(all.equal(par()$xaxp[1], 0)) not used because we strictly need zero as a result + par(xaxp = c(10^-30, par()$xaxp[2:3])) # because log10(par()$xaxp[1] == 0) == -Inf + } + if(par()$xaxp[2] == 0L){ # isTRUE(all.equal(par()$xaxp[1], 0)) not used because we strictly need zero as a result + par(xaxp = c(par()$xaxp[1], 10^-30, par()$xaxp[3])) # because log10(par()$xaxp[2] == 0) == -Inf + } + } + axis(side = x.side, at = c(10^par()$usr[1], 10^par()$usr[2]), labels=rep("", 2), lwd=1, lwd.ticks = 0) # draw the axis line + mtext(side = x.side, text = x.lab, line = x.dist.legend / 0.2, las = 0, cex = x.label.size) + par(tcl = -par()$mgp[2] * sec.tick.length) # length of the secondary ticks are reduced + suppressWarnings(rug(10^outer(c((log10(par("xaxp")[1]) -1):log10(par("xaxp")[2])), log10(1:10), "+"), ticksize = NA, side = x.side)) # ticksize = NA to allow the use of par()$tcl value + par(tcl = -par()$mgp[2] * tick.length) # back to main ticks + axis(side = x.side, at = c(1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10), labels = expression(10^-15, 10^-14, 10^-13, 10^-12, 10^-11, 10^-10, 10^-9, 10^-8, 10^-7, 10^-6, 10^-5, 10^-4, 10^-3, 10^-2, 10^-1, 10^0, 10^1, 10^2, 10^3, 10^4, 10^5, 10^6, 10^7, 10^8, 10^9, 10^10), lwd = 0, lwd.ticks = 1, cex.axis = x.axis.size) + x.text <- 10^par("usr")[2] + }else if(is.null(x.categ) & x.log.scale == FALSE){ + axis(side=x.side, at=c(par()$usr[1], par()$usr[2]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line + axis(side=x.side, at=round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), cex.axis = x.axis.size) # axis(side=x.side, at=round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), labels = format(round(seq(par()$xaxp[1], par()$xaxp[2], length.out=par()$xaxp[3]+1), 2), big.mark=','), cex.axis = x.axis.size) # to get the 1000 comma separator + mtext(side = x.side, text = x.lab, line = x.dist.legend / 0.2, las = 0, cex = x.label.size) + if(x.nb.inter.tick > 0){ + inter.tick.unit <- (par("xaxp")[2] - par("xaxp")[1]) / par("xaxp")[3] + par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced + suppressWarnings(rug(seq(par("xaxp")[1] - 10 * inter.tick.unit, par("xaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + x.nb.inter.tick)), ticksize = NA, x.side)) # ticksize = NA to allow the use of par()$tcl value + par(tcl = -par()$mgp[2] * tick.length) # back to main ticks + } + x.text <- par("usr")[2] + }else if(( ! is.null(x.categ)) & x.log.scale == FALSE){ + if(is.null(x.categ.pos)){ + x.categ.pos <- 1:length(x.categ) + }else if(length(x.categ.pos) != length(x.categ)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.categ.pos MUST BE THE SAME LENGTH AS x.categ") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + par(xpd = TRUE) + if(isTRUE(all.equal(x.side, 1))){ #isTRUE(all.equal(x.side, 1)) is similar to x.side == 1L but deals with float + segments(x0 = x.left.plot.region, x1 = x.right.plot.region, y0 = y.bottom.plot.region, y1 = y.bottom.plot.region) # draw the line of the axis + text(x = x.categ.pos, y = y.mid.bottom.fig.region, labels = x.categ, srt = text.angle, cex = x.axis.size) + }else if(isTRUE(all.equal(x.side, 3))){ #isTRUE(all.equal(x.side, 1)) is similar to x.side == 3L but deals with float + segments(x0 = x.left.plot.region, x1 = x.right.plot.region, y0 = y.top.plot.region, y1 = y.top.plot.region) # draw the line of the axis + text(x = x.categ.pos, y = y.mid.top.fig.region, labels = x.categ, srt = text.angle, cex = x.axis.size) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": ARGUMENT x.side CAN ONLY BE 1 OR 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + par(xpd = FALSE) + x.text <- par("usr")[2] + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": PROBLEM WITH THE x.side (", x.side ,") OR x.log.scale (", x.log.scale,") ARGUMENTS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + x.text <- par("usr")[2] + } + if(any(sapply(FUN = all.equal, c(2, 4), y.side) == TRUE)){ + par(xpd=FALSE, yaxt="s") + if(is.null(y.categ) & y.log.scale == TRUE){ + if(any(par()$yaxp[1:2] == 0L)){ # any(sapply(FUN = all.equal, par()$yaxp[1:2], 0) == TRUE) not used because we strictly need zero as a result. Beware: write "== TRUE", because the result is otherwise character and a warning message appears using any() + if(par()$yaxp[1] == 0L){ # strict zero needed + par(yaxp = c(10^-30, par()$yaxp[2:3])) # because log10(par()$yaxp[1] == 0) == -Inf + } + if(par()$yaxp[2] == 0L){ # strict zero needed + par(yaxp = c(par()$yaxp[1], 10^-30, par()$yaxp[3])) # because log10(par()$yaxp[2] == 0) == -Inf + } + } + axis(side=y.side, at=c(10^par()$usr[3], 10^par()$usr[4]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line + par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced + suppressWarnings(rug(10^outer(c((log10(par("yaxp")[1])-1):log10(par("yaxp")[2])), log10(1:10), "+"), ticksize = NA, side = y.side)) # ticksize = NA to allow the use of par()$tcl value + par(tcl = -par()$mgp[2] * tick.length) # back to main tick length + axis(side = y.side, at = c(1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10), labels = expression(10^-15, 10^-14, 10^-13, 10^-12, 10^-11, 10^-10, 10^-9, 10^-8, 10^-7, 10^-6, 10^-5, 10^-4, 10^-3, 10^-2, 10^-1, 10^0, 10^1, 10^2, 10^3, 10^4, 10^5, 10^6, 10^7, 10^8, 10^9, 10^10), lwd = 0, lwd.ticks = 1, cex.axis = y.axis.size) + y.text <- 10^(par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) + mtext(side = y.side, text = y.lab, line = y.dist.legend / 0.2, las = 0, cex = y.label.size) + }else if(is.null(y.categ) & y.log.scale == FALSE){ + axis(side=y.side, at=c(par()$usr[3], par()$usr[4]), labels=rep("", 2), lwd=1, lwd.ticks=0) # draw the axis line + axis(side=y.side, at=round(seq(par()$yaxp[1], par()$yaxp[2], length.out=par()$yaxp[3]+1), 2), cex.axis = y.axis.size) + mtext(side = y.side, text = y.lab, line = y.dist.legend / 0.2, las = 0, cex = y.label.size) + if(y.nb.inter.tick > 0){ + inter.tick.unit <- (par("yaxp")[2] - par("yaxp")[1]) / par("yaxp")[3] + par(tcl = -par()$mgp[2] * sec.tick.length) # length of the ticks are reduced + suppressWarnings(rug(seq(par("yaxp")[1] - 10 * inter.tick.unit, par("yaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + y.nb.inter.tick)), ticksize = NA, side=y.side)) # ticksize = NA to allow the use of par()$tcl value + par(tcl = -par()$mgp[2] * tick.length) # back to main tick length + } + y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) + }else if(( ! is.null(y.categ)) & y.log.scale == FALSE){ + if(is.null(y.categ.pos)){ + y.categ.pos <- 1:length(y.categ) + }else if(length(y.categ.pos) != length(y.categ)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.categ.pos MUST BE THE SAME LENGTH AS y.categ") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + axis(side = y.side, at = y.categ.pos, labels = rep("", length(y.categ)), lwd=0, lwd.ticks=1) # draw the line of the axis + par(xpd = TRUE) + if(isTRUE(all.equal(y.side, 2))){ #isTRUE(all.equal(y.side, 2)) is similar to y.side == 2L but deals with float + text(x = x.mid.left.fig.region, y = y.categ.pos, labels = y.categ, srt = text.angle, cex = y.axis.size) + }else if(isTRUE(all.equal(y.side, 4))){ # idem + text(x = x.mid.right.fig.region, y = y.categ.pos, labels = y.categ, srt = text.angle, cex = y.axis.size) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": ARGUMENT y.side CAN ONLY BE 2 OR 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + par(xpd = FALSE) + y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": PROBLEM WITH THE y.side (", y.side ,") OR y.log.scale (", y.log.scale,") ARGUMENTS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + y.text <- (par("usr")[4] + (par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3]) * (1 - par("plt")[4])) + } + par(xpd=NA) + text(x = x.mid.right.fig.region, y = y.text, corner.text, adj=c(1, 1.1), cex = corner.text.size) # text at the topright corner. Replace x.right.fig.region by x.text if text at the right edge of the plot region + if(just.label.add == TRUE & isTRUE(all.equal(x.side, 0)) & x.lab != ""){ + text(x = x.mid.plot.region, y = y.mid.bottom.fig.region, x.lab, adj=c(0.5, 0.5), cex = x.label.size) # x label + } + if(just.label.add == TRUE & isTRUE(all.equal(y.side, 0)) & y.lab != ""){ + text(x = y.mid.plot.region, y = x.mid.left.fig.region, y.lab, adj=c(0.5, 0.5), cex = y.label.size) # x label + } + par(xpd=FALSE) + if(par.reset == TRUE){ + tempo.par <- fun_open(pdf = FALSE, return.output = TRUE) + invisible(dev.off()) # close the new window + if( ! is.null(custom.par)){ + if( ! names(custom.par) %in% names(tempo.par$ini.par)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": custom.par ARGUMENT SHOULD HAVE THE NAMES OF THE COMPARTMENT LIST COMING FROM THE par() LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + par(custom.par) + text <- c(text, "\nGRAPH PARAMETERS SET TO VALUES DEFINED BY custom.par ARGUMENT\n") + }else{ + par(tempo.par$ini.par) + text <- c(text, "\nGRAPH PARAMETERS RESET TO par() DEFAULT VALUES\n") + } + } + output <- list(x.mid.left.dev.region = x.mid.left.dev.region, x.left.dev.region = x.left.dev.region, x.mid.right.dev.region = x.mid.right.dev.region, x.right.dev.region = x.right.dev.region, x.mid.left.fig.region = x.mid.left.fig.region, x.left.fig.region = x.left.fig.region, x.mid.right.fig.region = x.mid.right.fig.region, x.right.fig.region = x.right.fig.region, x.left.plot.region = x.left.plot.region, x.right.plot.region = x.right.plot.region, x.mid.plot.region = x.mid.plot.region, y.mid.bottom.dev.region = y.mid.bottom.dev.region, y.bottom.dev.region = y.bottom.dev.region, y.mid.top.dev.region = y.mid.top.dev.region, y.top.dev.region = y.top.dev.region, y.mid.bottom.fig.region = y.mid.bottom.fig.region, y.bottom.fig.region = y.bottom.fig.region, y.mid.top.fig.region = y.mid.top.fig.region, y.top.fig.region = y.top.fig.region, y.top.plot.region = y.top.plot.region, y.bottom.plot.region = y.bottom.plot.region, y.mid.plot.region = y.mid.plot.region, text = text) + return(output) } -# end conversion of geom_hline and geom_vline +######## fun_close() #### close specific graphic windows -# kind of geom_point (vectorial or raster) -scatter.kind <- vector("list", length = length(data1)) # list of same length as data1, that will be used to use either ggplot2::geom_point() (vectorial dot layer) or fun_gg_point_rast() (raster dot layer) -fix.ratio <- FALSE -if(is.null(raster.threshold)){ -if(raster == TRUE){ -scatter.kind[] <- "fun_gg_point_rast" # not important to fill everything: will be only used when geom == "geom_point" -fix.ratio <- TRUE -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") RASTER PLOT GENERATED -> ASPECT RATIO OF THE PLOT REGION SET BY THE raster.ratio ARGUMENT (", fun_round(raster.ratio, 2), ") TO AVOID A BUG OF ELLIPSOID DOT DRAWING") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -}else{ -scatter.kind[] <- "ggplot2::geom_point" -} -}else{ -for(i2 in 1:length(data1)){ -if(geom[[i2]] == "geom_point"){ -if(nrow(data1[[i2]]) <= raster.threshold){ -scatter.kind[[i2]] <- "ggplot2::geom_point" -}else{ -scatter.kind[[i2]] <- "fun_gg_point_rast" -fix.ratio <- TRUE -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i2, " OF data1 ARGUMENT")), " LAYER AS RASTER (NOT VECTORIAL)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -} -if(any(unlist(scatter.kind) == "fun_gg_point_rast")){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") RASTER PLOT GENERATED -> ASPECT RATIO OF THE PLOT REGION SET BY THE raster.ratio ARGUMENT (", fun_round(raster.ratio, 2), ") TO AVOID A BUG OF ELLIPSOID DOT DRAWING") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -} -# end kind of geom_point (vectorial or raster) - - - - -# no need loop part -coord.names <- NULL -tempo.gg.name <- "gg.indiv.plot." -tempo.gg.count <- 0 -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){x[[1]]}else{x.lab})) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y[[1]]}else{y.lab})) -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) -# text angle management -x.tempo.just <- fun_gg_just(angle = x.text.angle, pos = "bottom", kind = "axis") -y.tempo.just <- fun_gg_just(angle = y.text.angle, pos = "left", kind = "axis") -# end text angle management -add.check <- TRUE -if( ! is.null(add)){ # if add is NULL, then = 0 -if(grepl(pattern = "ggplot2::theme", add) == TRUE){ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED\nIT IS RECOMMENDED TO USE \"+ theme(aspect.ratio = raster.ratio)\" IF RASTER MODE IS ACTIVATED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -add.check <- FALSE -} -} -if(add.check == TRUE & article == TRUE){ -# WARNING: not possible to add several times theme(). NO message but the last one overwrites the others -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) -if(grid == TRUE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -line = ggplot2::element_line(size = 0.5), -axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis -axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis -panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.minor.x = ggplot2::element_line(colour = "grey90", size = 0.25), -panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), -axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), -axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), -aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster -)) -}else{ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_line(size = 0.5), -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -axis.line.y.left = ggplot2::element_line(colour = "black"), -axis.line.x.bottom = ggplot2::element_line(colour = "black"), -axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), -axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), -aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster -)) -} -}else if(add.check == TRUE & article == FALSE){ -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( -text = ggplot2::element_text(size = text.size), -plot.title = ggplot2::element_text(size = title.text.size), # stronger than text -line = ggplot2::element_line(size = 0.5), -legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend -panel.background = ggplot2::element_rect(fill = "grey95"), -axis.line.y.left = ggplot2::element_line(colour = "black"), -axis.line.x.bottom = ggplot2::element_line(colour = "black"), -panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.minor.x = ggplot2::element_line(colour = "grey90", size = 0.25), -panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), -panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), -strip.background = ggplot2::element_rect(fill = "white", colour = "black"), -axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), -axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), -aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster -# do not work -> legend.position = "none" # to remove the legend completely: https://www.datanovia.com/en/blog/how-to-remove-legend-from-a-ggplot/ -)) -} -# end no need loop part - - -# loop part -point.count <- 0 -line.count <- 0 -lg.order <- vector(mode = "list", length = 6) # order of the legend -lg.order <- lapply(lg.order, as.numeric) # order of the legend -lg.color <- vector(mode = "list", length = 6) # color of the legend -lg.dot.shape <- vector(mode = "list", length = 6) # etc. -lg.dot.size <- vector(mode = "list", length = 6) # etc. -lg.dot.size <- lapply(lg.dot.size, as.numeric) # etc. -lg.dot.border.size <- vector(mode = "list", length = 6) # etc. -lg.dot.border.size <- lapply(lg.dot.border.size, as.numeric) # etc. -lg.dot.border.color <- vector(mode = "list", length = 6) # etc. -lg.line.size <- vector(mode = "list", length = 6) # etc. -lg.line.size <- lapply(lg.line.size, as.numeric) # etc. -lg.line.type <- vector(mode = "list", length = 6) # etc. -lg.alpha <- vector(mode = "list", length = 6) # etc. -lg.alpha <- lapply(lg.alpha, as.numeric) # etc. -for(i1 in 1:length(data1)){ -if(geom[[i1]] == "geom_point"){ -point.count <- point.count + 1 -if(point.count== 1L){ -fin.lg.disp[[1]] <- legend.disp[[point.count + line.count]] -lg.order[[1]] <- point.count + line.count -lg.color[[1]] <- color[[i1]] # if color == NULL -> NULL -lg.dot.shape[[1]] <- dot.shape[[i1]] -lg.dot.size[[1]] <- dot.size[[i1]] -lg.dot.border.size[[1]] <- dot.border.size[[i1]] -lg.dot.border.color[[1]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL -if(plot == TRUE & fin.lg.disp[[1]] == TRUE & dot.shape[[1]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[1]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[1]] <- alpha[[i1]] -} -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], fill = categ[[i1]]), shape = dot.shape[[i1]], size = dot.size[[i1]], stroke = dot.border.size[[i1]], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = if(i5== 1L){TRUE}else{FALSE})) # WARNING: a single color allowed for color argument outside aesthetic, but here a single color for border --> loop could be inactivated but kept for commodity # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) -} -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_manual(name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = as.character(color[[i1]]), breaks = class.categ)) # values are the values of fill, breaks reorder the classes according to class.categ in the legend, order argument of guide_legend determines the order of the different aesthetics in the legend (not order of classes). See guide_legend settings of scale_..._manual below -} -if(point.count== 2L){ -fin.lg.disp[[2]] <- legend.disp[[point.count + line.count]] -lg.order[[2]] <- point.count + line.count -lg.color[[2]] <- color[[i1]] # if color == NULL -> NULL -lg.dot.shape[[2]] <- dot.shape[[i1]] -lg.dot.size[[2]] <- dot.size[[i1]] -lg.dot.border.size[[2]] <- dot.border.size[[i1]] -lg.dot.border.color[[2]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL -if(plot == TRUE & fin.lg.disp[[2]] == TRUE & dot.shape[[2]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[2]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[2]] <- alpha[[i1]] -} -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], shape = categ[[i1]]), size = dot.size[[i1]], stroke = dot.border.size[[i1]], fill = color[[i1]][i5], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = FALSE)) # WARNING: a single color allowed for fill argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) +fun_close <- function(kind = "pdf", return.text = FALSE){ + # AIM + # close only specific graphic windows (devices) + # ARGUMENTS: + # kind: vector, among c("windows", "quartz", "x11", "X11", "pdf", "bmp", "png", "tiff"), indicating the kind of graphic windows (devices) to close. BEWARE: either "windows", "quartz", "x11" or "X11" means that all the X11 GUI graphics devices will be closed, whatever the OS used + # return.text: print text regarding the kind parameter and the devices that were finally closed? + # RETURN + # text regarding the kind parameter and the devices that were finally closed + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # windows() ; windows() ; pdf() ; dev.list() ; fun_close(kind = c("pdf", "x11"), return.text = TRUE) ; dev.list() + # DEBUGGING + # kind = c("windows", "pdf") ; return.text = FALSE # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = kind, options = c("windows", "quartz", "x11", "X11", "pdf", "bmp", "png", "tiff"), fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.text, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + text <- paste0("THE REQUIRED KIND OF GRAPHIC DEVICES TO CLOSE ARE ", paste(kind, collapse = " ")) + if(Sys.info()["sysname"] == "Windows"){ # Note that .Platform$OS.type() only says "unix" for macOS and Linux and "Windows" for Windows + if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ + tempo <- kind %in% c("windows", "quartz", "x11", "X11") + kind[tempo] <- "windows" # term are replaced by what is displayed when using a <- dev.list() ; names(a) + } + }else if(Sys.info()["sysname"] == "Linux"){ + if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ + tempo.device <- suppressWarnings(try(X11(), silent = TRUE))[] # open a X11 window to try to recover the X11 system used + if( ! is.null(tempo.device)){ + text <- paste0(text, "\nCANNOT CLOSE GUI GRAPHIC DEVICES AS REQUIRED BECAUSE THIS LINUX SYSTEM DOES NOT HAVE IT") + }else{ + tempo <- kind %in% c("windows", "quartz", "x11", "X11") + kind[tempo] <- names(dev.list()[length(dev.list())]) # term are replaced by what is displayed when using a <- dev.list() ; names(a) + invisible(dev.off()) # close the X11 opened by tempo + } + } + }else{ # for macOS + if(any(kind %in% c("windows", "quartz", "x11", "X11"))){ + tempo <- kind %in% c("windows", "quartz", "x11", "X11") + kind[tempo] <- "quartz" # term are replaced by what is displayed when using a <- dev.list() ; names(a) + } + } + kind <- unique(kind) + if(length(dev.list()) != 0){ + for(i in length(names(dev.list())):1){ + if(names(dev.list())[i] %in% kind){ + text <- paste0(text, "\n", names(dev.list())[i], " DEVICE NUMBER ", dev.list()[i], " HAS BEEN CLOSED") + invisible(dev.off(dev.list()[i])) + } + } + } + if(return.text == TRUE){ + return(text) + } } -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_shape_manual(name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(dot.shape[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of shape, breaks reorder the classes according to class.categ in the legend. See guide_legend settings of scale_..._manual below + +################ Standard graphics + + +######## fun_empty_graph() #### text to display for empty graphs + + + + + +fun_empty_graph <- function( + text = NULL, + text.size = 1, + title = NULL, + title.size = 1.5 +){ + # AIM + # display an empty plot with a text in the middle of the window (for instance to specify that no plot can be drawn) + # ARGUMENTS + # text: character string of the message to display + # text.size: numeric value of the text size + # title: character string of the graph title + # title.size: numeric value of the title size (in points) + # RETURN + # an empty plot + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # simple example + # fun_empty_graph(text = "NO GRAPH") + # white page + # fun_empty_graph() # white page + # all the arguments + # fun_empty_graph(text = "NO GRAPH", text.size = 2, title = "GRAPH1", title.size = 1) + # DEBUGGING + # text = "NO GRAPH" ; title = "GRAPH1" ; text.size = 1 + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if( ! is.null(text)){ + tempo <- fun_check(data = text, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(title)){ + tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = title.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + ini.par <- par(no.readonly = TRUE) # to recover the initial graphical parameters if required (reset). BEWARE: this command alone opens a pdf of GUI window if no window already opened. But here, protected with the code because always a tempo window opened + par(ann=FALSE, xaxt="n", yaxt="n", mar = rep(1, 4), bty = "n", xpd = NA) + plot(1, 1, type = "n") # no display with type = "n" + x.left.dev.region <- (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / ((par("omd")[2] - par("omd")[1]) * (par("plt")[2] - par("plt")[1]))) * par("omd")[1]) + y.top.dev.region <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par("omd")[4] - par("omd")[3]) * (par("plt")[4] - par("plt")[3]))) * (1 - par("omd")[4])) + if( ! is.null(text)){ + text(x = 1, y = 1, labels = text, cex = text.size) + } + if( ! is.null(title)){ + text(x = x.left.dev.region, y = y.top.dev.region, labels = title, adj=c(0, 1), cex = title.size) + } + par(ini.par) } -if(point.count== 3L){ -fin.lg.disp[[3]] <- legend.disp[[point.count + line.count]] -lg.order[[3]] <- point.count + line.count -lg.color[[3]] <- color[[i1]] # if color == NULL -> NULL -lg.dot.shape[[3]] <- dot.shape[[i1]] -lg.dot.size[[3]] <- dot.size[[i1]] -lg.dot.border.size[[3]] <- dot.border.size[[i1]] -lg.dot.border.color[[3]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL -if(plot == TRUE & fin.lg.disp[[3]] == TRUE & dot.shape[[3]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[3]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[3]] <- alpha[[i1]] -} -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], stroke = categ[[i1]]), shape = dot.shape[[i1]], size = dot.size[[i1]], fill = color[[i1]][i5], stroke = dot.border.size[[i1]], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = FALSE)) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + + +################ gg graphics + + +######## fun_gg_palette() #### ggplot2 default color palette + + + + + +fun_gg_palette <- function(n, kind = "std"){ + # AIM + # provide colors used by ggplot2 + # the interest is to use another single color that is not the red one used by default + # for ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html + # ARGUMENTS + # n: number of groups on the graph + # kind: either "std" for standard gg colors, "dark" for darkened gg colors, or "light" for pastel gg colors + # RETURN + # the vector of hexadecimal colors + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # output of the function + # fun_gg_palette(n = 2) + # the ggplot2 palette when asking for 7 different colors + # plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7)) + # selection of the 5th color of the ggplot2 palette made of 7 different colors + # plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7)[5]) + # the ggplot2 palette made of 7 darkened colors + # plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7, kind = "dark")) + # the ggplot2 palette made of 7 lighten colors + # plot(1:7, pch = 16, cex = 5, col = fun_gg_palette(n = 7, kind = "light")) + # DEBUGGING + # n = 0 + # kind = "std" + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = n, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & isTRUE(all.equal(n, 0))){ # isTRUE(all.equal(n, 0))) is similar to n == 0 but deals with float + tempo.cat <- paste0("ERROR IN ", function.name, ": n ARGUMENT MUST BE A NON ZERO INTEGER. HERE IT IS: ", paste(n, collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + tempo <- fun_check(data = kind, options = c("std", "dark", "light"), length = 1, fun.name = function.name) ; eval(ee) + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + hues = seq(15, 375, length = n + 1) + hcl(h = hues, l = if(kind == "std"){65}else if(kind == "dark"){35}else if(kind == "light"){85}, c = 100)[1:n] } -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "stroke", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(dot.border.size[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of stroke, breaks reorder the classes according to class.categ in the legend. See guide_legend settings of scale_..._manual below + +######## fun_gg_just() #### ggplot2 justification of the axis labeling, depending on angle + + + + + +fun_gg_just <- function(angle, pos, kind = "axis"){ + # AIM + # provide correct justification for text labeling, depending on the chosen angle + # WARNINGS + # justification behave differently on plot, depending whether it is used for annotayed text or for axis labelling. Indeed the latter has labelling constrained + # Of note, a bug in ggplot2: vjust sometimes does not work, i.e., the same justification result is obtained whatever the value used. This is the case with angle = 90, pos = "top", kind = "axis". While everything is fine with angle = 90, pos = "bottom", kind = "axis". At least, everything seems fine for kind = "axis" and pos = c("left", "bottom") + # ARGUMENTS + # angle: integer value of the text angle, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. + # pos: where text is? Either "top", "right", "bottom" or "left" of the elements to justify from + # kind: kind of text? Either "axis" or "text". In the first case, the pos argument refers to the axis position, and in the second to annotated text (using ggplot2::annotate() or ggplot2::geom_text()) + # RETURN + # a list containing: + # $angle: the submitted angle (value potentially reduced to fit the [-360 ; 360] interval, e.g., 460 -> 100, without impact on the final angle displayed) + # $pos: the selected position (argument pos) + # $kind: the selected kind of text (argument kind) + # $hjust: the horizontal justification + # $vjust: the vertical justification + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_gg_just(angle = 45, pos = "bottom") + # fun_gg_just(angle = (360*2 + 45), pos = "left") + # output <- fun_gg_just(angle = 45, pos = "bottom") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)) + # output <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.y = ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)) + ggplot2::coord_flip() + # output1 <- fun_gg_just(angle = 90, pos = "bottom") ; output2 <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; ggplot2::ggplot() + ggplot2::geom_bar(data = obs1, mapping = ggplot2::aes(x = group, y = time), stat = "identity") + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = output1$angle, hjust = output1$hjust, vjust = output1$vjust), axis.text.y = ggplot2::element_text(angle = output2$angle, hjust = output2$hjust, vjust = output2$vjust)) + # output <- fun_gg_just(angle = -45, pos = "left") ; obs1 <- data.frame(time = 1, km = 1, bird = "pigeon", stringsAsFactors = FALSE) ; ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point() + ggplot2::geom_text(mapping = ggplot2::aes(label = bird), angle = output$angle, hjust = output$hjust, vjust = output$vjust) + # obs1 <- data.frame(time = 1:10, km = 1:10, bird = c(NA, NA, NA, "pigeon", NA, "cat", NA, NA, NA, NA), stringsAsFactors = FALSE) ; fun_open(width = 4, height = 4) ; for(i0 in c("text", "axis")){for(i1 in c("top", "right", "bottom", "left")){for(i2 in c(0, 45, 90, 135, 180, 225, 270, 315, 360)){output <- fun_gg_just(angle = i2, pos = i1, kind = i0) ; title <- paste0("kind: ", i0, " | pos: ", i1, " | angle = ", i2, " | hjust: ", output$hjust, " | vjust: ", output$vjust) ; if(i0 == "text"){print(ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point(color = fun_gg_palette(1), alpha = 0.5) + ggplot2::ggtitle(title) + ggplot2::geom_text(mapping = ggplot2::aes(label = bird), angle = output$angle, hjust = output$hjust, vjust = output$vjust) + ggplot2::theme(title = ggplot2::element_text(size = 5)))}else{print(ggplot2::ggplot(data = obs1, mapping = ggplot2::aes(x = time, y = km)) + ggplot2::geom_point(color = fun_gg_palette(1), alpha = 0.5) + ggplot2::ggtitle(title) + ggplot2::geom_text(mapping = ggplot2::aes(label = bird)) + ggplot2::scale_x_continuous(position = ifelse(i1 == "top", "top", "bottom")) + ggplot2::scale_y_continuous(position = ifelse(i1 == "right", "right", "left")) + ggplot2::theme(title = ggplot2::element_text(size = 5), axis.text.x = if(i1 %in% c("top", "bottom")){ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)}, axis.text.y = if(i1 %in% c("right", "left")){ggplot2::element_text(angle = output$angle, hjust = output$hjust, vjust = output$vjust)}))}}}} ; dev.off() + # DEBUGGING + # angle = 45 ; pos = "left" ; kind = "axis" + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument primary checking + # arg with no default values + mandat.args <- c( + "angle", + "pos" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # using fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = angle, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = pos, options = c("left", "top", "right", "bottom"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = kind, options = c("axis", "text"), length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <- c( + "angle", + "pos", + "kind" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # end second round of checking and data preparation + # main code + # to get angle between -360 and 360 + while(angle > 360){ + angle <- angle - 360 + } + while(angle < -360){ + angle <- angle + 360 + } + # end to get angle between -360 and 360 + # justifications + if(pos %in% c("bottom", "top")){ + # code below is for if(pos == "bottom"){ + if(any(sapply(FUN = all.equal, c(-360, -180, 0, 180, 360), angle) == TRUE)){ # equivalent of angle == -360 | angle == -180 | angle == 0 | angle == 180 | angle == 360 but deals with floats + hjust <- 0.5 + if(kind == "text"){ + if(any(sapply(FUN = all.equal, c(-360, 0, 360), angle) == TRUE)){ + vjust <- 1 + }else if(any(sapply(FUN = all.equal, c(-180, 180), angle) == TRUE)){ + vjust <- 0 + } + }else{ + vjust <- 0.5 + } + }else if(any(sapply(FUN = all.equal, c(-270, 90), angle) == TRUE)){ + hjust <- 1 + vjust <- 0.5 + }else if(any(sapply(FUN = all.equal, c(-90, 270), angle) == TRUE)){ + hjust <- 0 + vjust <- 0.5 + }else if((angle > -360 & angle < -270) | (angle > 0 & angle < 90)){ + hjust <- 1 + vjust <- 1 + }else if((angle > -270 & angle < -180) | (angle > 90 & angle < 180)){ + hjust <- 1 + vjust <- 0 + }else if((angle > -180 & angle < -90) | (angle > 180 & angle < 270)){ + hjust <- 0 + vjust <- 0 + if(kind == "text" & pos == "top"){ + hjust <- 1 + } + }else if((angle > -90 & angle < 0) | (angle > 270 & angle < 360)){ + hjust <- 0 + vjust <- 1 + } + if(pos == "top"){ + if( ! ((angle > -180 & angle < -90) | (angle > 180 & angle < 270))){ + hjust <- 1 - hjust + } + vjust <- 1 - vjust + } + }else if(pos %in% c("left", "right")){ + # code below is for if(pos == "left"){ + if(any(sapply(FUN = all.equal, c(-270, -90, 90, 270), angle) == TRUE)){ # equivalent of angle == -270 | angle == -90 | angle == 90 | angle == 270 but deals with floats + hjust <- 0.5 + if(kind == "text"){ + if(any(sapply(FUN = all.equal, c(-90, 90), angle) == TRUE)){ + vjust <- 0 + }else if(any(sapply(FUN = all.equal, c(-270, 270), angle) == TRUE)){ + vjust <- 1 + } + }else{ + vjust <- 0.5 + } + }else if(any(sapply(FUN = all.equal, c(-360, 0, 360), angle) == TRUE)){ + hjust <- 1 + vjust <- 0.5 + }else if(any(sapply(FUN = all.equal, c(-180, 180), angle) == TRUE)){ + hjust <- 0 + vjust <- 0.5 + }else if((angle > -360 & angle < -270) | (angle > 0 & angle < 90)){ + hjust <- 1 + vjust <- 0 + }else if((angle > -270 & angle < -180) | (angle > 90 & angle < 180)){ + hjust <- 0 + vjust <- 0 + }else if((angle > -180 & angle < -90) | (angle > 180 & angle < 270)){ + hjust <- 0 + vjust <- 1 + }else if((angle > -90 & angle < 0) | (angle > 270 & angle < 360)){ + hjust <- 1 + vjust <- 1 + } + if(pos == "right"){ + hjust <- 1 - hjust + if( ! (((angle > -270 & angle < -180) | (angle > 90 & angle < 180)) | ((angle > -180 & angle < -90) | (angle > 180 & angle < 270)))){ + vjust <- 1 - vjust + } + } + } + # end justifications + output <- list(angle = angle, pos = pos, kind = kind, hjust = hjust, vjust = vjust) + return(output) } -}else{ -line.count <- line.count + 1 -if(line.count== 1L){ -fin.lg.disp[[4]] <- legend.disp[[point.count + line.count]] -lg.order[[4]] <- point.count + line.count -lg.color[[4]] <- color[[i1]] # if color == NULL -> NULL -lg.line.size[[4]] <- line.size[[i1]] -lg.line.type[[4]] <- line.type[[i1]] -if(plot == TRUE & fin.lg.disp[[4]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[4]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[4]] <- alpha[[i1]] + + +######## fun_gg_get_legend() #### get the legend of ggplot objects + + + + + +fun_gg_get_legend <- function(ggplot_built, fun.name = NULL, lib.path = NULL){ + # AIM + # get legend of ggplot objects + # # from https://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot + # ARGUMENTS + # ggplot_built: a ggplot build object + # fun.name: single character string indicating the name of the function using fun_gg_get_legend() for warning and error messages. Ignored if NULL + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # RETURN + # a list of class c("gtable", "gTree", "grob", "gDesc"), providing legend information of ggplot_built objet, or NULL if the ggplot_built object has no legend + # REQUIRED PACKAGES + # ggplot2 + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # EXAMPLES + # Simple example + # obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time, fill = group)) ; fun_gg_get_legend(ggplot_built = ggplot2::ggplot_build(p)) + # Error message because no legend in the ggplot + # obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time)) ; fun_gg_get_legend(ggplot_built = ggplot2::ggplot_build(p)) + # DEBUGGING + # obs1 <- data.frame(time = 1:20, group = rep(c("CLASS_1", "CLASS_2"), times = 10), stringsAsFactors = TRUE) ; p <- ggplot2::ggplot() + ggplot2::geom_point(data = obs1, mapping = ggplot2::aes(x = group, y = time)) ; ggplot_built = ggplot2::ggplot_build(p) ; fun.name = NULL ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + req.function <- c( + "fun_check", + "fun_pack" + ) + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED ", i1, "() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end required function checking + # argument primary checking + # arg with no default values + mandat.args <- c( + "ggplot_built" + ) + tempo <- eval(parse(text = paste0("c(missing(", paste0(mandat.args, collapse = "), missing("), "))"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(sum(tempo, na.rm = TRUE) > 1, "S HAVE", " HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args[tempo], collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # using fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = ggplot_built, class = "ggplot_built", mode = "list", fun.name = function.name) ; eval(ee) + if( ! is.null(fun.name)){ + tempo <- fun_check(data = fun.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + } + if( ! is.null(arg.check)){ + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + } + # end using fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking + # management of NA + if(any(is.na(ggplot_built)) | any(is.na(fun.name)) | any(is.na(lib.path))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": NO ARGUMENT CAN HAVE NA VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA + # management of NULL + if(is.null(ggplot_built)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nggplot_built ARGUMENT CANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL + if( ! is.null(lib.path)){ + if( ! all(dir.exists(lib.path))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end second round of checking + # package checking + fun_pack(req.package = c("ggplot2"), lib.path = lib.path) + # end package checking + # main code + win.nb <- dev.cur() + pdf(file = NULL) + tmp <- ggplot2::ggplot_gtable(ggplot_built) + # BEWARE with ggplot_gtable : open a blanck device https://stackoverflow.com/questions/17012518/why-does-this-r-ggplot2-code-bring-up-a-blank-display-device + invisible(dev.off()) + if(win.nb > 1){ # to go back to the previous active device, if == 1 means no opened device + dev.set(win.nb) + } + leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box") + if(length(leg) == 0L){ + legend <- NULL + }else{ + legend <- tmp$grobs[[leg]] + } + return(legend) } -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl -ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting -"(data = tempo.data.frame, mapping = ggplot2::aes(x = ", -x[[i1]], -ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), -y[[i1]], -if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, -", linetype = ", -categ[[i1]], -"), color = \"", -color[[i1]][i5], -"\", size = ", -line.size[[i1]], -ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), -ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), -", alpha = ", -alpha[[i1]], -", show.legend = ", -ifelse(i5== 1L, TRUE, FALSE), -")" -)))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + + +######## fun_gg_point_rast() #### ggplot2 raster scatterplot layer + + + + + +fun_gg_point_rast <- function( + data = NULL, + mapping = NULL, + stat = "identity", + position = "identity", + ..., + na.rm = FALSE, + show.legend = NA, + inherit.aes = TRUE, + raster.width = NULL, + raster.height = NULL, + raster.dpi = 300, + inactivate = TRUE, + lib.path = NULL +){ + # AIM + # equivalent to ggplot2::geom_point() but in raster mode + # use it like ggplot2::geom_point() with the main raster.dpi additional argument + # WARNINGS + # can be long to generate the plot + # use a square plot region. Otherwise, the dots will have ellipsoid shape + # solve the transparency problems with some GUI + # this function is derived from the geom_point_rast() function, created by Viktor Petukhov , and present in the ggrastr package (https://rdrr.io/github/VPetukhov/ggrastr/src/R/geom-point-rast.R, MIT License, Copyright (c) 2017 Viktor Petukhov). Has been placed here to minimize package dependencies + # ARGUMENTS + # classical arguments of geom_point(), shown here https://rdrr.io/github/VPetukhov/ggrastr/man/geom_point_rast.html + # raster.width : width of the result image (in inches). Default: deterined by the current device parameters + # raster.height: height of the result image (in inches). Default: deterined by the current device parameters + # raster.dpi: resolution of the result image + # inactivate: logical. Inactivate the fun.name argument of the fun_check() function? If TRUE, the name of the fun_check() function in error messages coming from this function. Use TRUE if fun_gg_point_rast() is used like this: eval(parse(text = "fun_gg_point_rast")) + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # RETURN + # a raster scatter plot + # REQUIRED PACKAGES + # ggplot2 + # grid (included in the R installation packages but not automatically loaded) + # Cairo + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # EXAMPLES + # Two pdf in the current directory + # set.seed(1) ; data1 = data.frame(x = rnorm(100000), y = rnorm(10000), stringsAsFactors = TRUE) ; fun_open(pdf.name = "Raster") ; ggplot2::ggplot() + fun_gg_point_rast(data = data1, mapping = ggplot2::aes(x = x, y = y)) ; fun_open(pdf.name = "Vectorial") ; ggplot2::ggplot() + ggplot2::geom_point(data = data1, mapping = ggplot2::aes(x = x, y = y)) ; dev.off() ; dev.off() + # DEBUGGING + # + # function name + if(all(inactivate == FALSE)){ # inactivate has to be used here but will be fully checked below + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + }else if(all(inactivate == TRUE)){ + function.name <- NULL + }else{ + tempo.cat <- paste0("ERROR IN fun_gg_point_rast(): CODE INCONSISTENCY 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if( ! is.null(data)){ + tempo <- fun_check(data = data, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(mapping)){ + tempo <- fun_check(data = mapping, class = "uneval", typeof = "list", fun.name = function.name) ; eval(ee) # aes() is tested + } + # stat and position not tested because too complicate + tempo <- fun_check(data = na.rm, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = show.legend, class = "vector", mode = "logical", length = 1, na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = inherit.aes, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(raster.width)){ + tempo <- fun_check(data = raster.width, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + } + if( ! is.null(raster.height)){ + tempo <- fun_check(data = raster.height, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = raster.dpi, class = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = inactivate, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # package checking + fun_pack(req.package = c("ggplot2"), lib.path = lib.path) + fun_pack(req.package = c("grid"), lib.path = lib.path) + fun_pack(req.package = c("Cairo"), lib.path = lib.path) + # end package checking + # additional functions + DrawGeomPointRast <- function(data, panel_params, coord, na.rm = FALSE, raster.width = NULL, raster.height= NULL, raster.dpi = raster.dpi){ + if (is.null(raster.width)){ + raster.width <- par('fin')[1] + } + if (is.null(raster.height)){ + raster.height <- par('fin')[2] + } + prev_dev_id <- dev.cur() + p <- ggplot2::GeomPoint$draw_panel(data, panel_params, coord) + dev_id <- Cairo::Cairo(type='raster', width = raster.width*raster.dpi, height = raster.height*raster.dpi, dpi = raster.dpi, units = 'px', bg = "transparent")[1] + grid::pushViewport(grid::viewport(width = 1, height = 1)) + grid::grid.points(x = p$x, y = p$y, pch = p$pch, size = p$size, + name = p$name, gp = p$gp, vp = p$vp, draw = T) + grid::popViewport() + cap <- grid::grid.cap() + invisible(dev.off(dev_id)) + invisible(dev.set(prev_dev_id)) + grid::rasterGrob(cap, x = 0, y = 0, width = 1, height = 1, default.units = "native", just = c("left","bottom")) + } + # end additional functions + # main code + GeomPointRast <- ggplot2::ggproto("GeomPointRast", ggplot2::GeomPoint, draw_panel = DrawGeomPointRast) + ggplot2::layer( + data = data, + mapping = mapping, + stat = stat, + geom = GeomPointRast, + position = position, + show.legend = show.legend, + inherit.aes = inherit.aes, + params = list( + na.rm = na.rm, + raster.width = raster.width, + raster.height = raster.height, + raster.dpi = raster.dpi, + ... + ) + ) + # end main code } -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(line.type[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, with different values, breaks reorder the classes according to class.categ in the legend + + +######## fun_gg_boxplot() #### ggplot2 boxplot + background dots if required + + + + +######## fun_gg_scatter() #### ggplot2 scatterplot + lines (up to 6 overlays totally) + + + + +######## fun_gg_heatmap() #### ggplot2 heatmap + overlaid mask if required + + +#test plot.margin = margin(up.space.mds, right.space.mds, down.space.mds, left.space.mds, "inches") to set the dim of the region plot ? +# if matrix is full of zero (or same value I guess), heatmap is complicate. Test it and error message + +fun_gg_heatmap <- function( + data1, + legend.name1 = "", + low.color1 = "blue", + mid.color1 = "white", + high.color1 = "red", + limit1 = NULL, + midpoint1 = NULL, + data2 = NULL, + color2 = "black", + alpha2 = 0.5, + invert2 = FALSE, + text.size = 12, + title = "", + title.text.size = 12, + show.scale = TRUE, + rotate = FALSE, + return = FALSE, + plot = TRUE, + add = NULL, + warn.print = FALSE, + lib.path = NULL +){ + # AIM + # ggplot2 heatmap with the possibility to overlay a mask + # see also: + # draw : http://www.sthda.com/english/wiki/ggplot2-quick-correlation-matrix-heatmap-r-software-and-data-visualization + # same range scale : https://stackoverflow.com/questions/44655723/r-ggplot2-heatmap-fixed-scale-color-between-graphs + # for ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html + # ARGUMENTS + # data1: numeric matrix or data frame resulting from the conversion of the numeric matrix by reshape2::melt() + # legend.name1: character string of the data1 heatmap scale legend + # low.color1: character string of the color (i.e., "blue" or "#0000FF") of the lowest scale value + # mid.color1: same as low.color1 but for the middle scale value. If NULL, the middle color is the default color between low.color1 and high.color1. BEWARE: argument midpoint1 is not ignored, even if mid.color1 is NULL, meaning that the default mid color can still be controled + # high.color1: same as low.color1 but for the highest scale value + # limit1: 2 numeric values defining the lowest and higest color scale values. If NULL, take the range of data1 values + # midpoint1: single numeric value defining the value corresponding to the mid.color1 argument. A warning message is returned if midpoint1 does not correspond to the mean of limit1 values, because the color scale is not linear anymore. If NULL, takes the mean of limit1 values. Mean of data1, instead of mean of limit1, can be used here if required + # data2: binary mask matrix (made of 0 and 1) of same dimension as data1 or a data frame resulting from the conversion of the binary mask matrix by reshape2::melt(). Value 1 of data2 will correspond to color2 argument (value 0 will be NA color), and the opposite if invert2 argument is TRUE (inverted mask) + # color2: color of the 1 values of the binary mask matrix. The 0 values will be color NA + # alpha2: numeric value (from 0 to 1) of the mask transparency + # invert2: logical. Invert the mask (1 -> 0 and 0 -> 1)? + # text.size: numeric value of the size of the texts in scale + # title: character string of the graph title + # title.text.size: numeric value of the title size (in points) + # show.scale: logical. Show color scale? + # rotate: logical. Rotate the heatmap 90° clockwise? + # return: logical. Return the graph parameters? + # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting + # add: character string allowing to add more ggplot2 features (dots, lines, themes, etc.). BEWARE: (1) must start with "+" just after the simple or double opening quote (no space, end of line, carriage return, etc., allowed), (2) must finish with ")" just before the simple or double closing quote (no space, end of line, carriage return, etc., allowed) and (3) each function must be preceded by "ggplot2::" (for instance: "ggplot2::coord_flip()). If the character string contains the "ggplot2::theme" string, then internal ggplot2 theme() and theme_classic() functions will be inactivated to be reused by add. BEWARE: handle this argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions + # warn.print: logical. Print warnings at the end of the execution? No print if no warning messages + # lib.path: absolute path of the required packages, if not in the default folders + # RETURN + # a heatmap if plot argument is TRUE + # a list of the graph info if return argument is TRUE: + # $data: a list of the graphic info + # $axes: a list of the axes info + # $scale: the scale info (lowest, mid and highest values) + # $warn: the warning messages. Use cat() for proper display. NULL if no warning + # REQUIRED PACKAGES + # ggplot2 + # reshape2 + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # fun_round() + # EXAMPLES + # fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), title = "GRAPH 1") + # fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), return = TRUE) + # fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), legend.name1 = "VALUE", title = "GRAPH 1", text.size = 5, data2 = matrix(rep(c(1,0,0,0), 4), ncol = 4), invert2 = FALSE, return = TRUE) + # diagonal matrix + # fun_gg_heatmap(data1 = matrix(c(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1), ncol = 4)) + # fun_gg_heatmap(data1 = reshape2::melt(matrix(c(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1), ncol = 4))) + # error message + # fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), data2 = matrix(rep(c(1,0,0,0), 5), ncol = 5)) + # fun_gg_heatmap(data1 = matrix(1:16, ncol = 4), data2 = reshape2::melt(matrix(rep(c(1,0,0,0), 4), ncol = 4))) + # fun_gg_heatmap(data1 = reshape2::melt(matrix(1:16, ncol = 4)), data2 = reshape2::melt(matrix(rep(c(1,0,0,0), 4), ncol = 4))) + # DEBUGGING + # data1 = matrix(1:16, ncol = 4) ; legend.name1 = "" ; low.color1 = "blue" ; mid.color1 = "white" ; high.color1 = "red" ; limit1 = NULL ; midpoint1 = NULL ; data2 = matrix(rep(c(1,0,0,0), 4), ncol = 4) ; color2 = "black" ; alpha2 = 0.5 ; invert2 = FALSE ; text.size = 12 ; title = "" ; title.text.size = 12 ; show.scale = TRUE ; rotate = FALSE ; return = FALSE ; plot = TRUE ; add = NULL ; warn.print = TRUE ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_round", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_round() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # no reserved words required for this function + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if(all(is.matrix(data1))){ + tempo <- fun_check(data = data1, class = "matrix", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + }else if(all(is.data.frame(data1))){ + tempo <- fun_check(data = data1, class = "data.frame", length = 3, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + # structure of reshape2::melt() data frame + tempo <- fun_check(data = data1[, 1], data.name = "COLUMN 1 OF data1 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = data1[, 2], data.name = "COLUMN 2 OF data1 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = data1[, 3], data.name = "COLUMN 3 OF data1 (reshape2::melt() DATA FRAME)", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data1 ARGUMENT MUST BE A NUMERIC MATRIX OR A DATA FRAME OUTPUT OF THE reshape::melt() FUNCTION") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = legend.name1, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = low.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (all(low.color1 %in% colors() | grepl(pattern = "^#", low.color1)))){ # check that all strings of low.color1 start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": low.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(mid.color1)){ + tempo <- fun_check(data = mid.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (all(mid.color1 %in% colors() | grepl(pattern = "^#", mid.color1)))){ # check that all strings of mid.color1 start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": mid.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = high.color1, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (all(high.color1 %in% colors() | grepl(pattern = "^#", high.color1)))){ # check that all strings of high.color1 start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": high.color1 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(limit1)){ + tempo <- fun_check(data = limit1, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & any(limit1 %in% c(Inf, -Inf))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": limit1 ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + if( ! is.null(midpoint1)){ + tempo <- fun_check(data = midpoint1, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + } + if( ! is.null(data2)){ + if(all(is.matrix(data2))){ + tempo <- fun_check(data = data2, class = "matrix", mode = "numeric", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! all(unique(data2) %in% c(0,1))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX IN data2 MUST BE MADE OF 0 AND 1 ONLY (MASK MATRIX)") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & all(is.matrix(data1)) & ! identical(dim(data1), dim(data2))){ # matrix and matrix + tempo.cat <- paste0("ERROR IN ", function.name, ": MATRIX DIMENSION IN data2 MUST BE IDENTICAL AS MATRIX DIMENSION IN data1. HERE IT IS RESPECTIVELY:\n", paste(dim(data2), collapse = " "), "\n", paste(dim(data1), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & all(is.data.frame(data1)) & nrow(data1) != prod(dim(data2))){ # reshape2 and matrix + tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME IN data2 MUST HAVE ROW NUMBER EQUAL TO PRODUCT OF DIMENSIONS OF data1 MATRIX. HERE IT IS RESPECTIVELY:\n", paste(nrow(data1), collapse = " "), "\n", paste(prod(dim(data2)), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else if(all(is.data.frame(data2))){ + tempo <- fun_check(data = data2, class = "data.frame", length = 3, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + # structure of reshape2::melt() data frame + tempo <- fun_check(data = data2[, 1], data.name = "COLUMN 1 OF data2 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = data2[, 2], data.name = "COLUMN 2 OF data2 (reshape2::melt() DATA FRAME)", typeof = "integer", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = data2[, 3], data.name = "COLUMN 3 OF data2 (reshape2::melt() DATA FRAME)", mode = "numeric", fun.name = function.name) ; eval(ee) + } + if(tempo$problem == FALSE & ! all(unique(data2[, 3]) %in% c(0,1))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THIRD COLUMN OF DATA FRAME IN data2 MUST BE MADE OF 0 AND 1 ONLY (MASK DATA FRAME)") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & all(is.data.frame(data1)) & ! identical(dim(data1), dim(data2))){ # data frame and data frame + tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME DIMENSION IN data2 MUST BE IDENTICAL TO DATA FRAME DIMENSION IN data1. HERE IT IS RESPECTIVELY:\n", paste(dim(data2), collapse = " "), "\n", paste(dim(data1), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & all(is.matrix(data1)) & nrow(data2) != prod(dim(data1))){ # reshape2 and matrix + tempo.cat <- paste0("ERROR IN ", function.name, ": DATA FRAME IN data2 MUST HAVE ROW NUMBER EQUAL TO PRODUCT OF DIMENSION OF data1 MATRIX. HERE IT IS RESPECTIVELY:\n", paste(nrow(data2), collapse = " "), "\n", paste(prod(dim(data1)), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE data2 ARGUMENT MUST BE A NUMERIC MATRIX OR A DATA FRAME OUTPUT OF THE reshape::melt() FUNCTION") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = color2, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (all(color2 %in% colors() | grepl(pattern = "^#", color2)))){ # check that all strings of color2 start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": color2 ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors()") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = alpha2, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = invert2, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = show.scale, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(add)){ + tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! grepl(pattern = "^\\+", add)){ # check that the add string start by + + tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & ! grepl(pattern = "ggplot2::", add)){ # + tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST CONTAIN \"ggplot2::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & ! grepl(pattern = ")$", add)){ # check that the add string finished by ) + tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # package checking + fun_pack(req.package = c("reshape2", "ggplot2"), lib.path = lib.path) + # end package checking + # main code + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + if(all(is.matrix(data1))){ + data1 <- reshape2::melt(data1) # transform a matrix into a data frame with 2 coordinates columns and the third intensity column + } + if(rotate == TRUE){ + data1[, 1] <- rev(data1[, 1]) + } + if(is.null(limit1)){ + if(any(data1[, 3] %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE THIRD COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + limit1 <- range(data1[, 3], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE limit1 ARGUMENT IS NULL -> RANGE OF data1 ARGUMENT HAS BEEN TAKEN: ", paste(fun_round(limit1), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + if(suppressWarnings(any(limit1 %in% c(Inf, -Inf)))){ + tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED LIMIT CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(is.null(midpoint1)){ + midpoint1 <- mean(limit1, na.rm = TRUE) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE midpoint1 ARGUMENT IS NULL -> MEAN OF limit1 ARGUMENT HAS BEEN TAKEN: ", paste(fun_round(midpoint1), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else if(fun_round(midpoint1, 9) != fun_round(mean(limit1), 9)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE midpoint1 ARGUMENT (", fun_round(mean(midpoint1), 9), ") DOES NOT CORRESPOND TO THE MEAN OF THE limit1 ARGUMENT (", fun_round(mean(limit1), 9), "). COLOR SCALE IS NOT LINEAR") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data2)){ + if(all(is.matrix(data2))){ + data2 <- reshape2::melt(data2) # transform a matrix into a data frame with 2 coordinates columns and the third intensity column + } + if(rotate == TRUE){ + data2[, 1] <- rev(data2[, 1]) + } + data2[, 3] <- factor(data2[, 3]) # to converte continuous scale into discrete scale + } + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 # to facilitate debugging + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggplot()) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data1, mapping = ggplot2::aes_string(x = names(data1)[ifelse(rotate == FALSE, 2, 1)], y = names(data1)[ifelse(rotate == FALSE, 1, 2)], fill = names(data1)[3]), show.legend = show.scale)) # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_gradient2(low = low.color1, high = high.color1, mid = mid.color1, midpoint = midpoint1, limit = limit1, breaks = c(limit1[1], midpoint1, limit1[2]), labels = fun_round(c(limit1[1], midpoint1, limit1[2])), name = legend.name1)) + if( ! is.null(data2)){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data2, mapping = ggplot2::aes_string(x = names(data2)[ifelse(rotate == FALSE, 2, 1)], y = names(data2)[ifelse(rotate == FALSE, 1, 2)], alpha = names(data2)[3]), fill = color2, show.legend = FALSE)) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", values = if(invert2 == FALSE){c(0, alpha2)}else{c(alpha2, 0)}, guide = FALSE)) + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_raster(data = data2, mapping = ggplot2::aes_string(x = names(data2)[ifelse(rotate == FALSE, 2, 1)], y = names(data2)[ifelse(rotate == FALSE, 1, 2)], group = names(data2)[3]), fill = data2[, 3], alpha = alpha2, show.legend = FALSE)) # BEWARE: this does not work if NA present, because geom_raster() has a tendency to complete empty spaces, and thus, behave differently than geom_tile(). See https://github.com/tidyverse/ggplot2/issues/3025 + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_fixed()) # x = y + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_reverse()) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) + add.check <- TRUE + if( ! is.null(add)){ # if add is NULL, then = 0 + if(grepl(pattern = "ggplot2::theme", add) == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT -> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + add.check <- FALSE + } + } + if(add.check == TRUE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_blank(), + axis.title = ggplot2::element_blank(), + axis.text = ggplot2::element_blank(), + axis.ticks = ggplot2::element_blank(), + panel.background = ggplot2::element_blank() + )) + } + if(plot == TRUE){ + # suppressWarnings( + print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add})))) + # ) + }else{ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + if(return == TRUE){ + output <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))) + output <- output$data + names(output)[1] <- "heatmap" + if( ! is.null(data2)){ + names(output)[2] <- "mask" + } + return(list(data = output, axes = output$layout$panel_params[[1]], scale = c(limit1[1], midpoint1, limit1[2]), warn = warn)) + } } -if(line.count== 2L){ -fin.lg.disp[[5]] <- legend.disp[[point.count + line.count]] -lg.order[[5]] <- point.count + line.count -lg.color[[5]] <- color[[i1]] # if color == NULL -> NULL -lg.line.size[[5]] <- line.size[[i1]] -lg.line.type[[5]] <- line.type[[i1]] -if(plot == TRUE & fin.lg.disp[[5]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[5]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[5]] <- alpha[[i1]] + + +######## fun_gg_empty_graph() #### text to display for empty graphs + + + + + +fun_gg_empty_graph <- function( + text = NULL, + text.size = 12, + title = NULL, + title.size = 8, + lib.path = NULL +){ + # AIM + # display an empty ggplot2 plot with a text in the middle of the window (for instance to specify that no plot can be drawn) + # ARGUMENTS + # text: character string of the message to display + # text.size: numeric value of the text size (in points) + # title: character string of the graph title + # title.size: numeric value of the title size (in points) + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL + # RETURN + # an empty plot + # REQUIRED PACKAGES + # ggplot2 + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # EXAMPLES + ### simple example + # fun_gg_empty_graph(text = "NO GRAPH") + ### white page + # fun_gg_empty_graph() + ### all the arguments + # fun_gg_empty_graph(text = "NO GRAPH", text.size = 8, title = "GRAPH1", title.size = 10, lib.path = NULL) + # DEBUGGING + # text = "NO GRAPH" ; text.size = 12 ; title = "GRAPH1" ; title.size = 8 ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if( ! is.null(text)){ + tempo <- fun_check(data = text, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(title)){ + tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = title.size, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # package checking + fun_pack(req.package = c("ggplot2"), lib.path = lib.path) + # end package checking + # main code + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 + # no need loop part + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggplot()) + if( ! is.null(text)){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(data = data.frame(x = 1, y = 1, stringsAsFactors = TRUE), ggplot2::aes(x = x, y = y, label = text), size = text.size)) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void()) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + plot.title = ggplot2::element_text(size = title.size) # stronger than text + )) + suppressWarnings(print(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))) } -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl -ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting -"(data = tempo.data.frame, mapping = ggplot2::aes(x = ", -x[[i1]], -ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), -y[[i1]], -if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, -", alpha = ", -categ[[i1]], -"), color = \"", -color[[i1]][i5], -"\", size = ", -line.size[[i1]], -", linetype = ", -ifelse(is.numeric(line.type[[i1]]), "", "\""), -line.type[[i1]], -ifelse(is.numeric(line.type[[i1]]), "", "\""), -ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), -ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), -", show.legend = FALSE)" -)))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + + +################ Graphic extraction + + +######## fun_trim() #### display values from a quantitative variable and trim according to defined cut-offs + +# Add name of the variable in the graph +# not max and min for boxplot but 1.5IQR +fun_trim <- function( + data, + displayed.nb = NULL, + single.value.display = FALSE, + trim.method = "", + trim.cutoffs = c(0.05, + 0.975), + interval.scale.disp = TRUE, + down.space = 0.75, + left.space = 0.75, + up.space = 0.3, + right.space = 0.25, + orient = 1, + dist.legend = 0.37, + box.type = "l", + amplif.label = 1.25, + amplif.axis = 1.25, + std.x.range = TRUE, + std.y.range = TRUE, + cex.pt = 0.2, + col.box = hsv(0.55, + 0.8, + 0.8), + x.nb.inter.tick = 4, + y.nb.inter.tick = 0, + tick.length = 1, + sec.tick.length = 0.75, + corner.text = "", + amplif.legend = 1, + corner.text.size = 0.75, + trim.return = FALSE +){ + # AIM + # trim and display values from a numeric vector or matrix + # plot 4 graphs: stripchart of values, stripchart of rank of values, histogram and normal QQPlot + # different kinds of intervals are displayed on the top of graphes to facilitate the analysis of the variable and a trimming setting + # the trimming interval chosen is displayed on top of graphs + # both trimmed and not trimmed values are returned in a list + # ARGUMENTS + # data: values to plot (either a numeric vector or a numeric matrix) + # displayed.nb: number of values displayed. If NULL, all the values are displayed. Otherwise, if the number of values is over displayed.nb, then displayed.nb values are displayed after random selection + # single.value.display: provide the 4 graphs if data is made of a single (potentially repeated value)? If FALSE, an empty graph is displayed if data is made of a single (potentially repeated value). And the return list is made of NULL compartments + # trim.method: Write "" if not required. write "mean.sd" if mean +/- sd has to be displayed as a trimming interval (only recommanded for normal distribution). Write "quantile" to display a trimming interval based on quantile cut-offs. No other possibility allowed. See trim.cutoffs below + # trim.cutoffs: 2 values cutoff for the trimming interval displayed, each value between 0 and 1. Not used if trim.method == "".The couple of values c(lower, upper) represents the lower and upper boundaries of the trimming interval (in proportion), which represent the interval of distribution kept (between 0 and 1). Example: trim.cutoffs = c(0.05, 0.975). What is strictly kept for the display is ]lower , upper[, boundaries excluded. Using the "mean.sd" method, 0.025 and 0.975 represent 95% CI which is mean +/- 1.96 * sd + # interval.scale.disp: display sd and quantiles intervals on top of graphs ? + # down.space: lower vertical margin (in inches, mai argument of par()) + # left.space: left horizontal margin (in inches, mai argument of par()) + # up.space: upper vertical margin between plot region and grapical window (in inches, mai argument of par()) + # right.space: right horizontal margin (in inches, mai argument of par()) + # orient: scale number orientation (las argument of par()). 0, always parallel to the axis; 1, always horizontal; 2, always perpendicular to the axis; 3, always vertical + # dist.legend: numeric value that moves axis legends away in inches (first number of mgp argument of par() but in inches thus / 0.2) + # box.type: bty argument of par(). Either "o", "l", "7", "c", "u", "]", the resulting box resembles the corresponding upper case letter. A value of "n" suppresses the box + # amplif.label: increase or decrease the size of the text in legends + # amplif.axis: increase or decrease the size of the scale numbers in axis + # std.x.range: standard range on the x-axis? TRUE (no range extend) or FALSE (4% range extend). Controls xaxs argument of par() (TRUE is xaxs = "i", FALSE is xaxs = "r") + # std.y.range: standard range on the y-axis? TRUE (no range extend) or FALSE (4% range extend). Controls yaxs argument of par() (TRUE is yaxs = "i", FALSE is yaxs = "r") + # cex.pt: size of points in stripcharts (in inches, thus cex.pt will be thereafter / 0.2) + # col.box: color of boxplot + # x.nb.inter.tick: number of secondary ticks between main ticks on x-axis (only if not log scale). Zero means non secondary ticks + # y.nb.inter.tick: number of secondary ticks between main ticks on y-axis (only if not log scale). Zero means non secondary ticks + # tick.length: length of the ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc. 0 means no tick + # sec.tick.length: length of the secondary ticks (1 means complete the distance between the plot region and the axis numbers, 0.5 means half the length, etc., 0 for no ticks) + # corner.text: text to add at the top right corner of the window + # amplif.legend: increase or decrease the size of the text of legend + # corner.text.size: positive numeric. Increase or decrease the size of the text. Value 1 does not change it, 0.5 decreases by half, 2 increases by 2 + # trim.return: return the trimmed and non trimmed values? NULL returned for trimmed and non trimmed values if trim.method == "" + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # RETURN + # a list containing: + # $trim.method: correspond to trim.method above + # $trim.cutoffs: correspond to trim.cutoffs above + # $real.trim.cutoffs: the two boundary values (in the unit of the numeric vector or numeric matrix analyzed). NULL + # $trimmed.values: the values outside of the trimming interval as defined in trim.cutoffs above + # $kept.values: the values inside the trimming interval as defined in trim.cutoffs above + # EXAMPLES + # fun_trim(data = c(1:100, 1:10), displayed.nb = NULL, single.value.display = FALSE, trim.method = "mean.sd", trim.cutoffs = c(0.05, 0.975), interval.scale.disp = TRUE, down.space = 0.75, left.space = 0.75, up.space = 0.3, right.space = 0.25, orient = 1, dist.legend = 0.37, box.type = "l", amplif.label = 1.25, amplif.axis = 1.25, std.x.range = TRUE, std.y.range = TRUE, cex.pt = 0.2, col.box = hsv(0.55, 0.8, 0.8), x.nb.inter.tick = 4, y.nb.inter.tick = 0, tick.length = 0.5, sec.tick.length = 0.3, corner.text = "", amplif.legend = 1, corner.text.size = 0.75, trim.return = TRUE) + # DEBUGGING + # data = c(1:100, 1:10) ; displayed.nb = NULL ; single.value.display = FALSE ; trim.method = "quantile" ; trim.cutoffs = c(0.05, 0.975) ; interval.scale.disp = TRUE ; down.space = 1 ; left.space = 1 ; up.space = 0.5 ; right.space = 0.25 ; orient = 1 ; dist.legend = 0.5 ; box.type = "l" ; amplif.label = 1 ; amplif.axis = 1 ; std.x.range = TRUE ; std.y.range = TRUE ; cex.pt = 0.1 ; col.box = hsv(0.55, 0.8, 0.8) ; x.nb.inter.tick = 4 ; y.nb.inter.tick = 0 ; tick.length = 0.5 ; sec.tick.length = 0.3 ; corner.text = "" ; amplif.legend = 1 ; corner.text.size = 0.75 ; trim.return = TRUE # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + # argument checking without fun_check() + if( ! (all(class(data) == "numeric") | all(class(data) == "integer") | (all(class(data) %in% c("matrix", "array")) & base::mode(data) == "numeric"))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data ARGUMENT MUST BE A NUMERIC VECTOR OR NUMERIC MATRIX") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking without fun_check() + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + if( ! is.null(displayed.nb)){ + tempo <- fun_check(data = displayed.nb, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(displayed.nb < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": displayed.nb ARGUMENT MUST BE A SINGLE INTEGER VALUE GREATER THAN 1 AND NOT: ", paste(displayed.nb, collapse = " ")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = single.value.display, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = trim.method, options = c("", "mean.sd", "quantile"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = trim.cutoffs, class = "vector", mode = "numeric", length = 2, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = interval.scale.disp, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = down.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = left.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = up.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = right.space, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = orient, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dist.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.type, options = c("o", "l", "7", "c", "u", "]", "n"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = amplif.label, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = amplif.axis, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = std.x.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = std.y.range, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = cex.pt, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = col.box, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.nb.inter.tick, class = "integer", length = 1, neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.nb.inter.tick, class = "integer", length = 1, neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = sec.tick.length, class = "vector", mode = "numeric", length = 1, prop = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = corner.text, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = amplif.legend, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = corner.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = trim.return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + if(all(is.na(data) | ! is.finite(data))){ + tempo.cat <- paste0("ERROR IN fun_trim FUNCTION\ndata ARGUMENT CONTAINS ONLY NA OR Inf") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end argument checking + # main code + if(all(class(data)%in% c("matrix", "array"))){ + data <- as.vector(data) + } + na.nb <- NULL + if(any(is.na(data))){ + na.nb <- sum(c(is.na(data))) + data <- data[ ! is.na(data)] + } + color.cut <- hsv(0.75, 1, 1) # color of interval selected + col.mean <- hsv(0.25, 1, 0.8) # color of interval using mean+/-sd + col.quantile <- "orange" # color of interval using quantiles + quantiles.selection <- c(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.975, 0.99) # quantiles used in axis to help for choosing trimming cutoffs + if(single.value.display == FALSE & length(unique(data)) == 1L){ + par(bty = "n", xaxt = "n", yaxt = "n", xpd = TRUE) + plot(1, pch = 16, col = "white", xlab = "", ylab = "") + text(x = 1, y = 1, paste0("No graphic displayed\nBecause data made of a single different value (", formatC(as.double(table(data))), ")"), cex = 2) + output <- list(trim.method = NULL, trim.cutoffs = NULL, real.trim.cutoffs = NULL, trimmed.values = NULL, kept.values = NULL) + }else{ + output <- list(trim.method = trim.method, trim.cutoffs = trim.cutoffs, real.trim.cutoffs = NULL, trimmed.values = NULL, kept.values = NULL) + fun.rug <- function(sec.tick.length.f = sec.tick.length, x.nb.inter.tick.f = x.nb.inter.tick, y.nb.inter.tick.f = y.nb.inter.tick){ + if(x.nb.inter.tick.f > 0){ + inter.tick.unit <- (par("xaxp")[2] - par("xaxp")[1]) / par("xaxp")[3] + par.ini <- par()[c("xpd", "tcl")] + par(xpd = FALSE) + par(tcl = -par()$mgp[2] * sec.tick.length.f) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) + suppressWarnings(rug(seq(par("xaxp")[1] - 10 * inter.tick.unit, par("xaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + x.nb.inter.tick.f)), ticksize = NA, side = 1)) # ticksize = NA to allow the use of par()$tcl value + par(par.ini) + rm(par.ini) + } + if(y.nb.inter.tick.f > 0){ + inter.tick.unit <- (par("yaxp")[2] - par("yaxp")[1]) / par("yaxp")[3] + par.ini <- par()[c("xpd", "tcl")] + par(xpd = FALSE) + par(tcl = -par()$mgp[2] * sec.tick.length.f) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) + suppressWarnings(rug(seq(par("yaxp")[1] - 10 * inter.tick.unit, par("yaxp")[2] + 10 * inter.tick.unit, by = inter.tick.unit / (1 + y.nb.inter.tick.f)), ticksize = NA, side = 2)) # ticksize = NA to allow the use of par()$tcl value + par(par.ini) + rm(par.ini) + } + } + fun.add.cut <- function(data.f, trim.method.f = trim.method, trim.cutoffs.f = trim.cutoffs, color.cut.f = color.cut, return.f = FALSE){ + # DEBUGGING + # data.f = data ; trim.method.f = "mean.sd"; trim.cutoffs.f = trim.cutoffs ; color.cut.f = color.cut ; return.f = TRUE + real.trim.cutoffs.f <- NULL + if(trim.method.f != ""){ + data.f <- sort(data.f) + par.ini <- par()$xpd + par(xpd = FALSE) + if(trim.method.f == "mean.sd"){ + real.trim.cutoffs.f <- qnorm(trim.cutoffs.f, mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)) + abline(v = qnorm(trim.cutoffs.f, mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), col = color.cut.f) + segments(qnorm(trim.cutoffs.f[1], mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), par()$usr[4] * 0.75, qnorm(trim.cutoffs.f[2], mean(data.f, na.rm = TRUE), sd(data.f, na.rm = TRUE)), par()$usr[4] * 0.75, col = color.cut.f) + } + if(trim.method.f == "quantile"){ + real.trim.cutoffs.f <- quantile(data.f, probs = trim.cutoffs.f, type = 7, na.rm = TRUE) + abline(v = quantile(data.f, probs = trim.cutoffs.f, type = 7, na.rm = TRUE), col = color.cut.f) + segments(quantile(data.f, probs = trim.cutoffs.f[1], type = 7, na.rm = TRUE), par()$usr[4] * 0.75, quantile(data.f, probs = trim.cutoffs.f[2], type = 7, na.rm = TRUE), par()$usr[4] * 0.75, col = color.cut.f) + } + par(par.ini) + if(return.f == TRUE){ + trimmed.values.f <- data.f[data.f <= real.trim.cutoffs.f[1] | data.f >= real.trim.cutoffs.f[2]] + kept.values.f <- data.f[data.f > real.trim.cutoffs.f[1] & data.f < real.trim.cutoffs.f[2]] + } + }else{ + real.trim.cutoffs.f <- NULL + trimmed.values.f <- NULL + kept.values.f <- NULL + } + if(return.f == TRUE){ + output <- list(trim.method = trim.method.f, trim.cutoffs = trim.cutoffs.f, real.trim.cutoffs = real.trim.cutoffs.f, trimmed.values = trimmed.values.f, kept.values = kept.values.f) + return(output) + } + } + fun.interval.scale.display <- function(data.f, col.quantile.f = col.quantile, quantiles.selection.f = quantiles.selection, col.mean.f = col.mean){ # intervals on top of graphs + par.ini <- par()[c("mgp", "xpd")] + par(mgp = c(0.25, 0.25, 0), xpd = NA) + axis(side = 3, at = c(par()$usr[1], par()$usr[2]), labels = rep("", 2), col = col.quantile.f, lwd.ticks = 0) + par(xpd = FALSE) + axis(side = 3, at = quantile(as.vector(data.f), probs = quantiles.selection.f, type = 7, na.rm = TRUE), labels = quantiles.selection.f, col.axis = col.quantile.f, col = col.quantile.f) + par(mgp = c(1.75, 1.75, 1.5), xpd = NA) + axis(side = 3, at = c(par()$usr[1], par()$usr[2]), labels = rep("", 2), col = col.mean.f, lwd.ticks = 0) + par(xpd = FALSE) + axis(side = 3, at = m + s * qnorm(quantiles.selection.f), labels = formatC(round(qnorm(quantiles.selection.f), 2)), col.axis = col.mean.f, col = col.mean.f, lwd.ticks = 1) + par(par.ini) + } + zone<-matrix(1:4, ncol=2) + layout(zone) + par(omi = c(0, 0, 1.5, 0), mai = c(down.space, left.space, up.space, right.space), las = orient, mgp = c(dist.legend / 0.2, 0.5, 0), xpd = FALSE, bty= box.type, cex.lab = amplif.label, cex.axis = amplif.axis, xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) + par(tcl = -par()$mgp[2] * tick.length) # tcl gives the length of the ticks as proportion of line text, knowing that mgp is in text lines. So the main ticks are a 0.5 of the distance of the axis numbers by default. The sign provides the side of the tick (negative for outside of the plot region) + if(is.null(displayed.nb)){ + sampled.data <- as.vector(data) + if(corner.text == ""){ + corner.text <- paste0("ALL VALUES OF THE DATASET DISPLAYED") + }else{ + corner.text <- paste0(corner.text, "\nALL VALUES OF THE DATASET DISPLAYED") + } + }else{ + if(length(as.vector(data)) > displayed.nb){ + sampled.data <- sample(as.vector(data), displayed.nb, replace = FALSE) + if(corner.text == ""){ + corner.text <- paste0("WARNING: ONLY ", displayed.nb, " VALUES ARE DISPLAYED AMONG THE ", length(as.vector(data)), " VALUES OF THE DATASET ANALYZED") + }else{ + corner.text <- paste0(corner.text, "\nWARNING: ONLY ", displayed.nb, " VALUES ARE DISPLAYED AMONG THE ", length(as.vector(data)), " VALUES OF THE DATASET ANALYZED") + } + }else{ + sampled.data <- as.vector(data) + if(corner.text == ""){ + corner.text <- paste0("WARNING: THE DISPLAYED NUMBER OF VALUES PARAMETER ", deparse(substitute(displayed.nb)), " HAS BEEN SET TO ", displayed.nb, " WHICH IS ABOVE THE NUMBER OF VALUES OF THE DATASET ANALYZED -> ALL VALUES DISPLAYED") + }else{ + corner.text <- paste0(corner.text, "\nWARNING: THE DISPLAYED NUMBER OF VALUES PARAMETER ", deparse(substitute(displayed.nb)), " HAS BEEN SET TO ", displayed.nb, " WHICH IS ABOVE THE NUMBER OF VALUES OF THE DATASET ANALYZED -> ALL VALUES DISPLAYED") + } + } + } + if( ! is.null(na.nb)){ + if(corner.text == ""){ + corner.text <- paste0("WARNING: NUMBER OF NA REMOVED IS ", na.nb) + }else{ + corner.text <- paste0("WARNING: NUMBER OF NA REMOVED IS ", na.nb) + } + } + stripchart(sampled.data, method="jitter", jitter=0.4, vertical=FALSE, ylim=c(0.5, 1.5), group.names = "", xlab = "Value", ylab="", pch=1, cex = cex.pt / 0.2) + fun.rug(y.nb.inter.tick.f = 0) + boxplot(as.vector(data), horizontal=TRUE, add=TRUE, boxwex = 0.4, staplecol = col.box, whiskcol = col.box, medcol = col.box, boxcol = col.box, range = 0, whisklty = 1) + m <- mean(as.vector(data), na.rm = TRUE) + s <- sd(as.vector(data), na.rm = TRUE) + segments(m, 0.8, m, 1, lwd=2, col="red") # mean + segments(m -1.96 * s, 0.9, m + 1.96 * s, 0.9, lwd=1, col="red") # mean + graph.xlim <- par()$usr[1:2] # for hist() and qqnorm() below + if(interval.scale.disp == TRUE){ + fun.interval.scale.display(data.f = data) + if(corner.text == ""){ + corner.text <- paste0("MULTIPLYING FACTOR DISPLAYED (MEAN +/- SD) ON SCALES: ", paste(formatC(round(qnorm(quantiles.selection), 2))[-(1:(length(quantiles.selection) - 1) / 2)], collapse = ", "), "\nQUANTILES DISPLAYED ON SCALES: ", paste(quantiles.selection, collapse = ", ")) + }else{ + corner.text <- paste0(corner.text, "\nMULTIPLYING FACTOR DISPLAYED (MEAN +/- SD) ON SCALES: ", paste(formatC(round(qnorm(quantiles.selection), 2))[-(1:(length(quantiles.selection) - 1) / 2)], collapse = ", "), "\nQUANTILES DISPLAYED ON SCALES: ", paste(quantiles.selection, collapse = ", ")) + } + } + output.tempo <- fun.add.cut(data.f = data, return.f = TRUE) # to recover real.trim.cutoffs + if(trim.return == TRUE){ + output <- output.tempo + } + par(xpd = NA) + if(trim.method != ""){ + if(corner.text == ""){ + corner.text <- paste0("SELECTED CUT-OFFS (PROPORTION): ", paste(trim.cutoffs, collapse = ", "), "\nSELECTED CUT-OFFS: ", paste(output.tempo$real.trim.cutoffs, collapse = ", ")) + }else{ + corner.text <- paste0(corner.text, "\nSELECTED CUT-OFFS (PROPORTION): ", paste(trim.cutoffs, collapse = ", "), "\nSELECTED CUT-OFFS: ", paste(output.tempo$real.trim.cutoffs, collapse = ", ")) + } + if(interval.scale.disp == TRUE){ + legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- 1.96sd", paste0("Trimming interval: ", paste0(trim.cutoffs, collapse = " , ")), "Mean +/- sd multiplying factor", "Quantile"), yjust = 0, lty=1, col=c(col.box, "red", color.cut, col.mean, col.quantile), bty="n", cex = amplif.legend) + }else{ + legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- 1.96sd", paste0("Trimming interval: ", paste0(trim.cutoffs, collapse = " , "))), yjust = 0, lty=1, col=c(col.box, "red", color.cut), bty="n", cex = amplif.legend, y.intersp=1.25) + } + }else{ + if(interval.scale.disp == TRUE){ + legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- sd", "Mean +/- sd multiplying factor", "Quantile"), yjust = 0, lty=1, col=c(col.box, "red", col.mean, col.quantile), bty="n", cex = amplif.legend) + }else{ + legend(x = (par("usr")[1] - ((par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1])) * par("plt")[1] - ((par("usr")[2] - par("usr")[1]) / (par("omd")[2] - par("omd")[1])) * par("omd")[1]), y = (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / (par("omd")[4] - par("omd")[3])) * (1 - par("omd")[4]) / 2), legend = c(c("min, Q1, Median, Q3, max"), "mean +/- sd"), yjust = 0, lty=1, col=c(col.box, "red"), bty="n", cex = amplif.legend, y.intersp=1.25) + } + } + par(xpd = FALSE, xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) + hist(as.vector(data), main = "", xlim = graph.xlim, xlab = "Value", ylab="Density", col = grey(0.25)) # removed: breaks = seq(min(as.vector(data), na.rm = TRUE), max(as.vector(data), na.rm = TRUE), length.out = length(as.vector(data)) / 10) + abline(h = par()$usr[3]) + fun.rug() + if(interval.scale.disp == TRUE){ + fun.interval.scale.display(data.f = data) + } + fun.add.cut(data.f = data) + par(xaxs = ifelse(std.x.range, "i", "r")) + stripchart(rank(sampled.data), method="stack", vertical=FALSE, ylim=c(0.99, 1.3), group.names = "", xlab = "Rank of values", ylab="", pch=1, cex = cex.pt / 0.2) + fun.rug(y.nb.inter.tick.f = 0) + x.text <- par("usr")[2] + (par("usr")[2] - par("usr")[1]) / (par("plt")[2] - par("plt")[1]) * (1 - par("plt")[2]) / 2 + y.text <- (par("usr")[4] + ((par("usr")[4] - par("usr")[3]) / (par("plt")[4] - par("plt")[3])) * (1 - par("plt")[4]) + ((par("usr")[4] - par("usr")[3]) / ((par()$omd[4] / 2) * ((par("plt")[4] - par("plt")[3])))) * (1 - par("omd")[4])) # BEWARE. Here in "(par()$omd[4] / 2", division by two because there are 2 graphs staked on the y axis, and not one + par(xpd=NA) + text(x = x.text, y = y.text, paste0(corner.text), adj=c(1, 1.1), cex = corner.text.size) # text at the topright corner + par(xpd=FALSE) + par(xaxs = ifelse(std.x.range, "i", "r"), yaxs = ifelse(std.y.range, "i", "r")) + qqnorm(as.vector(sampled.data), main = "", datax = TRUE, ylab = "Value", pch = 1, col = "red", cex = cex.pt / 0.2) + fun.rug() + if(diff(quantile(as.vector(data), probs = c(0.25, 0.75), na.rm = TRUE)) != 0){ # otherwise, error generated + qqline(as.vector(data), datax = TRUE) + } + if(interval.scale.disp == TRUE){ + fun.interval.scale.display(data.f = data) + } + fun.add.cut(data.f = data) + } + if(trim.return == TRUE){ + return(output) + } } -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(alpha[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, with different values, breaks reorder the classes according to class.categ in the legend + + +######## fun_segmentation() #### segment a dot cloud on a scatterplot and define the dots from another cloud outside the segmentation + + +fun_segmentation <- function( + data1, + x1, + y1, + x.range.split = NULL, + x.step.factor = 10, + y.range.split = NULL, + y.step.factor = 10, + error = 0, + data2 = NULL, + x2, + y2, + data2.pb.dot = "unknown", + xy.cross.kind = "&", + plot = FALSE, + graph.in.file = FALSE, + raster = TRUE, + warn.print = FALSE, + lib.path = NULL +){ + # AIM + # if data1 is a data frame corresponding to the data set of a scatterplot (with a x column for x-axis values and a y column for the y-axis column), then fun_segmentation() delimits a frame around the dots cloud using a sliding window set by x.range.split and x.step.factor to frame the top and bottom part of the cloud, and set by y.range.split and y.step.factor to frame the left and right part of the cloud + # if a second data frame is provided, corresponding to the data set of a scatterplot (with a x column for x-axis values and a y column for the y-axis column), then fun_segmentation() defines the dots of this data frame, outside of the frame of the first data frame + # WARNINGS + # if dots from data2 look significant on the graph (outside the frame) but are not (not black on the last figure), this is probably because the frame is flat on the zero coordinate (no volume inside the frame at this position). Thus, no way to conclude that data2 dots here are significant. These dots are refered to as "unknown". The pb.dot argument deals with such dots + # dots that are sometimes inside and outside the frame, depending on the sliding window, are treated differently: they are removed. Such dots are neither classified as "signif", "non signif" or "unknown", but as "inconsistent" + # unknown dots are treated as finally significant, not significant, or unknown (data2.pb.dot argument) for each x-axis and y-axis separately. Then, the union or intersection of significant dots is performed (argument xy.cross.kind). See the example section + # ARGUMENTS + # data1: a data frame containing a column of x-axis values and a column of y-axis values + # x1: character string of the data1 column name for x-axis (first column of data1 by default) + # y1: character string of the data1 column name for y-axis (second column of data1 by default) + # x.range.split: positive non null numeric value giving the number of interval on the x value range. if x.range is the range of the dots on the x-axis, then abs(diff(x.range) / x.range.split) gives the window size. Window size decreases when range.split increases. In unit of x-axis. Write NULL if not required. At least one of the x.range.split and y.range.split must be non NULL + # x.step.factor: positive non null numeric value giving the shift step of the window. If x.step.factor = 1, no overlap during the sliding (when the window slides from position n to position n+1, no overlap between the two positions). If x.step.factor = 2, 50% of overlap (when the window slides from position n to position n+1, the window on position n+1 overlap 50% of the window when it was on position n) + # y.range.split: same as x.range.split for the y-axis. At least one of the x.range.split and y.range.split must be non NULL + # y.step.factor: same as x.step.factor for the y-axis + # error: proportion (from 0 to 1) of false positives (i.e., proportion of dots from data1 outside of the frame). 0.05 means 5% of the dots from data1 outside of the frame + # data2: a data frame containing a column of x-axis values and a column of y-axis values, for which outside dots of the data1 cloud has to be determined. Write NULL if not required + # x2: character string of the data1 column name for x-axis (first column of data1 by default) + # y2: character string of the data1 column name for y-axis (second column of data1 by default) + # data2.pb.dot: unknown dots are explain in the warning section above. If "signif", then the unknown dots are finally considered as significant (outside the frame). If "not.signif", then the unknown dots are finally considered as non significant (inside the frame). If "unknown", no conclusion are drawn from these dots. See the examples below + # xy.cross.kind: if data2 is non null and if both x.range.split and y.range.split are non null, which dots are finally significants? Write "&" for intersection of outside dots on x and on y. Write "|" for union of outside dots on x and on y. See the examples below + # plot: logical. Print graphs that check the frame? + # graph.in.file: logical. Graphs sent into a graphic device already opened? If FALSE, GUI are opened for each graph. If TRUE, no GUI are opended. The graphs are displayed on the current active graphic device. Ignored if plot is FALSE + # raster: logical. Dots in raster mode? If FALSE, dots from each geom_point from geom argument are in vectorial mode (bigger pdf and long to display if millions of dots). If TRUE, dots from each geom_point from geom argument are in matricial mode (smaller pdf and easy display if millions of dots, but long to generate the layer). If TRUE, the region plot will be square to avoid a bug in fun_gg_point_rast(). If TRUE, solve the transparency problem with some GUI. Not considered if plot is FALSE + # warn.print: logical. Print warnings at the end of the execution? No print if no warning messages + # lib.path: character vector specifying the absolute pathways of the directories containing the required packages if not in the default directories. Ignored if NULL. Ignored if plot is FALSE + # RETURN + # several graphs if plot is TRUE + # a list containing: + # $data1.removed.row.nb: which rows have been removed due to NA; NaN, -Inf or Inf detection in x1 or y1 columns (NULL if no row removed) + # $data1.removed.rows: removed rows (NULL if no row removed) + # $data2.removed.row.nb: which rows have been removed due to NA; NaN, -Inf or Inf detection in x2 or y2 columns (NULL if no row removed) + # $data2.removed.rows: removed rows (NULL if no row removed) + # $hframe: x and y coordinates of the bottom and top frames for frame plotting (frame1 for the left step and frame2 for the right step) + # $vframe: x and y coordinates of the left and right frames for frame plotting (frame1 for the down step and frame2 for the top step) + # $data1.signif.dot: the significant dots of data1 (i.e., dots outside the frame). A good segmentation should not have any data1.signif.dot + # $data1.non.signif.dot: the non significant dots of data1 (i.e., dots inside the frame) + # $data1.inconsistent.dot: see the warning section above + # $data2.signif.dot: the significant dots of data2 if non NULL (i.e., dots outside the frame) + # $data2.non.signif.dot: the non significant dots of data2 (i.e., dots inside the frame) + # $data2.unknown.dot: the problematic dots of data2 (i.e., data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots). Is systematically NULL except if argument data2.pb.dot = "unknown" and some data2 dots are in such situation. Modifying the segmentation x.range.split, x.step.factor, y.range.split, y.step.factor arguments can solve this problem + # $data2.inconsistent.dot: see the warning section above + # $axes: the x-axis and y-axis info + # $warn: the warning messages. Use cat() for proper display. NULL if no warning + # REQUIRED PACKAGES + # ggplot2 if plot is TRUE + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # if plot is TRUE: + # fun_pack() + # fun_open() + # fun_gg_palette() + # fun_gg_scatter() + # fun_gg_empty_graph() + # fun_close() + # EXAMPLES + # example explaining the unknown and inconsistent dots, and the cross + + # set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data1[5:7, 2] <- NA ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; data2[11:13, 1] <- Inf ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = 20, x.step.factor = 10, y.range.split = 23, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "not.signif", xy.cross.kind = "|", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) + # set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = NULL, x.step.factor = 10, y.range.split = 23, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "unknown", xy.cross.kind = "|", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) + # set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_segmentation(data1 = data1, x1 = names(data1)[1], y1 = names(data1)[2], x.range.split = 20, x.step.factor = 10, y.range.split = NULL, y.step.factor = 10, error = 0, data2 = data2, x2 = names(data2)[1], y2 = names(data2)[2], data2.pb.dot = "unknown", xy.cross.kind = "&", plot = TRUE, graph.in.file = FALSE, raster = FALSE, lib.path = NULL) + # DEBUGGING + # set.seed(1) ; data1 = data.frame(x = rnorm(50), y = rnorm(50), stringsAsFactors = TRUE) ; data1[5:7, 2] <- NA ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 5 ; x.step.factor = 10 ; y.range.split = 5 ; y.step.factor = 10 ; error = 0 ; data2 = data.frame(x = rnorm(50, 0, 2), y = rnorm(50, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "unknown" ; xy.cross.kind = "|" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL + # set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 20 ; x.step.factor = 10 ; y.range.split = 23 ; y.step.factor = 10 ; error = 0 ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "not.signif" ; xy.cross.kind = "|" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL + # set.seed(1) ; data1 = data.frame(x = rnorm(500), y = rnorm(500), stringsAsFactors = TRUE) ; data2 = data.frame(x = rnorm(500, 0, 2), y = rnorm(500, 0, 2), stringsAsFactors = TRUE) ; set.seed(NULL) ; x1 = names(data1)[1] ; y1 = names(data1)[2] ; x.range.split = 20 ; x.step.factor = 10 ; y.range.split = NULL ; y.step.factor = 10 ; error = 0 ; x2 = names(data2)[1] ; y2 = names(data2)[2] ; data2.pb.dot = "unknown" ; xy.cross.kind = "&" ; plot = TRUE ; graph.in.file = FALSE ; raster = FALSE ; warn.print = TRUE ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + ini.warning.length <- options()$warning.length + warn <- NULL + warn.count <- 0 + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & length(data1) < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OF AT LEAST 2 COLUMNS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = x1, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (x1 %in% names(data1))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x1 ARGUMENT MUST BE A COLUMN NAME OF data1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & x1 %in% names(data1)){ + tempo <- fun_check(data = data1[, x1], data.name = "x1 COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = y1, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (y1 %in% names(data1))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y1 ARGUMENT MUST BE A COLUMN NAME OF data1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & y1 %in% names(data1)){ + tempo <- fun_check(data = data1[, y1], data.name = "y1 COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + if(is.null(x.range.split) & is.null(y.range.split)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE OF THE x.range.split AND y.range.split ARGUMENTS MUST BE NON NULL") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(x.range.split)){ + tempo <- fun_check(data = x.range.split, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & x.range.split < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.range.split ARGUMENT CANNOT BE LOWER THAN 1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + if( ! is.null(y.range.split)){ + tempo <- fun_check(data = y.range.split, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & y.range.split < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.range.split ARGUMENT CANNOT BE LOWER THAN 1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = x.step.factor, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & x.step.factor < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.step.factor ARGUMENT CANNOT BE LOWER THAN 1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = y.step.factor, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & y.step.factor < 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.step.factor ARGUMENT CANNOT BE LOWER THAN 1") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = error, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(data2)){ + if(is.null(x2) | is.null(y2)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x2 AND y2 ARGUMENTS CANNOT BE NULL IF data2 ARGUMENT IS NON NULL") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = data2, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & length(data2) < 2){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data2 ARGUMENT MUST BE A DATA FRAME OF AT LEAST 2 COLUMNS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(x2)){ + tempo <- fun_check(data = x2, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (x2 %in% names(data2))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x2 ARGUMENT MUST BE A COLUMN NAME OF data2") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & x2 %in% names(data2)){ + tempo <- fun_check(data = data2[, x2], data.name = "x2 COLUMN OF data2", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + } + if( ! is.null(y2)){ + tempo <- fun_check(data = y2, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & ! (y2 %in% names(data2))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y2 ARGUMENT MUST BE A COLUMN NAME OF data2") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & y2 %in% names(data2)){ + tempo <- fun_check(data = data2[, y2], data.name = "y2 COLUMN OF data2", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) ; eval(ee) + } + } + } + if( ! is.null(data2)){ + tempo <- fun_check(data = data2.pb.dot, options = c("signif", "not.signif", "unknown"), length = 1, fun.name = function.name) ; eval(ee) + } + if( ! (is.null(x.range.split)) & ! (is.null(y.range.split))){ + tempo <- fun_check(data = xy.cross.kind, options = c("&", "|"), length = 1, fun.name = function.name) ; eval(ee) + } + tempo <- fun_check(data = plot, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & plot == TRUE){ + tempo <- fun_check(data = raster, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = graph.in.file, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & graph.in.file == TRUE & is.null(dev.list())){ + tempo.cat <- paste0("ERROR IN ", function.name, ": \ngraph.in.file PARAMETER SET TO TRUE BUT NO ACTIVE GRAPHIC DEVICE DETECTED") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo$problem == FALSE & graph.in.file == TRUE & ! is.null(dev.list())){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHS PRINTED IN THE CURRENT DEVICE (TYPE ", toupper(names(dev.cur())), ")") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # other required function checking + if(plot == TRUE){ + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_open", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_open() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_gg_palette", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_palette() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_gg_empty_graph", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_empty_graph() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_gg_scatter", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_gg_scatter() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_close", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_close() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end other required function checking + # package checking + if(plot == TRUE){ + fun_pack(req.package = c("ggplot2"), lib.path = lib.path) + } + # end package checking + # main code + # na and Inf detection and removal (done now to be sure of the correct length of categ) + data1.removed.row.nb <- NULL + data1.removed.rows <- NULL + data2.removed.row.nb <- NULL + data2.removed.rows <- NULL + if(any(is.na(data1[, c(x1, y1)])) | any(is.infinite(data1[, x1])) | any(is.infinite(data1[, y1]))){ + tempo.na <- unlist(lapply(lapply(c(data1[c(x1, y1)]), FUN = is.na), FUN = which)) + tempo.inf <- unlist(lapply(lapply(c(data1[c(x1, y1)]), FUN = is.infinite), FUN = which)) + data1.removed.row.nb <- sort(unique(c(tempo.na, tempo.inf))) + if(length(data1.removed.row.nb) > 0){ + data1.removed.rows <- data1[data1.removed.row.nb, ] + } + if(length(data1.removed.row.nb) == nrow(data1)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE NA, NaN, -Inf OR Inf DETECTED IN EACH ROW OF data1. FUNCTION CANNOT BE USED ON EMPTY DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(data1.removed.row.nb) > 0){ + data1 <- data1[-data1.removed.row.nb, ] + } + if(nrow(data1) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x1, y1), collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $data1.removed.row.nb AND $data1.removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x1, y1), collapse = " "), " OF data1. NO ROW REMOVED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data2)){ + if(any(is.na(data2[, c(x2, y2)])) | any(is.infinite(data2[, x2])) | any(is.infinite(data2[, y2]))){ + tempo.na <- unlist(lapply(lapply(c(data2[c(x2, y2)]), FUN = is.na), FUN = which)) + tempo.inf <- unlist(lapply(lapply(c(data2[c(x2, y2)]), FUN = is.infinite), FUN = which)) + data2.removed.row.nb <- sort(unique(c(tempo.na, tempo.inf))) + if(length(data2.removed.row.nb) > 0){ + data2.removed.rows <- data2[data2.removed.row.nb, ] + } + if(length(data2.removed.row.nb) == nrow(data2)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": AT LEAST ONE NA, NaN, -Inf OR Inf DETECTED IN EACH ROW OF data2. FUNCTION CANNOT BE USED ON EMPTY DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(data2.removed.row.nb) > 0){ + data2 <- data2[-data2.removed.row.nb, ] + } + if(nrow(data2) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x2, y2), collapse = " "), " OF data2 AND CORRESPONDING ROWS REMOVED (SEE $data2.removed.row.nb AND $data2.removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NO NA, NaN, -Inf OR Inf DETECTED IN COLUMN ", paste(c(x2, y2), collapse = " "), " OF data2. NO ROW REMOVED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end na and Inf detection and removal (done now to be sure of the correct length of categ) + # row annotation (dot number) + # data1 <- data1[ ! duplicated(data1[, c(x1, y1)]), ] # do not remove the dots that have same x and y values, because they will have different dot number -> not the same position on the matrices (so true for symmetric matrices) + data1 <- cbind(data1, DOT_NB = 1:nrow(data1), stringsAsFactors = TRUE) + if( ! is.null(data2)){ + # data2 <- data2[ ! duplicated(data2[, c(x2, y2)]), ] # do not remove the dots that have same x and y values, because they will have different dot number -> not the same position on the matrices (so true for symmetric matrices) + data2 <- cbind(data2, DOT_NB = 1:nrow(data2), stringsAsFactors = TRUE) + } + # end row annotation (dot number) + + + + + # Method using x unit interval + # may be create vector of each column to increase speed + x.data1.l <- NULL # x coord of the y upper and lower limits defined on the data1 cloud for left step line + x.data1.r <- NULL # x coord of the y upper and lower limits defined on the data1 cloud for right step line + y.data1.down.limit.l <- NULL # lower limit of the data1 cloud for left step line + y.data1.top.limit.l <- NULL # upper limit of the data1 cloud for left step line + y.data1.down.limit.r <- NULL # lower limit of the data1 cloud for right step line + y.data1.top.limit.r <- NULL # upper limit of the data1 cloud for left step line + if(any(data1[, x1] %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE x1 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + x.range <- range(data1[, x1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + if(suppressWarnings(any(x.range %in% c(Inf, -Inf)))){ + tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED x.range CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1[, y1] %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE data1 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE y1 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + y.range <- range(data1[, y1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + if(suppressWarnings(any(x.range %in% c(Inf, -Inf)))){ + tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED y.range CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + x.range.plot <- range(data1[, x1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + y.range.plot <- range(data1[, y1], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + if( ! is.null(data2)){ + if(any(data2[, x2] %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE data2 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE x2 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + x.range.plot <- range(data1[, x1], data2[, x2], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + if(any(data2[, y2] %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE data2 ARGUMENT CONTAINS -Inf OR Inf VALUES IN THE y2 COLUMN, THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + y.range.plot <- range(data1[, y1], data2[, y2], na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + } + if(suppressWarnings(any(x.range.plot %in% c(Inf, -Inf)))){ + tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED x.range.plot CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 (AND data2?) ARGUMENTS ARE NA OR Inf ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(suppressWarnings(any(y.range.plot %in% c(Inf, -Inf)))){ + tempo.cat <- paste0("ERROR IN ", function.name, " COMPUTED y.range.plot CONTAINS Inf VALUES, BECAUSE VALUES FROM data1 (AND data2?) ARGUMENTS ARE NA OR Inf ONLY") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(x.range.split)){ + # data.frame ordering to slide the window from small to big values + sliding window definition + data1 <- data1[order(data1[, x1], na.last = TRUE), ] + if( ! is.null(data2)){ + data2 <- data2[order(data2[, x2], na.last = TRUE), ] + } + x.win.size <- abs(diff(x.range) / x.range.split) # in unit of x-axis + step <- x.win.size / x.step.factor + # end data.frame ordering to slide the window from small to big values + sliding window definition + # x-axis sliding and y-axis limits of the data1 cloud -> y significant data2 + loop.nb <- ceiling((diff(x.range) - x.win.size) / step) # x.win.size + n * step covers the x range if x.win.size + n * step >= diff(x.range), thus if n >= (diff(x.range) - x.win.size) / step + y.outside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are upper or lower than the frame + y.inside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are not upper or lower than the frame + y.data1.median <- median(data1[, y1], na.rm = TRUE) # will be used for sliding windows without data1 in it + if( ! is.null(data2)){ + y.outside.data2.dot.nb <- integer() # vector that will contain the selected 1D coordinates (i.e., dots) of data2 that are upper or lower than the data1 frame + y.inside.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are not upper or lower than the data1 frame + y.unknown.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are problematic: data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots + # recover data2 dots outside the range of data1 + if(any(data2[, x2] < x.range[1])){ + y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[data2[, x2] < x.range[1]]) + #tempo.warn & indicate the interval + } + if(any(data2[, x2] > x.range[2])){ + y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[data2[, x2] > x.range[2]]) + #tempo.warn & indicate the interval + } + # end recover data2 dots outside the range of data1 + } + # loop.ini.time <- as.numeric(Sys.time()) + for(i1 in 0:(loop.nb + 1)){ + min.pos <- x.range[1] + step * i1 # lower position of the sliding window in data1 + max.pos <- min.pos + x.win.size # upper position of the sliding window in data1 + x.data1.l <- c(x.data1.l, min.pos, min.pos + step) # min.pos + step to make the steps + x.data1.r <- c(x.data1.r, max.pos, max.pos + step) # max.pos + step to make the steps + x.data1.dot.here <- data1[, x1] >= min.pos & data1[, x1] < max.pos # is there data1 dot present in the sliding window, considering the x axis? + if( ! is.null(data2)){ + x.data2.dot.here <- data2[, x2] >= min.pos & data2[, x2] < max.pos # is there data2 dot present in the sliding window, considering the x axis? + } + # recover the data1 dots outside the frame + if(any(x.data1.dot.here == TRUE)){ + tempo.y.data1.top.limit <- quantile(data1[x.data1.dot.here, y1], probs = 1 - error, na.rm = TRUE) + tempo.y.data1.down.limit <- quantile(data1[x.data1.dot.here, y1], probs = 0 + error, na.rm = TRUE) + y.data1.top.limit.l <- c(y.data1.top.limit.l, tempo.y.data1.top.limit, tempo.y.data1.top.limit) + y.data1.down.limit.l <- c(y.data1.down.limit.l, tempo.y.data1.down.limit, tempo.y.data1.down.limit) + y.data1.top.limit.r <- c(y.data1.top.limit.r, tempo.y.data1.top.limit, tempo.y.data1.top.limit) + y.data1.down.limit.r <- c(y.data1.down.limit.r, tempo.y.data1.down.limit, tempo.y.data1.down.limit) + y.data1.dot.signif <- ( ! ((data1[, y1] <= tempo.y.data1.top.limit) & (data1[, y1] >= tempo.y.data1.down.limit))) & x.data1.dot.here # is there data1 dot present in the sliding window, above or below the data1 limits, considering the y axis? + y.data1.dot.not.signif <- x.data1.dot.here & ! y.data1.dot.signif + y.outside.data1.dot.nb <- c(y.outside.data1.dot.nb, data1$DOT_NB[y.data1.dot.signif]) # recover the row number of data1 + y.outside.data1.dot.nb <- unique(y.outside.data1.dot.nb) + y.inside.data1.dot.nb <- c(y.inside.data1.dot.nb, data1$DOT_NB[y.data1.dot.not.signif]) + y.inside.data1.dot.nb <- unique(y.inside.data1.dot.nb) + }else{ + y.data1.top.limit.l <- c(y.data1.top.limit.l, y.data1.median, y.data1.median) + y.data1.down.limit.l <- c(y.data1.down.limit.l, y.data1.median, y.data1.median) + y.data1.top.limit.r <- c(y.data1.top.limit.r, y.data1.median, y.data1.median) + y.data1.down.limit.r <- c(y.data1.down.limit.r, y.data1.median, y.data1.median) + } + # end recover the data1 dots outside the frame + # recover the data2 dots outside the frame + if( ! is.null(data2)){ + if(any(x.data1.dot.here == TRUE) & any(x.data2.dot.here == TRUE)){ + y.data2.dot.signif <- ( ! ((data2[, y2] <= tempo.y.data1.top.limit) & (data2[, y2] >= tempo.y.data1.down.limit))) & x.data2.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the y axis? + y.data2.dot.not.signif <- x.data2.dot.here & ! y.data2.dot.signif + y.outside.data2.dot.nb <- c(y.outside.data2.dot.nb, data2$DOT_NB[y.data2.dot.signif]) + y.outside.data2.dot.nb <- unique(y.outside.data2.dot.nb) + y.inside.data2.dot.nb <- c(y.inside.data2.dot.nb, data2$DOT_NB[y.data2.dot.not.signif]) + y.inside.data2.dot.nb <- unique(y.inside.data2.dot.nb) + }else if(any(x.data1.dot.here == FALSE) & any(x.data2.dot.here == TRUE)){ # problem: data2 dots in the the window but no data1 dots to generates the quantiles + y.unknown.data2.dot.nb <- c(y.unknown.data2.dot.nb, data2$DOT_NB[x.data2.dot.here]) + y.unknown.data2.dot.nb <- unique(y.unknown.data2.dot.nb) + #tempo.warn & indicate the interval + + + + + # tempo.warn <- paste0("FROM FUNCTION ", function.name, ": THE [", round(min.pos, 3), " ; ", round(max.pos, 3), "] INTERVAL DOES NOT CONTAIN data1 X VALUES BUT CONTAINS data2 X VALUES WHICH CANNOT BE EVALUATED.\nTHE CONCERNED data2 ROW NUMBERS ARE:\n", paste(which(x.data1.dot.here == FALSE & x.data2.dot.here == TRUE), collapse = "\n")) + # warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end recover the data2 dots outside the frame + # if(any(i1 == seq(1, loop.nb, 500))){ + # loop.fin.time <- as.numeric(Sys.time()) # time of process end + # cat(paste0("COMPUTATION TIME OF LOOP ", i1, " / ", loop.nb, ": ", as.character(lubridate::seconds_to_period(round(loop.fin.time - loop.ini.time))), "\n")) + # } + } + if(max.pos < x.range[2]){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE SLIDING WINDOW HAS NOT REACHED THE MAX VALUE OF data1 ON THE X-AXIS: ", max.pos, " VERSUS ", x.range[2]) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + y.incon.data1.dot.nb.final <- unique(c(y.outside.data1.dot.nb[y.outside.data1.dot.nb %in% y.inside.data1.dot.nb], y.inside.data1.dot.nb[y.inside.data1.dot.nb %in% y.outside.data1.dot.nb])) # inconsistent dots: if a row number of y.inside.data1.dot.nb is present in y.outside.data1.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list + y.outside.data1.dot.nb.final <- y.outside.data1.dot.nb[ ! (y.outside.data1.dot.nb %in% y.incon.data1.dot.nb.final)] # inconsistent dots removed from the outside list + y.inside.data1.dot.nb.final <- y.inside.data1.dot.nb[ ! (y.inside.data1.dot.nb %in% y.incon.data1.dot.nb.final)] # inconsistent dots removed from the inside list + if( ! is.null(data2)){ + # if some unknown dots are also inside, and/or outside, they are put in the inside and/or outside. Ok, because then the intersection between inside and outside is treated -> inconsistent dots + tempo.unknown.out <- y.unknown.data2.dot.nb[y.unknown.data2.dot.nb %in% y.outside.data2.dot.nb] + y.outside.data2.dot.nb <- unique(c(y.outside.data2.dot.nb, tempo.unknown.out)) # if a row number of y.unknown.data2.dot.nb is present in y.outside.data2.dot.nb, it is put into outside + tempo.unknown.in <- y.unknown.data2.dot.nb[y.unknown.data2.dot.nb %in% y.inside.data2.dot.nb] + y.inside.data2.dot.nb <- unique(c(y.inside.data2.dot.nb, tempo.unknown.in)) # if a row number of y.unknown.data2.dot.nb is present in y.inside.data2.dot.nb, it is put into inside + y.unknown.data2.dot.nb.final <- y.unknown.data2.dot.nb[ ! (y.unknown.data2.dot.nb %in% c(y.outside.data2.dot.nb, y.inside.data2.dot.nb))] # then dots also in inside and outside are remove from unknown + y.incon.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb[y.outside.data2.dot.nb %in% y.inside.data2.dot.nb], y.inside.data2.dot.nb[y.inside.data2.dot.nb %in% y.outside.data2.dot.nb])) # inconsistent dots: if a row number of y.inside.data2.dot.nb is present in y.outside.data2.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list + y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb[ ! (y.outside.data2.dot.nb %in% y.incon.data2.dot.nb.final)] # inconsistent dots removed from the outside list + y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb[ ! (y.inside.data2.dot.nb %in% y.incon.data2.dot.nb.final)] # inconsistent dots removed from the inside list + } + # end x-axis sliding and y-axis limits of the data1 cloud -> y significant data2 + } + # end Method using x unit interval + + + + + # Method using y unit interval + y.data1.d <- NULL # y coord of the x upper and lower limits defined on the data1 cloud for down step line + y.data1.t <- NULL # y coord of the x upper and lower limits defined on the data1 cloud for top step line + x.data1.left.limit.d <- NULL # left limit of the data1 cloud for down step line + x.data1.right.limit.d <- NULL # right limit of the data1 cloud for down step line + x.data1.left.limit.t <- NULL # left limit of the data1 cloud for top step line + x.data1.right.limit.t <- NULL # right limit of the data1 cloud for top step line + if( ! is.null(y.range.split)){ + # data.frame ordering to slide the window from small to big values + sliding window definition + data1 <- data1[order(data1[, y1], na.last = TRUE), ] + if( ! is.null(data2)){ + data2 <- data2[order(data2[, y2], na.last = TRUE), ] + } + y.win.size <- abs(diff(y.range) / y.range.split) # in unit of y-axis + step <- y.win.size / y.step.factor + # end data.frame ordering to slide the window from small to big values + sliding window definition + # y-axis sliding and x-axis limits of the data1 cloud -> x significant data2 + loop.nb <- ceiling((diff(y.range) - y.win.size) / step) # y.win.size + n * step covers the y range if y.win.size + n * step >= diff(y.range), thus if n >= (diff(y.range) - y.win.size) / step + x.outside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are upper or lower than the frame + x.inside.data1.dot.nb <- integer() # vector that will contain the selected rows numbers of data1 that are not upper or lower than the frame + x.data1.median <- median(data1[, x1], na.rm = TRUE) # will be used for sliding window without data1 in it + if( ! is.null(data2)){ + x.outside.data2.dot.nb <- integer() # vector that will contain the selected 1D coordinates (i.e., dots) of data2 that are upper or lower than the data1 frame + x.inside.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are not upper or lower than the data1 frame + x.unknown.data2.dot.nb <- integer() # vector that will contain the 1D coordinates (i.e., dots) of data2 that are problematic: data2 dots outside of the range of data1, or data2 dots in a sliding window without data1 dots + # recover data2 dots outside the range of data1 + if(any(data2[, y2] < y.range[1])){ + x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[data2[, y2] < y.range[1]]) + } + if(any(data2[, y2] > y.range[2])){ + x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[data2[, y2] > y.range[2]]) + } + # end recover data2 dots outside the range of data1 + } + # loop.ini.time <- as.numeric(Sys.time()) + for(i1 in 0:(loop.nb + 1)){ + min.pos <- y.range[1] + step * i1 # lower position of the sliding window in data1 + max.pos <- min.pos + y.win.size # upper position of the sliding window in data1 + y.data1.d <- c(y.data1.d, min.pos, min.pos + step) # min.pos + step to make the steps + y.data1.t <- c(y.data1.t, max.pos, max.pos + step) # max.pos + step to make the steps + y.data1.dot.here <- data1[, y1] >= min.pos & data1[, y1] < max.pos # is there data1 dot present in the sliding window, considering the y axis? + if( ! is.null(data2)){ + y.data2.dot.here <- data2[, y2] >= min.pos & data2[, y2] < max.pos # is there data2 dot present in the sliding window, considering the y axis? + } + # recover the data1 dots outside the frame + if(any(y.data1.dot.here == TRUE)){ + tempo.x.data1.right.limit <- quantile(data1[y.data1.dot.here, x1], probs = 1 - error, na.rm = TRUE) + tempo.x.data1.left.limit <- quantile(data1[y.data1.dot.here, x1], probs = 0 + error, na.rm = TRUE) + x.data1.right.limit.d <- c(x.data1.right.limit.d, tempo.x.data1.right.limit, tempo.x.data1.right.limit) + x.data1.left.limit.d <- c(x.data1.left.limit.d, tempo.x.data1.left.limit, tempo.x.data1.left.limit) + x.data1.right.limit.t <- c(x.data1.right.limit.t, tempo.x.data1.right.limit, tempo.x.data1.right.limit) + x.data1.left.limit.t <- c(x.data1.left.limit.t, tempo.x.data1.left.limit, tempo.x.data1.left.limit) + x.data1.dot.signif <- ( ! ((data1[, x1] <= tempo.x.data1.right.limit) & (data1[, x1] >= tempo.x.data1.left.limit))) & y.data1.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the x axis? + x.data1.dot.not.signif <- y.data1.dot.here & ! x.data1.dot.signif + x.outside.data1.dot.nb <- c(x.outside.data1.dot.nb, data1$DOT_NB[x.data1.dot.signif]) # recover the row number of data1 + x.outside.data1.dot.nb <- unique(x.outside.data1.dot.nb) + x.inside.data1.dot.nb <- c(x.inside.data1.dot.nb, data1$DOT_NB[x.data1.dot.not.signif]) + x.inside.data1.dot.nb <- unique(x.inside.data1.dot.nb) + }else{ + x.data1.right.limit.d <- c(x.data1.right.limit.d, x.data1.median, x.data1.median) + x.data1.left.limit.d <- c(x.data1.left.limit.d, x.data1.median, x.data1.median) + x.data1.right.limit.t <- c(x.data1.right.limit.t, x.data1.median, x.data1.median) + x.data1.left.limit.t <- c(x.data1.left.limit.t, x.data1.median, x.data1.median) + } + # end recover the data1 dots outside the frame + # recover the data2 dots outside the frame + if( ! is.null(data2)){ + if(any(y.data1.dot.here == TRUE) & any(y.data2.dot.here == TRUE)){ + x.data2.dot.signif <- ( ! ((data2[, x2] <= tempo.x.data1.right.limit) & (data2[, x2] >= tempo.x.data1.left.limit))) & y.data2.dot.here # is there data2 dot present in the sliding window, above or below the data1 limits, considering the x axis? + x.data2.dot.not.signif <- y.data2.dot.here & ! x.data2.dot.signif + x.outside.data2.dot.nb <- c(x.outside.data2.dot.nb, data2$DOT_NB[x.data2.dot.signif]) + x.outside.data2.dot.nb <- unique(x.outside.data2.dot.nb) + x.inside.data2.dot.nb <- c(x.inside.data2.dot.nb, data2$DOT_NB[x.data2.dot.not.signif]) + x.inside.data2.dot.nb <- unique(x.inside.data2.dot.nb) + }else if(any(y.data1.dot.here == FALSE) & any(y.data2.dot.here == TRUE)){ # recover the data2 dots outside the range of the data1 cloud + x.unknown.data2.dot.nb <- c(x.unknown.data2.dot.nb, data2$DOT_NB[y.data2.dot.here]) + x.unknown.data2.dot.nb <- unique(x.unknown.data2.dot.nb) + + + + # tempo.warn <- paste0("FROM FUNCTION ", function.name, ": THE [", round(min.pos, 3), " ; ", round(max.pos, 3), "] INTERVAL DOES NOT CONTAIN data1 Y VALUES BUT CONTAINS data2 Y VALUES WHICH CANNOT BE EVALUATED.\nTHE CONCERNED data2 ROW NUMBERS ARE:\n", paste(which(y.data1.dot.here == FALSE & y.data2.dot.here == TRUE), collapse = "\n")) + # warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end recover the data2 dots outside the frame + # if(any(i1 == seq(1, loop.nb, 500))){ + # loop.fin.time <- as.numeric(Sys.time()) # time of process end + # cat(paste0("COMPUTATION TIME OF LOOP ", i1, " / ", loop.nb, ": ", as.character(lubridate::seconds_to_period(round(loop.fin.time - loop.ini.time))), "\n")) + # } + } + if(max.pos < y.range[2]){ + tempo.cat <- paste0("ERROR IN ", function.name, ": THE SLIDING WINDOW HAS NOT REACHED THE MAX VALUE OF data1 ON THE Y-AXIS: ", max.pos, " VERSUS ", y.range[2]) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + x.incon.data1.dot.nb.final <- unique(c(x.outside.data1.dot.nb[x.outside.data1.dot.nb %in% x.inside.data1.dot.nb], x.inside.data1.dot.nb[x.inside.data1.dot.nb %in% x.outside.data1.dot.nb])) # inconsistent dots: if a row number of x.inside.data1.dot.nb is present in x.outside.data1.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list + x.outside.data1.dot.nb.final <- x.outside.data1.dot.nb[ ! (x.outside.data1.dot.nb %in% x.incon.data1.dot.nb.final)] # inconsistent dots removed from the outside list + x.inside.data1.dot.nb.final <- x.inside.data1.dot.nb[ ! (x.inside.data1.dot.nb %in% x.incon.data1.dot.nb.final)] # inconsistent dots removed from the inside list + if( ! is.null(data2)){ + # if some unknown dots are also inside, and/or outside, they are put in the inside and/or outside. Ok, because then the intersection between inside and outside is treated -> inconsistent dots + tempo.unknown.out <- x.unknown.data2.dot.nb[x.unknown.data2.dot.nb %in% x.outside.data2.dot.nb] + x.outside.data2.dot.nb <- unique(c(x.outside.data2.dot.nb, tempo.unknown.out)) # if a row number of x.unknown.data2.dot.nb is present in x.outside.data2.dot.nb, it is put into outside + tempo.unknown.in <- x.unknown.data2.dot.nb[x.unknown.data2.dot.nb %in% x.inside.data2.dot.nb] + x.inside.data2.dot.nb <- unique(c(x.inside.data2.dot.nb, tempo.unknown.in)) # if a row number of x.unknown.data2.dot.nb is present in x.inside.data2.dot.nb, it is put into inside + x.unknown.data2.dot.nb.final <- x.unknown.data2.dot.nb[ ! (x.unknown.data2.dot.nb %in% c(x.outside.data2.dot.nb, x.inside.data2.dot.nb))] # then dots also in inside and outside are remove from unknown + x.incon.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb[x.outside.data2.dot.nb %in% x.inside.data2.dot.nb], x.inside.data2.dot.nb[x.inside.data2.dot.nb %in% x.outside.data2.dot.nb])) # inconsistent dots: if a row number of x.inside.data2.dot.nb is present in x.outside.data2.dot.nb (and vice versa), it means that during the sliding, a dot has been sometime inside, sometime outside -> removed from the outside list + x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb[ ! (x.outside.data2.dot.nb %in% x.incon.data2.dot.nb.final)] # inconsistent dots removed from the outside list + x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb[ ! (x.inside.data2.dot.nb %in% x.incon.data2.dot.nb.final)] # inconsistent dots removed from the inside list + } + # end y-axis sliding and x-axis limits of the data1 cloud -> x significant data2 + } + # end Method using y unit interval + + + + # recovering the frame coordinates + hframe = rbind( + data.frame( + x = if(is.null(x.data1.l)){NULL}else{x.data1.l}, + y = if(is.null(x.data1.l)){NULL}else{y.data1.down.limit.l}, + kind = if(is.null(x.data1.l)){NULL}else{"down.frame1"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(x.data1.r)){NULL}else{x.data1.r}, + y = if(is.null(x.data1.r)){NULL}else{y.data1.down.limit.r}, + kind = if(is.null(x.data1.r)){NULL}else{"down.frame2"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(x.data1.l)){NULL}else{x.data1.l}, + y = if(is.null(x.data1.l)){NULL}else{y.data1.top.limit.l}, + kind = if(is.null(x.data1.l)){NULL}else{"top.frame1"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(x.data1.r)){NULL}else{x.data1.r}, + y = if(is.null(x.data1.r)){NULL}else{y.data1.top.limit.r}, + kind = if(is.null(x.data1.r)){NULL}else{"top.frame2"}, + stringsAsFactors = TRUE + ), + stringsAsFactors = TRUE + ) + vframe = rbind( + data.frame( + x = if(is.null(y.data1.d)){NULL}else{x.data1.left.limit.d}, + y = if(is.null(y.data1.d)){NULL}else{y.data1.d}, + kind = if(is.null(y.data1.d)){NULL}else{"left.frame1"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(y.data1.t)){NULL}else{x.data1.left.limit.t}, + y = if(is.null(y.data1.t)){NULL}else{y.data1.t}, + kind = if(is.null(y.data1.t)){NULL}else{"left.frame2"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(y.data1.d)){NULL}else{x.data1.right.limit.d}, + y = if(is.null(y.data1.d)){NULL}else{y.data1.d}, + kind = if(is.null(y.data1.d)){NULL}else{"right.frame1"}, + stringsAsFactors = TRUE + ), + data.frame( + x = if(is.null(y.data1.t)){NULL}else{x.data1.right.limit.t}, + y = if(is.null(y.data1.t)){NULL}else{y.data1.t}, + kind = if(is.null(y.data1.t)){NULL}else{"right.frame2"}, + stringsAsFactors = TRUE + ), + stringsAsFactors = TRUE + ) + # end recovering the frame coordinates + # recovering the dot coordinates + data1.signif.dot <- NULL + data1.non.signif.dot <- NULL + data1.incon.dot <- NULL + data2.signif.dot <- NULL + data2.non.signif.dot <- NULL + data2.unknown.dot <- NULL + data2.incon.dot <- NULL + if(( ! is.null(x.range.split)) & ( ! is.null(y.range.split))){ + # inconsistent dots recovery + if(length(unique(c(x.incon.data1.dot.nb.final, y.incon.data1.dot.nb.final))) > 0){ + data1.incon.dot <- data1[data1$DOT_NB %in% unique(c(x.incon.data1.dot.nb.final, y.incon.data1.dot.nb.final)), ] # if a dot in inconsistent in x or y -> classified as inconsistent (so unique() used) + # removal of the inconsistent dot in the other classifications + x.inside.data1.dot.nb.final <- x.inside.data1.dot.nb.final[ ! x.inside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + y.inside.data1.dot.nb.final <- y.inside.data1.dot.nb.final[ ! y.inside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + x.outside.data1.dot.nb.final <- x.outside.data1.dot.nb.final[ ! x.outside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + y.outside.data1.dot.nb.final <- y.outside.data1.dot.nb.final[ ! y.outside.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + x.unknown.data1.dot.nb.final <- x.unknown.data1.dot.nb.final[ ! x.unknown.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + y.unknown.data1.dot.nb.final <- y.unknown.data1.dot.nb.final[ ! y.unknown.data1.dot.nb.final %in% data1.incon.dot$DOT_NB] + # end removal of the inconsistent dot in the other classifications + } + if( ! is.null(data2)){ + if(length(unique(c(x.incon.data2.dot.nb.final, y.incon.data2.dot.nb.final))) > 0){ + data2.incon.dot <- data2[data2$DOT_NB %in% unique(c(x.incon.data2.dot.nb.final, y.incon.data2.dot.nb.final)), ] + # removal of the inconsistent dot in the other classifications + x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + x.unknown.data2.dot.nb.final <- x.unknown.data2.dot.nb.final[ ! x.unknown.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + y.unknown.data2.dot.nb.final <- y.unknown.data2.dot.nb.final[ ! y.unknown.data2.dot.nb.final %in% data2.incon.dot$DOT_NB] + # end removal of the inconsistent dot in the other classifications + } + } + # end inconsistent dots recovery + # unknown dots recovery + if( ! is.null(data2)){ + if(data2.pb.dot == "signif"){ + x.outside.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) + x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% x.unknown.data2.dot.nb.final] # remove x.unknown.data2.dot.nb.final from x.inside.data2.dot.nb.final + y.outside.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) + y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% y.unknown.data2.dot.nb.final] # remove y.unknown.data2.dot.nb.final from y.inside.data2.dot.nb.final + x.unknown.data2.dot.nb.final <- NULL + y.unknown.data2.dot.nb.final <- NULL + data2.unknown.dot <- NULL + }else if(data2.pb.dot == "not.signif"){ + x.inside.data2.dot.nb.final <- unique(c(x.inside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) + x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% x.unknown.data2.dot.nb.final] # remove x.unknown.data2.dot.nb.final from x.outside.data2.dot.nb.final + y.inside.data2.dot.nb.final <- unique(c(y.inside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) + y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% y.unknown.data2.dot.nb.final] # remove y.unknown.data2.dot.nb.final from y.outside.data2.dot.nb.final + x.unknown.data2.dot.nb.final <- NULL + y.unknown.data2.dot.nb.final <- NULL + data2.unknown.dot <- NULL + }else if(data2.pb.dot == "unknown"){ + if(length(unique(c(x.unknown.data2.dot.nb.final, y.unknown.data2.dot.nb.final))) > 0){ + data2.unknown.dot <- data2[data2$DOT_NB %in% unique(c(x.unknown.data2.dot.nb.final, y.unknown.data2.dot.nb.final)), ] # if a dot in unknown in x or y -> classified as unknown (so unique() used) + x.outside.data2.dot.nb.final <- x.outside.data2.dot.nb.final[ ! x.outside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove x.unknown.data2.dot.nb.final from x.outside.data2.dot.nb.final + x.inside.data2.dot.nb.final <- x.inside.data2.dot.nb.final[ ! x.inside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove x.unknown.data2.dot.nb.final from x.inside.data2.dot.nb.final + y.outside.data2.dot.nb.final <- y.outside.data2.dot.nb.final[ ! y.outside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove y.unknown.data2.dot.nb.final from y.outside.data2.dot.nb.final + y.inside.data2.dot.nb.final <- y.inside.data2.dot.nb.final[ ! y.inside.data2.dot.nb.final %in% data2.unknown.dot$DOT_NB] # remove y.unknown.data2.dot.nb.final from y.inside.data2.dot.nb.final + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end unknown dots recovery + # sign and non sign dot recovery + if(xy.cross.kind == "|"){ # here the problem is to deal with significant dots depending on x and y. Thus I start with that, recover dots finally non significant in outside and put them in inside (when &), and remove from inside the dots in outside + if(length(unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final))) > 0){ + tempo.outside <- unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final)) # union so unique() used + tempo.inside <- unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)) + tempo.inside <- tempo.inside[ ! tempo.inside %in% tempo.outside] + data1.signif.dot <- data1[data1$DOT_NB %in% tempo.outside, ] + data1.non.signif.dot <- data1[data1$DOT_NB %in% tempo.inside, ] + }else{ + data1.non.signif.dot <- data1[unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it + } + }else if(xy.cross.kind == "&"){ + if(sum(x.outside.data1.dot.nb.final %in% y.outside.data1.dot.nb.final) > 0){ # that is intersection + tempo.outside <- unique(x.outside.data1.dot.nb.final[x.outside.data1.dot.nb.final %in% y.outside.data1.dot.nb.final]) # intersection + tempo.outside.removed <- unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final))[ ! unique(c(x.outside.data1.dot.nb.final, y.outside.data1.dot.nb.final)) %in% tempo.outside] + tempo.inside <- unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)) + data1.signif.dot <- data1[data1$DOT_NB %in% tempo.outside, ] + data1.non.signif.dot <- data1[data1$DOT_NB %in% tempo.inside, ] + }else{ + data1.non.signif.dot <- data1[unique(c(x.inside.data1.dot.nb.final, y.inside.data1.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(data2)){ + if(xy.cross.kind == "|"){ # here the problem is to deal with significant dots depending on x and y. Thus I start with that, recover dots finally non significant in outside and put them in inside (when &), and remove from inside the dots in outside + if(length(unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final))) > 0){ + tempo.outside <- unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final)) # union so unique() used + tempo.inside <- unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)) + tempo.inside <- tempo.inside[ ! tempo.inside %in% tempo.outside] + data2.signif.dot <- data2[data2$DOT_NB %in% tempo.outside, ] + data2.non.signif.dot <- data2[data2$DOT_NB %in% tempo.inside, ] + }else{ + data2.non.signif.dot <- data2[unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it + } + }else if(xy.cross.kind == "&"){ + if(sum(x.outside.data2.dot.nb.final %in% y.outside.data2.dot.nb.final) > 0){ # that is intersection + tempo.outside <- unique(x.outside.data2.dot.nb.final[x.outside.data2.dot.nb.final %in% y.outside.data2.dot.nb.final]) # intersection + tempo.outside.removed <- unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final))[ ! unique(c(x.outside.data2.dot.nb.final, y.outside.data2.dot.nb.final)) %in% tempo.outside] + tempo.inside <- unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)) + data2.signif.dot <- data2[data2$DOT_NB %in% tempo.outside, ] + data2.non.signif.dot <- data2[data2$DOT_NB %in% tempo.inside, ] + }else{ + data2.non.signif.dot <- data2[unique(c(x.inside.data2.dot.nb.final, y.inside.data2.dot.nb.final)), ] # if no outside dots, I recover all the inside dots and that's it + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end sign and non sign dot recovery + }else if(( ! is.null(x.range.split)) & is.null(y.range.split)){ + # inconsistent dots recovery + if(length(y.incon.data1.dot.nb.final) > 0){ + data1.incon.dot <- data1[data1$DOT_NB %in% y.incon.data1.dot.nb.final, ] + } + if( ! is.null(data2)){ + if(length(y.incon.data2.dot.nb.final) > 0){ + data2.incon.dot <- data2[data2$DOT_NB %in% y.incon.data2.dot.nb.final, ] + } + }# end inconsistent dots recovery + # unknown dots recovery + if( ! is.null(data2)){ + if(data2.pb.dot == "signif"){ + y.outside.data2.dot.nb.final <- unique(c(y.outside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) + }else if(data2.pb.dot == "not.signif"){ + y.inside.data2.dot.nb.final <- unique(c(y.inside.data2.dot.nb.final, y.unknown.data2.dot.nb.final)) + }else if(data2.pb.dot == "unknown"){ + if(length(y.unknown.data2.dot.nb.final) > 0){ + data2.unknown.dot <- data2[data2$DOT_NB %in% y.unknown.data2.dot.nb.final, ] + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 6") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end unknown dots recovery + # sign and non sign dot recovery + if(length(y.outside.data1.dot.nb.final) > 0){ + data1.signif.dot <- data1[data1$DOT_NB %in% y.outside.data1.dot.nb.final, ] + } + if(length(y.inside.data1.dot.nb.final) > 0){ + data1.non.signif.dot <- data1[data1$DOT_NB %in% y.inside.data1.dot.nb.final, ] + } + if( ! is.null(data2)){ + if(length(y.outside.data2.dot.nb.final) > 0){ + data2.signif.dot <- data2[data2$DOT_NB %in% y.outside.data2.dot.nb.final, ] + } + if(length(y.inside.data2.dot.nb.final) > 0){ + data2.non.signif.dot <- data2[data2$DOT_NB %in% y.inside.data2.dot.nb.final, ] + } + } + # end sign and non sign dot recovery + }else if(is.null(x.range.split) & ( ! is.null(y.range.split))){ + # inconsistent dots recovery + if(length(x.incon.data1.dot.nb.final) > 0){ + data1.incon.dot <- data1[data1$DOT_NB %in% x.incon.data1.dot.nb.final, ] + } + if( ! is.null(data2)){ + if(length(x.incon.data2.dot.nb.final) > 0){ + data2.incon.dot <- data2[data2$DOT_NB %in% x.incon.data2.dot.nb.final, ] + } + }# end inconsistent dots recovery + # unknown dots recovery + if( ! is.null(data2)){ + if(data2.pb.dot == "signif"){ + x.outside.data2.dot.nb.final <- unique(c(x.outside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) + }else if(data2.pb.dot == "not.signif"){ + x.inside.data2.dot.nb.final <- unique(c(x.inside.data2.dot.nb.final, x.unknown.data2.dot.nb.final)) + }else if(data2.pb.dot == "unknown"){ + if(length(x.unknown.data2.dot.nb.final) > 0){ + data2.unknown.dot <- data2[data2$DOT_NB %in% x.unknown.data2.dot.nb.final, ] + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 7") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end unknown dots recovery + # sign and non sign dot recovery + if(length(x.outside.data1.dot.nb.final) > 0){ + data1.signif.dot <- data1[data1$DOT_NB %in% x.outside.data1.dot.nb.final, ] + } + if(length(x.inside.data1.dot.nb.final) > 0){ + data1.non.signif.dot <- data1[data1$DOT_NB %in% x.inside.data1.dot.nb.final, ] + } + if( ! is.null(data2)){ + if(length(x.outside.data2.dot.nb.final) > 0){ + data2.signif.dot <- data2[data2$DOT_NB %in% x.outside.data2.dot.nb.final, ] + } + if(length(x.inside.data2.dot.nb.final) > 0){ + data2.non.signif.dot <- data2[data2$DOT_NB %in% x.inside.data2.dot.nb.final, ] + } + } + # end sign and non sign dot recovery + } + # end recovering the dot coordinates + # verif + if(any(data1.signif.dot$DOT_NB %in% data1.non.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", FUNCTION.NAME, ": CODE INCONSISTENCY 8") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1.non.signif.dot$DOT_NB %in% data1.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", FUNCTION.NAME, ": CODE INCONSISTENCY 9") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1.signif.dot$DOT_NB %in% data1.incon.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 10") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1.incon.dot$DOT_NB %in% data1.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 11") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1.non.signif.dot$DOT_NB %in% data1.incon.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 12") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data1.incon.dot$DOT_NB %in% data1.non.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 13") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(data2)){ + if(any(data2.signif.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 14") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.non.signif.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 15") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.signif.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 16") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.unknown.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 17") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.signif.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 18") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.incon.dot$DOT_NB %in% data2.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 19") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.non.signif.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 20") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.unknown.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 21") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.non.signif.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 22") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.incon.dot$DOT_NB %in% data2.non.signif.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 23") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.unknown.dot$DOT_NB %in% data2.incon.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 24") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(data2.incon.dot$DOT_NB %in% data2.unknown.dot$DOT_NB)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 25") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end verif + # plot + # recovering the axes data whatever plot or not + if(is.null(data2)){ + axes <- fun_gg_scatter(data1 = list(data1), x = list(x1), y = list(y1), categ = list(NULL), color = list(fun_gg_palette(2)[2]), geom = list("geom_point"), alpha = list(0.5), x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, plot = FALSE, return = TRUE)$axes + }else{ + axes <- fun_gg_scatter(data1 = list(data1, data2), x = list(x1, x2), y = list(y1, y2), categ = list(NULL, NULL), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1]), geom = list("geom_point", "geom_point"), alpha = list(0.5, 0.5), x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, plot = FALSE, return = TRUE)$axes + } + # end recovering the axes data whatever plot or not + if(plot == TRUE){ + # add a categ for plot legend + tempo.df.name <- c("data1", "data1.signif.dot", "data1.incon.dot", "data2", "data2.signif.dot", "data2.unknown.dot", "data2.incon.dot") + tempo.class.name <- c("data1", "data1", "data1", "data2", "data2", "data2", "data2") + for(i2 in 1:length(tempo.df.name)){ + if( ! is.null(get(tempo.df.name[i2], env = sys.nframe(), inherit = FALSE))){ + assign(tempo.df.name[i2], data.frame(get(tempo.df.name[i2], env = sys.nframe(), inherit = FALSE), kind = tempo.class.name[i2]), + stringsAsFactors = TRUE) + } + } + # end add a categ for plot legend + if(( ! is.null(x.range.split)) & ( ! is.null(y.range.split))){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe), x = list(x1, "x", "x"), y = list(y1, "y", "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data1.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe, data1.signif.dot), x = list(x1, "x", "x", x1), y = list(y1, "y", "y", y1), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") + } + if( ! is.null(data1.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, vframe, data1.incon.dot), x = list(x1, "x", "x", x1), y = list(y1, "y", "y", y1), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME" , "VERT FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") + } + if( ! is.null(data2)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, hframe , vframe), x = list(x1, x2, "x", "x"), y = list(y1, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data2.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") + } + if( ! is.null(data2.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") + } + if( ! is.null(data2.unknown.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, hframe , vframe), x = list(x1, x2, x2, "x", "x"), y = list(y1, y2, y2, "y", "y"), categ = list("kind", "kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "HORIZ FRAME" , "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 12, title = "DATA2 + DATA2 UNKNOWN DOTS") + } + } + }else if(( ! is.null(x.range.split)) & is.null(y.range.split)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe), x = list(x1, "x"), y = list(y1, "y"), categ = list("kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_path"), alpha = list(0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data1.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, data1.signif.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") + } + if( ! is.null(data1.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, hframe, data1.incon.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "HORIZ FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") + } + if( ! is.null(data2)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, hframe), x = list(x1, x2, "x"), y = list(y1, y2, "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data2.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") + } + if( ! is.null(data2.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") + } + if( ! is.null(data2.unknown.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, hframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "HORIZ FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.1, 0.15), v = c(0.75, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 8, title = "DATA2 + DATA2 UNKNOWN DOTS") + } + } + }else if(is.null(x.range.split) & ( ! is.null(y.range.split))){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe), x = list(x1, "x"), y = list(y1, "y"), categ = list("kind", "kind"), legend.name = list("DATASET", "VERT FRAME"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_path"), alpha = list(0.5, 0.5), title = "DATA1", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data1.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe, data1.signif.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "VERT FRAME", "SIGNIF DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), "black"), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA1 SIGNIFICANT DOTS") + } + if( ! is.null(data1.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, vframe, data1.incon.dot), x = list(x1, "x", x1), y = list(y1, "y", y1), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "VERT FRAME", "INCONSISTENT DOTS"), color = list(fun_gg_palette(2)[2], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2), fun_gg_palette(7)[6]), geom = list("geom_point", "geom_path", "geom_point"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA1 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA1\nINCONSISTENT DOTS", text.size = 8, title = "DATA1 + DATA1 INCONSISTENT DOTS") + } + if( ! is.null(data2)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, vframe), x = list(x1, x2, "x"), y = list(y1, y2, "y"), categ = list("kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5), title = "DATA1 + DATA2", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(data2.signif.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.signif.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "SIGNIF DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], "black", rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2 DOTS\nOUTSIDE THE FRAMES", text.size = 8, title = "DATA1 + DATA2 + DATA2 SIGNIFICANT DOTS") + } + if( ! is.null(data2.incon.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.incon.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "INCONSISTENT DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[6], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 INCONSISTENT DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nINCONSISTENT DOTS", text.size = 8, title = "DATA2 + DATA2 INCONSISTENT DOTS") + } + if( ! is.null(data2.unknown.dot)){ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + tempo.graph <- fun_gg_scatter(data1 = list(data1, data2, data2.unknown.dot, vframe), x = list(x1, x2, x2, "x"), y = list(y1, y2, y2, "y"), categ = list("kind", "kind", "kind", "kind"), legend.name = list("DATASET", "DATASET", "UNKNOWN DOTS", "VERT FRAME"), color = list(fun_gg_palette(2)[2], fun_gg_palette(2)[1], fun_gg_palette(7)[5], rep(hsv(h = c(0.5, 0.6), v = c(0.9, 1)), 2)), geom = list("geom_point", "geom_point", "geom_point", "geom_path"), alpha = list(0.5, 0.5, 0.5, 0.5), title = "DATA1 + DATA2 + DATA2 UNKNOWN DOTS", x.lim = x.range.plot, y.lim = y.range.plot, raster = raster, return = TRUE) + if( ! is.null(tempo.graph$warn)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") FROM fun_gg_scatter():\n", tempo.graph$warn) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(graph.in.file == FALSE){ + fun_open(pdf = FALSE) + } + fun_gg_empty_graph(text = "NO PLOT\nBECAUSE\nNO DATA2\nUNKNOWN DOTS", text.size = 8, title = "DATA2 + DATA2 UNKNOWN DOTS") + } + } + } + } + # end plot + if(warn.print == TRUE & ! is.null(warn)){ + options(warning.length = 8170) + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + tempo.list <- list(data1.removed.row.nb = data1.removed.row.nb, data1.removed.rows = data1.removed.rows, data2.removed.row.nb = data2.removed.row.nb, data2.removed.rows = data2.removed.rows, hframe = hframe, vframe = vframe, data1.signif.dot = data1.signif.dot, data1.non.signif.dot = data1.non.signif.dot, data1.inconsistent.dot = data1.incon.dot, data2.signif.dot = data2.signif.dot, data2.non.signif.dot = data2.non.signif.dot, data2.unknown.dot = data2.unknown.dot, data2.inconsistent.dot = data2.incon.dot, axes = axes, warn = warn) + return(tempo.list) } -if(line.count== 3L){ -fin.lg.disp[[6]] <- legend.disp[[point.count + line.count]] -lg.order[[6]] <- point.count + line.count -lg.color[[6]] <- color[[i1]] # if color == NULL -> NULL -lg.line.size[[6]] <- line.size[[i1]] -lg.line.type[[6]] <- line.type[[i1]] -if(plot == TRUE & fin.lg.disp[[6]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -lg.alpha[[6]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf -}else{ -lg.alpha[[6]] <- alpha[[i1]] + + +################ Import + + +######## fun_pack() #### check if R packages are present and import into the working environment + + +fun_pack <- function( + req.package, + load = FALSE, + lib.path = NULL +){ + # AIM + # check if the specified R packages are present in the computer and import them into the working environment + # ARGUMENTS + # req.package: character vector of package names to import + # load: logical. Load the package into the environement (using library())? Interesting if packages are not in default folders or for checking the functions names of packages using search() + # lib.path: optional character vector specifying the absolute pathways of the directories containing some of the listed packages in the req.package argument, if not in the default directories. Ignored if NULL + # RETURN + # nothing + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_pack(req.package = "nopackage") + # fun_pack(req.package = "ggplot2") + # fun_pack(req.package = "ggplot2", lib.path = "blablabla") + # DEBUGGING + # req.package = "ggplot2" ; lib.path = "C:/Program Files/R/R-3.5.1/library" + # req.package = "serpentine" ; lib.path = "C:/users/gael/appdata/roaming/python/python36/site-packages" + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = req.package, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = load, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + if(is.null(lib.path)){ + lib.path <- .libPaths() # .libPaths(new = lib.path) # or .libPaths(new = c(.libPaths(), lib.path)) + }else{ + .libPaths(new = sub(x = lib.path, pattern = "/$|\\\\$", replacement = "")) # .libPaths(new = ) add path to default path. BEWARE: .libPaths() does not support / at the end of a submitted path. Thus check and replace last / or \\ in path + lib.path <- .libPaths() + } + tempo <- NULL + for(i1 in 1:length(req.package)){ + if( ! req.package[i1] %in% rownames(utils::installed.packages(lib.loc = lib.path))){ + tempo <- c(tempo, req.package[i1]) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0( + "ERROR IN ", + function.name, + ": PACKAGE", + ifelse(length(tempo) == 1L, paste0("\n\n", tempo, "\n\n"), paste0("S\n", paste(tempo, collapse = "\n"), "\n")), + "MUST BE INSTALLED IN", + ifelse(length(lib.path) == 1L, "", " ONE OF THESE FOLDERS"), + ":\n", + paste(lib.path, collapse = "\n") + ) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(load == TRUE){ + for(i2 in 1:length(req.package)){ + suppressMessages(suppressWarnings(suppressPackageStartupMessages(library(req.package[i2], lib.loc = lib.path, quietly = TRUE, character.only = TRUE)))) + } + } } -class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) -for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same -tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl -ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting -"(data = tempo.data.frame, mapping = ggplot2::aes(x = ", -x[[i1]], -ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), -y[[i1]], -if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, -", size = ", -categ[[i1]], -"), color = \"", -color[[i1]][i5], -"\", linetype = ", -ifelse(is.numeric(line.type[[i1]]), "", "\""), -line.type[[i1]], -ifelse(is.numeric(line.type[[i1]]), "", "\""), -ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), -ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), -", alpha = ", -alpha[[i1]], -", show.legend = FALSE)" -)))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency -coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + + +######## fun_python_pack() #### check if python packages are present + + +fun_python_pack <- function( + req.package, + python.exec.path = NULL, + lib.path = NULL, + R.lib.path = NULL +){ + # AIM + # check if the specified python packages are present in the computer (no import) + # WARNINGS + # for python 3.7. Previous versions return an error "Error in sys$stdout$flush() : attempt to apply non-function" + # ARGUMENTS + # req.package: character vector of package names to import + # python.exec.path: optional character vector specifying the absolute pathways of the executable python file to use (associated to the packages to use). If NULL, the reticulate::import_from_path() function used in fun_python_pack() seeks for an available version of python.exe, and then uses python_config(python_version, required_module, python_versions). But might not be the correct one for the lib.path parameter specified. Thus, it is recommanded to do not leave NULL, notably when using computing clusters + # lib.path: optional character vector specifying the absolute pathways of the directories containing some of the listed packages in the req.package argument, if not in the default directories + # R.lib.path: absolute path of the reticulate packages, if not in the default folders + # RETURN + # nothing + # REQUIRED PACKAGES + # reticulate + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # fun_pack() + # EXAMPLES + # example of error message + # fun_python_pack(req.package = "nopackage") + # example without error message (require the installation of the python serpentine package from https://github.com/koszullab/serpentine + # fun_python_pack(req.package = "serpentine", python.exec.path = "C:/ProgramData/Anaconda3/python.exe", lib.path = "c:/programdata/anaconda3/lib/site-packages/") + # another example of error message + # fun_python_pack(req.package = "serpentine", lib.path = "blablabla") + # DEBUGGING + # req.package = "serpentine" ; python.exec.path = "C:/ProgramData/Anaconda3/python.exe" ; lib.path = "c:/programdata/anaconda3/lib/site-packages/" ; R.lib.path = NULL + # req.package = "bad" ; lib.path = NULL ; R.lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(utils::find("fun_pack", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_pack() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = req.package, class = "character", fun.name = function.name) ; eval(ee) + if( ! is.null(python.exec.path)){ + tempo <- fun_check(data = python.exec.path, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(file.exists(python.exec.path))){ # separation to avoid the problem of tempo$problem == FALSE and python.exec.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": FILE PATH INDICATED IN THE python.exec.path ARGUMENT DOES NOT EXISTS:\n", paste(python.exec.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if( ! is.null(R.lib.path)){ + tempo <- fun_check(data = R.lib.path, class = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(R.lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and R.lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE R.lib.path ARGUMENT DOES NOT EXISTS:\n", paste(R.lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # package checking + fun_pack(req.package = "reticulate", lib.path = R.lib.path) + # end package checking + # main code + if(is.null(python.exec.path)){ + python.exec.path <- reticulate::py_run_string(" +import sys ; +path_lib = sys.path +") # python string + python.exec.path <- python.exec.path$path_lib + } + if(is.null(lib.path)){ + lib.path <- reticulate::py_run_string(" +import sys ; +path_lib = sys.path +") # python string + lib.path <- lib.path$path_lib + } + reticulate::use_python(Sys.which(python.exec.path), required = TRUE) # required to avoid the use of erratic python exec by reticulate::import_from_path() + for(i1 in 1:length(req.package)){ + tempo.try <- vector("list", length = length(lib.path)) + for(i2 in 1:length(lib.path)){ + tempo.try[[i2]] <- suppressWarnings(try(reticulate::import_from_path(req.package[i1], path = lib.path[i2]), silent = TRUE)) + tempo.try[[i2]] <- suppressWarnings(try(reticulate::import_from_path(req.package[i1], path = lib.path[i2]), silent = TRUE)) # done twice to avoid the error message about flushing present the first time but not the second time. see https://stackoverflow.com/questions/57357001/reticulate-1-13-error-in-sysstdoutflush-attempt-to-apply-non-function + } + if(all(sapply(tempo.try, FUN = grepl, pattern = "[Ee]rror"))){ + print(tempo.try) + tempo.cat <- paste0("ERROR IN ", function.name, ": PACKAGE ", req.package[i1], " MUST BE INSTALLED IN THE MENTIONNED DIRECTORY:\n", paste(lib.path, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } # else{ + # suppressMessages(suppressWarnings(suppressPackageStartupMessages(assign(req.package[i1], reticulate::import(req.package[i1]))))) # not required because try() already evaluates + # } + } } -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "size", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(line.size[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, breaks reorder the classes according to class.categ in the legend + + +################ Print / Exporting results (text & tables) + + +######## fun_report() #### print string or data object into output file + + +# Problem with 1D tables : names over the table not printed. In addition, see how the 2D tables are printed. + +fun_report <- function( + data, + output = "results.txt", + path = "C:/Users/Gael/Desktop/", + overwrite = FALSE, + rownames.kept = FALSE, + vector.cat = FALSE, + noquote = TRUE, + sep = 2 +){ + # AIM + # log file function: print a character string or a data object into a same output file + # ARGUMENTS + # data: object to print in the output file. If NULL, nothing is done, with no warning + # output: name of the output file + # path: location of the output file + # overwrite: (logical) if output file already exists, defines if the printing is appended (default FALSE) or if the output file content is erased before printing (TRUE) + # rownames.kept: (logical) defines whether row names have to be removed or not in small tables (less than length.rows rows) + # vector.cat (logical). If TRUE print a vector of length > 1 using cat() instead of capture.output(). Otherwise (default FALSE) the opposite + # noquote: (logical). If TRUE no quote are present for the characters + # sep: number of separating lines after printed data (must be integer) + # RETURN + # nothing + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_report() + # fun_report(data = 1:3, output = "results.txt", path = "C:/Users/Gael/Desktop", overwrite = TRUE, rownames.kept = FALSE, vector.cat = FALSE, noquote = FALSE, sep = 2) + # DEBUGGING + # data = 1:3 ; output = "results.txt" ; path = "C:/Users/Gael/Desktop" ; overwrite = TRUE ; rownames.kept = FALSE ; vector.cat = FALSE ; noquote = FALSE ; sep = 2 # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # argument checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = output, class = "character", length = 1, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & output == ""){ + tempo.cat <- paste0("ERROR IN ", function.name, ": output ARGUMENT AS \"\" DOES NOT CORRESPOND TO A VALID FILE NAME") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo <- fun_check(data = path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": path ARGUMENT DOES NOT CORRESPOND TO EXISTING DIRECTORY\n", paste(path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = overwrite, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = rownames.kept, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = vector.cat, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = noquote, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = sep, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # the 4 next lines are inactivated but kept because at a time, I might have a problem with data (solved with data = NULL). These 4 lines are just to know how to detect a missing argument. Important here because if data is not provided, print the code of the data function + # arg.user.list <- as.list(match.call(expand.dots = FALSE))[-1] # recover all the arguments provided by the function user (excluding the argument with defaults values not provided by the user. Thus, it is really the list indicated by the user) + # default.arg.list <- formals(fun = sys.function(sys.parent())) # list of all the arguments of the function with their default values (not the values of the user !). It seems that ls() as first line of the function provide the names of the arguments (empty, called, etc., or not) + # arg.without.default.value <- sapply(default.arg.list, is.symbol) & sapply(sapply(default.arg.list, as.character), identical, "") # logical to detect argument without default values (these are typeof "symbol" and class "name" and empty character + # if( ! all(names(default.arg.list)[arg.without.default.value] %in% names(arg.user.list))){ # test that the arguments with no null values are provided by the user + # tempo.cat <- paste0("ERROR IN ", function.name, ": VALUE REQUIRED FOR THESE ARGUMENTS WITH NO DEFAULTS VALUES: ", paste(names(default.arg.list)[arg.without.default.value][ ! names(default.arg.list)[arg.without.default.value] %in% names(arg.user.list)], collapse = " ")) + # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + # } + # end argument checking + # main code + if( ! is.null(data)){ + if(all(class(data) == "data.frame") | all(class(data) == "table") | all(class(data) %in% c("matrix", "array"))){ # before R4.0.0, it was all(class(data) %in% c("matrix", "data.frame", "table")) + if(rownames.kept == FALSE & all(class(data) == "data.frame") & nrow(data) != 0 & nrow(data) <= 4){ # for data frames with nrows <= 4 + rownames.output.tables <- "" + length.rows <- nrow(data) + for(i in 1:length.rows){ # replace the rownames of the first 4 rows by increasing number of spaces (because identical row names not allowed in data frames). This method cannot be extended to more rows as the printed data frame is shifted on the right because of "big empty rownames" + rownames.output.tables <- c(rownames.output.tables, paste0(rownames.output.tables[i]," ", collapse="")) + } + row.names(data) <- rownames.output.tables[1:length.rows] + }else if(rownames.kept == FALSE & (all(class(data) == "table") | all(class(data) %in% c("matrix", "array")))){ # before R4.0.0, it was & all(class(data) %in% c("matrix", "table")) + rownames(data) <- rep("", nrow(data)) # identical row names allowed in matrices and tables + } + if(noquote == TRUE){ + utils::capture.output(noquote(data), file=paste0(path, "/", output), append = ! overwrite) + }else{ + utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) + } + }else if(is.vector(data) & all(class(data) != "list") & (length(data) == 1L | vector.cat == TRUE)){ + if(noquote == TRUE){ + cat(noquote(data), file= paste0(path, "/", output), append = ! overwrite) + }else{ + cat(data, file= paste0(path, "/", output), append = ! overwrite) + } + }else if(all(base::mode(data) == "character")){ # characters (array, list, factor or vector with vector.cat = FALSE) + if(noquote == TRUE){ + utils::capture.output(noquote(data), file=paste0(path, "/", output), append = ! overwrite) + }else{ + utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) + } + }else{ # other object (S4 for instance, which do not like noquote() + utils::capture.output(data, file=paste0(path, "/", output), append = ! overwrite) + } + sep.final <- paste0(rep("\n", sep), collapse = "") + write(sep.final, file= paste0(path, "/", output), append = TRUE) # add a sep + } } + + +######## fun_get_message() #### return error/warning/other messages of an expression (that can be exported) + + +fun_get_message <- function( + data, + kind = "error", + header = TRUE, + print.no = FALSE, + text = NULL, + env = NULL +){ + # AIM + # evaluate an instruction written between "" and return the first of the error, or warning or standard (non error non warning) messages if ever exist + # using argument print.no = FALSE, return NULL if no message, which is convenient in some cases + # WARNINGS + # Only the first message is returned + # Always use the env argument when fun_get_message() is used inside functions + # The function does not prevent printing if print() is used inside the instruction tested. To prevent that, use tempo <- capture.output(error <- fun_get_message(data = "fun_check(data = 'a', class = mean, neg.values = FALSE, print = TRUE)")). The return of fun_get_message() is assigned into error and the printed messages are captured by capture.output() and assigned into tempo. See the examples + # ARGUMENTS + # data: character string to evaluate + # kind: character string. Either "error" to get error messages, or "warning" to get warning messages, or "message" to get non error and non warning messages + # header: logical. Add a header in the returned message? + # print.no: logical. Print a message saying that no message reported? + # text: character string added to the output message (even if no message exists and print.no is TRUE). Inactivated if header is FALSE + # env: the name of an existing environment. NULL if not required + # RETURN + # the message or NULL if no message and print.no is FALSE + # REQUIRED PACKAGES + # none + # REQUIRED FUNCTIONS FROM CUTE_LITTLE_R_FUNCTION + # fun_check() + # EXAMPLES + # fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "error", print.no = TRUE, text = "IN A") + # fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "warning", print.no = TRUE, text = "IN A") + # fun_get_message(data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)", kind = "message", print.no = TRUE, text = "IN A") + # fun_get_message(data = "wilcox.test()", kind = "error", print.no = TRUE, text = "IN A") + # fun_get_message(data = "sum(1)", kind = "error", print.no = TRUE, text = "IN A") + # fun_get_message(data = "message('ahah')", kind = "error", print.no = TRUE, text = "IN A") + # fun_get_message(data = "message('ahah')", kind = "message", print.no = TRUE, text = "IN A") + # fun_get_message(data = "ggplot2::ggplot(data = data.frame(X = 1:10, stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()", kind = "message", print.no = TRUE, text = "IN FUNCTION 1") + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; fun_get_message(data = 'fun_gg_boxplot(data = obs1, y = "Time", categ = "Group1")', kind = "message", print.no = TRUE, text = "IN FUNCTION 1") + # DEBUGGING + # data = "wilcox.test(c(1,1,3), c(1, 2, 4), paired = TRUE)" ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging + # data = "sum(1)" ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Group1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; data = 'fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Group1")' ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging + # data = "message('ahah')" ; kind = "error" ; header = TRUE ; print.no = TRUE ; text = "IN A" ; env = NULL + # data = 'ggplot2::ggplot(data = data.frame(X = "a", stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()' ; kind = "message" ; header = TRUE ; print.no = FALSE ; text = NULL # for function debugging + # data = 'ggplot2::ggplot(data = data.frame(X = "a", stringsAsFactors = TRUE), mapping = ggplot2::aes(x = X)) + ggplot2::geom_histogram()' ; kind = "warning" ; header = TRUE ; print.no = FALSE ; text = NULL # for function debugging + # data = "emmeans::emmeans(object = emm.rg, specs = contrast.var)" ; kind = "message" ; header = TRUE ; print.no = FALSE ; text = NULL ; env = NULL # for function debugging + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + # end function name + # required function checking + if(length(utils::find("fun_check", mode = "function")) == 0L){ + tempo.cat <- paste0("ERROR IN ", function.name, ": REQUIRED fun_check() FUNCTION IS MISSING IN THE R ENVIRONMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # no need to use reserved words to avoid bugs, because it is local, and exists("tempo.warning", inherit = FALSE), never use the scope + # argument checking + # argument checking with fun_check() + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data, class = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = kind, options = c("error", "warning", "message"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = print.no, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = header, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(text)){ + tempo <- fun_check(data = text, class = "character", length = 1, fun.name = function.name) ; eval(ee) + } + if( ! is.null(env)){ + tempo <- fun_check(data = env, class = "environment", fun.name = function.name) ; eval(ee) # + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # end argument checking with fun_check() + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument checking + # main code + pdf(file = NULL) # send plots into a NULL file, no pdf file created + window.nb <- dev.cur() + invisible(dev.set(window.nb)) + # last warning cannot be used because suppressWarnings() does not modify last.warning present in the base evironment (created at first warning in a new R session), or warnings() # to reset the warning history : unlockBinding("last.warning", baseenv()) ; assign("last.warning", NULL, envir = baseenv()) + output <- NULL + tempo.error <- try(suppressMessages(suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))), silent = TRUE) # get error message, not warning or messages + if(any(class(tempo.error) %in% c("gg", "ggplot"))){ + tempo.error <- try(suppressMessages(suppressWarnings(ggplot2::ggplot_build(tempo.error))), silent = TRUE)[1] + } + if(exists("tempo.error", inherit = FALSE) == TRUE){ # inherit = FALSE avoid the portee lexical and thus the declared word + if( ! all(class(tempo.error) == "try-error")){ # deal with NULL and S4 objects. Old code: ! (all(class(tempo.error) == "try-error") & any(grepl(x = tempo.error, pattern = "^Error|^error|^ERROR"))) but problem with S4 objects. Old code : if((length(tempo.error) > 0 & ! any(grepl(x = tempo.error, pattern = "^Error|^error|^ERROR"))) | (length(tempo.error) == 0) ){ but problem when tempo.error is a list but added this did not work: | ! all(class(tempo.error) == "character") + tempo.error <- NULL + } + }else{ + tempo.error <- NULL + } + if(kind == "error" & ! is.null(tempo.error)){ # + if(header == TRUE){ + tempo.error[1] <- gsub(x = tempo.error[1], pattern = "^Error i|^error i|^ERROR I", replacement = "I") + output <- paste0("ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.error[1]) # + }else{ + output <- tempo.error[1] # + } + }else if(kind == "error" & is.null(tempo.error) & print.no == TRUE){ + output <- paste0("NO ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + }else if(kind != "error" & ( ! is.null(tempo.error)) & print.no == TRUE){ + output <- paste0("NO ", ifelse(kind == "warning", "WARNING", "STANDARD (NON ERROR AND NON WARNING)"), " MESSAGE BECAUSE OF ERROR MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + }else if(is.null(tempo.error)){ + fun.warning.capture <- function(expr){ + # from demo(error.catching) typed in the R console, coming from ?tryCatch + # see also http://mazamascience.com/WorkingWithData/?p=912 + # return a character string or NULL + # expr <- wilcox.test.default(c(1, 1, 3), c(1, 2, 4), paired = TRUE) + W <- NULL + w.handler <- function(w){ # warning handler + W <<- w # send to the above env, i.e., the inside of the fun.warning.capture function + invokeRestart("muffleWarning") # here w.handler() muffles all the warnings. See http://romainfrancois.blog.free.fr/index.php?post/2009/05/20/Disable-specific-warnings to muffle specific warnings and print others + } + output <- list( + value = suppressMessages(withCallingHandlers(tryCatch(expr, error = function(e){e}), warning = w.handler)), # BEWARE: w.handler is a function written without (), like in other functions with FUN argument + warning = W # processed by w.handler() + ) + return(if(is.null(output$warning)){NULL}else{as.character(output$warning)}) + } + tempo.warn <- fun.warning.capture(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env})) + # warn.options.ini <- options()$warn ; options(warn = 1) ; tempo.warn <- utils::capture.output({tempo <- suppressMessages(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))}, type = "message") ; options(warn = warn.options.ini) # this recover warnings not messages and not errors but does not work in all enviroments + tempo.message <- utils::capture.output({ + tempo <- suppressMessages(suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env}))) + if(any(class(tempo) %in% c("gg", "ggplot"))){ + tempo <- ggplot2::ggplot_build(tempo) + }else{ + tempo <- suppressWarnings(eval(parse(text = data), envir = if(is.null(env)){parent.frame()}else{env})) + } + }, type = "message") # recover messages not warnings and not errors + if(kind == "warning" & ! is.null(tempo.warn)){ + if(length(tempo.warn) > 0){ # to avoid character(0) + if( ! any(sapply(tempo.warn, FUN = "grepl", pattern = "() FUNCTION:$"))){ + tempo.warn <- paste(unique(tempo.warn), collapse = "\n") # if FALSE, means that the tested data is a special function. If TRUE, means that the data is a standard function. In that case, the output of capture.output() is two strings per warning messages: if several warning messages -> identical first string, which is removed in next messages by unique() + }else{ + tempo.warn <- paste(tempo.warn, collapse = "\n") + } + if(header == TRUE){ + if(any(grepl(x = tempo.warn[[1]], pattern = "^simpleWarning i"))){ + tempo.warn[[1]] <- gsub(x = tempo.warn[[1]], pattern = "^Warning i", replacement = "I") + } + if(any(grepl(x = tempo.warn[[1]], pattern = "^Warning i"))){ + tempo.warn[[1]] <- gsub(x = tempo.warn[[1]], pattern = "^Warning i", replacement = "I") + } + output <- paste0("WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.warn) # + }else{ + output <- tempo.warn # + } + }else{ + if(print.no == TRUE){ + output <- paste0("NO WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + } # no need else{} here because output is already NULL at first + } + }else if(kind == "warning" & is.null(tempo.warn) & print.no == TRUE){ + output <- paste0("NO WARNING MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + }else if(kind == "message" & exists("tempo.message", inherit = FALSE) == TRUE){ # inherit = FALSE avoid the portee lexical and thus the declared word + if(length(tempo.message) > 0){ # if something is returned by capture.ouptput() (only in this env) with a length more than 1 + if(header == TRUE){ + output <- paste0("STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text, ":\n", tempo.message) # + }else{ + output <- tempo.message # + } + }else{ + if(print.no == TRUE){ + output <- paste0("NO STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + } # no need else{} here because output is already NULL at first + } + }else if(kind == "message" & exists("tempo.message", inherit = FALSE) == FALSE & print.no == TRUE){ + output <- paste0("NO STANDARD (NON ERROR AND NON WARNING) MESSAGE REPORTED", ifelse(is.null(text), "", " "), text) + } # no need else{} here because output is already NULL at first + } # no need else{} here because output is already NULL at first + invisible(dev.off(window.nb)) # end send plots into a NULL file + return(output) # do not use cat() because the idea is to reuse the message } + + + + + + + +# Error: class order not good when a class is removed due to NA +# Error: line 136 in check 20201126 with add argument +# Solve this: sometimes error messages can be more than the max display (8170). Thus, check every paste0("ERROR IN ", function.name, and trunck the message if to big. In addition, add at the begining of the warning message that it is too long and see the $warn output for complete message. Add also this into fun_scatter +# add dot.shape ? See with available aesthetic layers +# rasterise: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html +# add horizontal argument and deal any conflict with vertical argument. Start with horizontal = NULL as default. If ! is.null() -> convert vertical if required +# time for excecution : microbenchmark package. See also in RStudio time per line of code + +fun_gg_boxplot <- function( + data1, + y, + categ, + categ.class.order = NULL, + categ.color = NULL, + box.legend.name = NULL, + box.fill = FALSE, + box.width = 0.5, + box.space = 0.1, + box.line.size = 0.75, + box.notch = FALSE, + box.alpha = 1, + box.mean = TRUE, + box.whisker.kind = "std", + box.whisker.width = 0, + dot.color = grey(0.25), + dot.categ = NULL, + dot.categ.class.order = NULL, + dot.legend.name = NULL, + dot.tidy = FALSE, + dot.tidy.bin.nb = 50, + dot.jitter = 0.5, + dot.seed = 2, + dot.size = 3, + dot.alpha = 0.5, + dot.border.size = 0.5, + dot.border.color = NULL, + x.lab = NULL, + x.angle = 0, + y.lab = NULL, + y.lim = NULL, + y.log = "no", + y.tick.nb = NULL, + y.second.tick.nb = 1, + y.include.zero = FALSE, + y.top.extra.margin = 0.05, + y.bottom.extra.margin = 0.05, + stat.pos = "top", + stat.mean = FALSE, + stat.size = 4, + stat.dist = 5, + stat.angle = 0, + vertical = TRUE, + text.size = 12, + title = "", + title.text.size = 8, + legend.show = TRUE, + legend.width = 0.5, + article = TRUE, + grid = FALSE, + add = NULL, + return = FALSE, + return.ggplot = FALSE, + return.gtable = TRUE, + plot = TRUE, + warn.print = FALSE, + lib.path = NULL +){ + # AIM + # Plot ggplot2 boxplots + dots + means + # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html + # WARNINGS + # Rows containing NA in data1[, c(y, categ)] will be removed before processing, with a warning (see below) + # Hinges are not computed like in the classical boxplot() function of R. See https://ggplot2.tidyverse.org/reference/geom_boxplot.html + # To have a single box, please create a factor column with a single class and specify the name of this column in the categ argument. For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1, knowing that categ2 must also be specified in this situation). See categ argument below + # The dot.alpha argument can alter the display of the color boxes when using pdf output + # Size arguments (box.line.size, dot.size, dot.border.size, stat.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE)) + # Display seems to be done twice on Windows devices (like a blink). However, no double plots on pdf devices. Thus, the blink remains mysterious + # To remove boxes and have only dots, use box.alpha = 0 + # ARGUMENTS + # data1: data frame containing one column of quantitative values (see the y argument below) and one or two columns of categories (see the categ argument below). Duplicated column names are not allowed + # y: character string of the data1 column name for y-axis (column containing numeric values). Numeric values will be split according to the classes of the column names indicated in the categ argument to generate the boxes and will also be used to plot the dots + # categ: vector of character strings of the data1 column name for categories (column of characters or factors). Must be either one or two column names. If a single column name (further referred to as categ1), then one box per class of categ1. If two column names (further referred to as categ1 and categ2), then one box per class of categ2, which form a group of boxes in each class of categ1. WARNING: no empty classes allowed. To have a single box, create a factor column with a single class and specify the name of this column in the categ argument (here, no categ2 in categ argument). For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1), in addition to the already used category (as categ2 in this situation) + # categ.class.order: list indicating the order of the classes of categ1 and categ2 represented on the boxplot (the first compartment for categ1 and and the second for categ2). If categ.class.order == NULL, classes are represented according to the alphabetical order. Some compartments can be NULL and others not. See the categ argument for categ1 and categ2 description + # categ.color: vector of color character string for box frames (see the categ argument for categ1 and categ2 description) + # If categ.color == NULL, default colors of ggplot2, whatever categ1 and categ2 + # If categ.color is non-null and only categ1 in categ argument, categ.color can be either: + # (1) a single color string. All the boxes will have this color, whatever the number of classes of categ1 + # (2) a vector of string colors, one for each class of categ1. Each color will be associated according to categ.class.order of categ1 + # (3) a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ1 and a single class of categ1 per color must be respected + # Color functions, like grey(), hsv(), etc., are also accepted + # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the maximal integer value among all the integers in categ.color (see fun_gg_palette()) + # If categ.color is non-null and categ1 and categ2 are specified, all the rules described above will apply to categ2 instead of categ1 (colors will be determined for boxes inside a group of boxes) + # box.legend.name: character string of the legend title. If box.legend.name is NULL, then box.legend.name <- categ1 if only categ1 is present, and box.legend.name <- categ2 if categ1 and categ2 are present in the categ argument. Write "" if no legend required. See the categ argument for categ1 and categ2 description + # box.fill: logical. Fill the box? If TRUE, the categ.color argument will be used to generate filled boxplots (the box frames being black) as well as filled outlier dots (the dot border being controlled by the dot.border.color argument). If all the dots are plotted (argument dot.color other than NULL), they will be over the boxes. If FALSE, the categ.color argument will be used to color the box frames and the outlier dot borders. If all the dots are plotted, they will be beneath the boxes + # box.width: single numeric value (from 0 to 1) of width of either boxes or group of boxes + # When categ argument has a single categ1 element (i.e., separate boxes. See the categ argument for categ1 and categ2 description), then each class of categ1 is represented by a single box. In that case, box.width argument defines each box width, from 0 (no box width) to 1 (max box width), but also the space between boxes (the code uses 1 - box.width for the box spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each box) + # When categ argument has a two categ1 and categ2 elements (i.e., grouped boxes), box.width argument defines the width allocated for each set of grouped boxes, from 0 (no group width) to 1 (max group width), but also the space between grouped boxes (the code uses 1 - box.width for the spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each set of grouped box) + # box.space: single numeric value (from 0 to 1) indicating the box separation inside grouped boxes, when categ argument has a two categ1 and categ2 elements. 0 means no space and 1 means boxes shrunk to a vertical line. Ignored if categ argument has a single categ1 element + # box.line.size: single numeric value of line width of boxes and whiskers in mm + # box.notch: logical. Notched boxplot? It TRUE, display notched boxplot, notches corresponding approximately to the 95% confidence interval of the median (the notch interval is exactly 1.58 x Inter Quartile Range (IQR) / sqrt(n), with n the number of values that made the box). If notch intervals between two boxes do not overlap, it can be interpreted as significant median differences + # box.alpha: single numeric value (from 0 to 1) of box transparency (full transparent to full opaque, respectively). To remove boxplots, use box.alpha = 0 + # box.mean: logical. Add mean value? If TRUE, a diamond-shaped dot, with the horizontal diagonal corresponding to the mean value, is displayed over each boxplot + # box.whisker.kind: range of the whiskers. Either "no" (no whiskers), or "std" (length of each whisker equal to 1.5 x Inter Quartile Range (IQR)), or "max" (length of the whiskers up or down to the most distant dot) + # box.whisker.width: single numeric value (from 0 to 1) of the whisker width, with 0 meaning no whiskers and 1 meaning a width equal to the box width + # dot.color: vector of color character string ruling the dot colors and the dot display. See the example section below for easier understanding of the rules described here + # If NULL, no dots plotted + # If "same", the dots will have the same colors as the respective boxplots + # Otherwise, as in the rule (1), (2) or (3) described in the categ.color argument, except that in the possibility (3), the rule "a single color per class of categ and a single class of categ per color", does not have to be respected (for instance, each dot can have a different color). Colors will also depend on the dot.categ argument. If dot.categ is NULL, then colors will be applied to each class of the last column name specified in categ. If dot.categ is non-NULL, colors will be applied to each class of the column name specified in dot.categ. See examples + # dot.categ: optional single character string of a column name (further referred to as categ3) of the data1 argument. This column of data1 will be used to generate a legend for dots, in addition to the legend for boxes. See the dot.color argument for details about the way the legend is built using the two dot.categ and dot.color arguments. If NULL, no legend created and the colors of dots will depend on dot.color and categ arguments (as explained in the dot.color argument) + # dot.categ.class.order: optional vector of character strings indicating the order of the classes of categ3 (see the dot.categ argument). If dot.categ is non-NULL and dot.categ.class.order is NULL, classes are displayed in the legend according to the alphabetical order. Ignored if dot.categ is NULL + # dot.legend.name: optional character string of the legend title for categ3 (see the dot.categ argument). If dot.legend.name == NULL, dot.categ value is used (name of the column in data1). Write "" if no legend required. Ignored if dot.categ is NULL + # dot.tidy: logical. Nice dot spreading? If TRUE, use the geom_dotplot() function for a nice representation. WARNING: change the true quantitative coordinates of dots (i.e., y-axis values for vertical display) because of binning. Thus, the gain in aestheticism is associated with a loss in precision that can be very important. If FALSE, dots are randomly spread on the qualitative axis, using the dot.jitter argument (see below) keeping the true quantitative coordinates + # dot.tidy.bin.nb: positive integer indicating the number of bins (i.e., nb of separations) of the y.lim range. Each dot will then be put in one of the bin, with a diameter of the width of the bin. In other words, increase the number of bins to have smaller dots. Not considered if dot.tidy is FALSE + # dot.jitter: numeric value (from 0 to 1) of random dot horizontal dispersion (for vertical display), with 0 meaning no dispersion and 1 meaning dispersion in the corresponding box width interval. Not considered if dot.tidy is TRUE + # dot.seed: integer value that set the random seed. Using the same number will generate the same dot jittering. Write NULL to have different jittering each time the same instruction is run. Ignored if dot.tidy is TRUE + # dot.size: numeric value of dot diameter in mm. Not considered if dot.tidy is TRUE + # dot.alpha: numeric value (from 0 to 1) of dot transparency (full transparent to full opaque, respectively) + # dot.border.size: numeric value of border dot width in mm. Write zero for no dot border. If dot.tidy is TRUE, value 0 remove the border and other values leave the border without size control (geom_doplot() feature) + # dot.border.color: single character color string defining the color of the dot border (same color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette() + # x.lab: a character string or expression for x-axis legend. If NULL, character string of categ1 (see the categ argument for categ1 and categ2 description) + # x.angle: integer value of the text angle for the x-axis numbers, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. + # y.lab: a character string or expression for y-axis legend. If NULL, character string of the y argument + # y.lim: 2 numeric values indicating the range of the y-axis. Order matters (for inverted axis). If NULL, the range of the x column name of data1 will be used. + # y.log: either "no", "log2" (values in the y argument column of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y argument column of the data1 data frame will be log10 transformed and y-axis will be log10 scaled). WARNING: not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881) + # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) + # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks + # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10" + # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis + # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis + # stat.pos: add the median number above the corresponding box. Either NULL (no number shown), "top" (at the top of the plot region) or "above" (above each box) + # stat.mean: logical. Display mean numbers instead of median numbers? Ignored if stat.pos is NULL + # stat.size: numeric value of the stat font size in mm. Ignored if stat.pos is NULL + # stat.dist: numeric value of the stat distance in percentage of the y-axis range (stat.dist = 5 means move the number displayed at 5% of the y-axis range). Ignored if stat.pos is NULL or "top" + # stat.angle: integer value of the angle of stat, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. + # vertical: logical. Vertical boxes? WARNING: will be automatically set to TRUE if y.log argument is other than "no". Indeed, not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881) + # text.size: numeric value of the font size of the (1) axis numbers, (2) axis labels and (3) texts in the graphic legend (in mm) + # title: character string of the graph title + # title.text.size: numeric value of the title font size in mm + # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not + # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend + # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme + # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present) + # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL + # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()" + # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_boxplot() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments) + # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions + # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_boxplot() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))' + # return: logical. Return the graph parameters? + # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_boxplot() function (e.g., a <- fun_gg_boxplot()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details + # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details + # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting + # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE + # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders + # RETURN + # A boxplot if plot argument is TRUE + # A list of the graph info if return argument is TRUE: + # $data: the initial data with graphic information added + # $stat: the graphic statistics (mostly equivalent to ggplot_build()$data[[2]]) + # $removed.row.nb: which rows have been removed due to NA/Inf detection in y and categ columns (NULL if no row removed) + # $removed.rows: removed rows (NULL if no row removed) + # $plot: the graphic box and dot coordinates + # $dots: dot coordinates + # $main.box: coordinates of boxes + # $median: median coordinates + # $sup.whisker: coordinates of top whiskers (y for base and y.end for extremities) + # $inf.whisker: coordinates of bottom whiskers (y for base and y.end for extremities) + # $sup.whisker.edge: coordinates of top whisker edges (x and xend) + # $inf.whisker.edge: coordinates of bottom whisker edges(x and xend) + # $mean: diamond mean coordinates (only if box.mean argument is TRUE) + # $stat.pos: coordinates of stat numbers (only if stat.pos argument is not NULL) + # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no") + # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no") + # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0 + # $axes: the x-axis and y-axis info + # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE + # $ggplot: ggplot object that can be used for reprint (use print(...$ggplot) or update (use ...$ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-NULL $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot + # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot + # REQUIRED PACKAGES + # ggplot2 + # gridExtra + # lemon (in case of use in the add argument) + # scales + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # fun_check() + # fun_comp_1d() + # fun_comp_2d() + # fun_gg_just() + # fun_gg_palette() + # fun_inter_ticks() + # fun_name_change() + # fun_pack() + # fun_round() + # fun_scale() + # EXAMPLE + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(20, 100, 10), rnorm(20, 200, 50), rnorm(20, 500, 60), rnorm(20, 100, 50)), Categ1 = rep(c("CAT", "DOG"), times = 40), Categ2 = rep(c("A", "B", "C", "D"), each = 20), Color1 = rep(c("coral", "lightblue"), times = 40), Color2 = rep(c("#9F2108", "#306100", "#007479", "#8500C0"), each = 20), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Categ1") + # see http + # DEBUGGING + # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Categ1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$Time[1:10] <- NA ; data1 = obs1 ; y = "Time" ; categ = c("Categ1") ; categ.class.order = NULL ; categ.color = NULL ; box.legend.name = NULL ; box.fill = FALSE ; box.width = 0.5 ; box.space = 0.1 ; box.line.size = 0.75 ; box.notch = FALSE ; box.alpha = 1 ; box.mean = TRUE ; box.whisker.kind = "std" ; box.whisker.width = 0 ; dot.color = grey(0.25) ; dot.categ = NULL ; dot.categ.class.order = NULL ; dot.legend.name = NULL ; dot.tidy = FALSE ; dot.tidy.bin.nb = 50 ; dot.jitter = 0.5 ; dot.seed = 2 ; dot.size = 3 ; dot.alpha = 0.5 ; dot.border.size = 0.5 ; dot.border.color = NULL ; x.lab = NULL ; x.angle = 0 ; y.lab = NULL ; y.lim = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = 1 ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; stat.pos = "top" ; stat.mean = FALSE ; stat.size = 4 ; stat.dist = 5 ; stat.angle = 0 ; vertical = TRUE ; text.size = 12 ; title = "" ; title.text.size = 8 ; legend.show = TRUE ; legend.width = 0.5 ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_comp_2d", + "fun_gg_just", + "fun_gg_palette", + "fun_name_change", + "fun_pack", + "fun_check", + "fun_round", + "fun_scale", + "fun_inter_ticks" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function")) == 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words to avoid bugs (names of dataframe columns used in this function) + reserved.words <- c("categ.check", "categ.color", "dot.color", "dot.categ", "dot.max", "dot.min", "group", "PANEL", "group.check", "MEAN", "tempo.categ1", "tempo.categ2", "text.max.pos", "text.min.pos", "x", "x.y", "y", "y.check", "y_from.dot.max", "ymax", "tidy_group", "binwidth") + # end reserved words to avoid bugs (used in this function) + # arg with no default values + mandat.args <- c( + "data1", + "y", + "categ" + ) + tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")"))) + if(any(tempo)){ # normally no NA for missing() output + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = categ, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if( ! is.null(categ.class.order)){ + tempo <- fun_check(data = categ.class.order, class = "list", fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = categ.class.order, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(box.legend.name)){ + tempo <- fun_check(data = box.legend.name, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = box.legend.name, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(categ.color)){ + tempo1 <- fun_check(data = categ.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = categ.color, class = "factor", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem + if(tempo.check.color == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(any(categ.color == 0L, na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = categ.color, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = box.fill, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.space, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.notch, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.mean, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.whisker.kind, options = c("no", "std", "max"), length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = box.whisker.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(dot.color)){ + tempo1 <- fun_check(data = dot.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = dot.color, class = "factor", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem + if(tempo.check.color == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(any(dot.color == 0L, na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.color, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(dot.categ)){ + tempo <- fun_check(data = dot.categ, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.categ, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(dot.categ.class.order)){ + tempo <- fun_check(data = dot.categ.class.order, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.categ.class.order, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(dot.legend.name)){ + tempo <- fun_check(data = dot.legend.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.legend.name, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = dot.tidy, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dot.tidy.bin.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if(dot.tidy.bin.nb == 0L){ # length and NA checked above + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.tidy.bin.nb ARGUMENT MUST BE A NON-NULL AND POSITVE INTEGER VALUE") # integer possible because dealt above + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + tempo <- fun_check(data = dot.jitter, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(dot.seed)){ + tempo <- fun_check(data = dot.seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.seed, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dot.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if( ! is.null(dot.border.color)){ + tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + }else if(tempo1$problem == FALSE & tempo2$problem == TRUE){ + if( ! all(dot.border.color %in% colors() | grepl(pattern = "^#", dot.border.color), na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.border.color, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(x.lab)){ + tempo1 <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x.lab, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = x.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) + if( ! is.null(y.lab)){ + tempo1 <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.lab, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(y.lim)){ + tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if(any(is.infinite(y.lim))){ # normally no NA for is.infinite() output + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.lim, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(y.tick.nb)){ + tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if(y.tick.nb < 0){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(y.second.tick.nb)){ + tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if(y.second.tick.nb <= 0){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.second.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.second.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(stat.pos)){ + tempo <- fun_check(data = stat.pos, options = c("top", "above"), length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = stat.pos, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = stat.mean, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = stat.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = stat.dist, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = stat.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = vertical, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(legend.width)){ + tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = legend.width, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(add)){ + tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = add, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path), na.rm = TRUE)){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = lib.path, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if(any(arg.check) == TRUE){ # normally no NA + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "data1", + "y", + "categ", + "box.fill", + "box.width", + "box.space", + "box.line.size", + "box.notch", + "box.alpha", + "box.mean", + "box.whisker.kind", + "box.whisker.width", + # "dot.color", # inactivated because can be null + "dot.tidy", + "dot.tidy.bin.nb", + "dot.jitter", + # "dot.seed", # inactivated because can be null + "dot.size", + "dot.alpha", + "dot.border.size", + "x.angle", + "y.log", + # "y.second.tick.nb", # inactivated because can be null + "y.include.zero", + "y.top.extra.margin", + "y.bottom.extra.margin", + # "stat.pos", # inactivated because can be null + "stat.mean", + "stat.size", + "stat.dist", + "stat.angle", + "vertical", + "text.size", + "title", + "title.text.size", + "legend.show", + # "legend.width", # inactivated because can be null + "article", + "grid", + "return", + "return.ggplot", + "return.gtable", + "plot", + "warn.print" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){# normally no NA with is.null() + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # see also Protocol 100-rev0 Parallelization in R.docx + if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment + tempo.random.seed <- .Random.seed + on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv)) + }else{ + on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness + } + set.seed(dot.seed) + # end code that protects set.seed() in the global environment + # warning initiation + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + # end warning initiation + # other checkings + if(any(duplicated(names(data1)), na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! (y %in% names(data1))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE A COLUMN NAME OF data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + tempo <- fun_check(data = data1[, y], data.name = "y COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) + if(tempo$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE NUMERIC COLUMN IN data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(length(categ) > 2){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT CANNOT HAVE MORE THAN 2 COLUMN NAMES OF data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! all(categ %in% names(data1))){ # all() without na.rm -> ok because categ cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(categ, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(length(dot.categ) > 1){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT HAVE MORE THAN 1 COLUMN NAMES OF data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! all(dot.categ %in% names(data1))){ # all() without na.rm -> ok because dot.categ cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(dot.categ, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # reserved word checking + if(any(names(data1) %in% reserved.words, na.rm = TRUE)){ + if(any(duplicated(names(data1)), na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(dot.categ)){ + if(dot.categ %in% categ){ + reserved.words <- c(reserved.words, paste0(dot.categ, "_DOT")) # paste0(dot.categ, "_DOT") is added to the reserved words because in such situation, a new column will be added to data1 that is named paste0(dot.categ, "_DOT") + } + } + tempo.output <- fun_name_change(names(data1), reserved.words) + for(i2 in 1:length(tempo.output$ini)){ # a loop to be sure to take the good ones + names(data1)[names(data1) == tempo.output$ini[i2]] <- tempo.output$post[i2] + if(any(y == tempo.output$ini[i2])){ # any() without na.rm -> ok because y cannot be NA (tested above) + y[y == tempo.output$ini[i2]] <- tempo.output$post[i2] + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN y ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN y ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # WARNING: names of y argument potentially replaced + if(any(categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because categ cannot be NA (tested above) + categ[categ == tempo.output$ini[i2]] <- tempo.output$post[i2] + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # WARNING: names of categ argument potentially replaced + if( ! is.null(dot.categ)){ + if(any(dot.categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because dot.categ cannot be NA (tested above) + dot.categ[dot.categ == tempo.output$ini[i2]] <- tempo.output$post[i2] + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN dot.categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # WARNING: names of dot.categ argument potentially replaced + } + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") REGARDING COLUMN NAMES REPLACEMENT, THE NAMES\n", paste(tempo.output$ini, collapse = " "), "\nHAVE BEEN REPLACED BY\n", paste(tempo.output$post, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + if( ! (is.null(add) | is.null(tempo.output$ini))){ + if(grepl(x = add, pattern = paste(tempo.output$ini, collapse = "|"))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nCOLUMN NAMES HAVE TO BE CHANGED\nTHE PROBLEMATIC COLUMN NAMES ARE SOME OF THESE NAMES:\n", paste(tempo.output$ini, collapse = " "), "\nIN THE DATA FRAME OF data1 AND IN THE STRING OF add ARGUMENT, TRY TO REPLACE NAMES BY:\n", paste(tempo.output$post, collapse = " "), "\n\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + } + if( ! (is.null(add))){ + if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end reserved word checking + # verif of add + if( ! is.null(add)){ + if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by + + tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ # + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by ) + tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end verif of add + # management of add containing facet + facet.categ <- NULL + if( ! is.null(add)){ + facet.check <- TRUE + tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) # + tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap") + tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid") + tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep") + if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){ + tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")]))) + facet.categ <- names(tempo1$params$facets) + tempo.text <- "facet_wrap OR facet_rep_wrap" + facet.check <- FALSE + }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){ + tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")]))) + facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols)) + tempo.text <- "facet_grid OR facet_rep_grid" + facet.check <- FALSE + } + if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end management of add containing facet + # conversion of categ columns in data1 into factors + for(i1 in 1:length(categ)){ + tempo1 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\n", paste0("categ NUMBER ", i1, " OF data1"), " MUST BE A FACTOR OR CHARACTER VECTOR") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(tempo1$problem == FALSE){ # character vector + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN categ NUMBER ", i1, " IN data1, THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR, WITH LEVELS ACCORDING TO THE ALPHABETICAL ORDER") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + data1[, categ[i1]] <- factor(data1[, categ[i1]]) # if already a factor, change nothing, if characters, levels according to alphabetical order + } + # OK: all the categ columns of data1 are factors from here + # end conversion of categ columns in data1 into factors + + + + # management of log scale and Inf removal + if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite0() and is.na() + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + data1.ini <- data1 # strictly identical to data1 except that in data1 y is log converted if and only if y.log != "no" + if(y.log != "no"){ + tempo1 <- ! is.finite(data1[, y]) # where are initial NA and Inf + data1[, y] <- suppressWarnings(get(y.log)(data1[, y]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + if(any( ! (tempo1 | is.finite(data1[, y])))){ # normally no NA with is.finite + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # Inf removal + if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite + removed.row.nb <- which(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y]))) + removed.rows <- data1.ini[removed.row.nb, ] # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation + data1 <- data1[-removed.row.nb, ] # + data1.ini <- data1.ini[-removed.row.nb, ] # + }else{ + removed.row.nb <- NULL + removed.rows <- data.frame(stringsAsFactors = FALSE) + } + # From here, data1 and data.ini have no more Inf + # end Inf removal + if(y.log != "no" & ! is.null(y.lim)){ + if(any(y.lim <= 0)){ # any() without na.rm -> ok because y.lim cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ # normally no NA with is.finite + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(y.log != "no" & y.include.zero == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + y.include.zero <- FALSE + } + if(y.log != "no" & vertical == FALSE){ + vertical <- TRUE + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") BECAUSE OF A BUG IN ggplot2, CANNOT FLIP BOXES HORIZONTALLY WITH A Y.LOG SCALE -> vertical ARGUMENT RESET TO TRUE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end management of log scale and Inf removal + # na detection and removal (done now to be sure of the correct length of categ) + column.check <- unique(c(y, categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){dot.categ}, if( ! is.null(facet.categ)){facet.categ})) # dot.categ because can be a 3rd column of data1, categ.color and dot.color will be tested later + if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + for(i2 in 1:length(column.check)){ + if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na + tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn))) + } + } + tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which))) + removed.row.nb <- c(removed.row.nb, tempo) # removed.row.nb created to remove Inf + removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance) + column.check <- column.check[ ! column.check == y] # remove y to keep quali columns + if(length(tempo) != 0){ + data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former + data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former + for(i3 in 1:length(column.check)){ + if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + } + count.categ <- 0 + for(i2 in 1:length(column.check)){ + if(column.check[i2] %in% categ){ + count.categ <- count.categ + 1 + } + if(column.check[i2] == categ[count.categ]){ + categ.class.order[count.categ] <- list(levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])]) # remove the absent color in the character vector + data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.class.order[[count.categ]])) + } + if( ! is.null(dot.color) & ! is.null(dot.categ)){ # reminder : dot.categ cannot be a column name of categ anymore (because in that case dot.categ name is changed into "..._DOT" + if(column.check[i2] == dot.categ){ + dot.categ.class.order <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector + data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.categ.class.order)) + } + } + if(column.check[i2] %in% facet.categ){ # works if facet.categ == NULL this method should keep the order of levels when removing some levels + tempo.levels <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(as.character(data1[, column.check[i2]]))] + data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = tempo.levels) + } + } + } + # end na detection and removal (done now to be sure of the correct length of categ) + # From here, data1 and data.ini have no more NA or NaN in y, categ, dot.categ (if dot.color != NULL) and facet.categ + + + + if( ! is.null(categ.class.order)){ + if(length(categ.class.order) != length(categ)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.class.order ARGUMENT MUST BE A LIST OF LENGTH EQUAL TO LENGTH OF categ\nHERE IT IS LENGTH: ", length(categ.class.order), " VERSUS ", length(categ)) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + for(i3 in 1:length(categ.class.order)){ + if(is.null(categ.class.order[[i3]])){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i3, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + data1[, categ[i3]] <- factor(as.character(data1[, categ[i3]])) # if already a factor, change nothing, if characters, levels according to alphabetical order + categ.class.order[[i3]] <- levels(data1[, categ[i3]]) # character vector that will be used later + }else{ + tempo <- fun_check(data = categ.class.order[[i3]], data.name = paste0("COMPARTMENT ", i3 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[, categ[i3]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(any(duplicated(categ.class.order[[i3]]), na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i3]], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! (all(categ.class.order[[i3]] %in% unique(data1[, categ[i3]]), na.rm = TRUE) & all(unique(data1[, categ[i3]]) %in% categ.class.order[[i3]], na.rm = TRUE))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT MUST BE CLASSES OF ELEMENT ", i3, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i3]], collapse = " "), "\nFOR COMPARTMENT ", i3, " OF categ.class.order AND IT IS:\n", paste(unique(data1[, categ[i3]]), collapse = " "), "\nFOR COLUMN ", categ[i3], " OF data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + data1[, categ[i3]] <- factor(data1[, categ[i3]], levels = categ.class.order[[i3]]) # reorder the factor + + } + names(categ.class.order)[i3] <- categ[i3] + } + } + }else{ + categ.class.order <- vector("list", length = length(categ)) + tempo.categ.class.order <- NULL + for(i2 in 1:length(categ.class.order)){ + categ.class.order[[i2]] <- levels(data1[, categ[i2]]) + names(categ.class.order)[i2] <- categ[i2] + tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]]) + } + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR BOX ORDERING:\n", paste(tempo.categ.class.order, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # categ.class.order not NULL anymore (list) + if(is.null(box.legend.name) & box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE box.legend.name SETTING IS NULL. NAMES OF categ WILL BE USED: ", paste(categ, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + box.legend.name <- categ[length(categ)] # if only categ1, then legend name of categ1, if length(categ) == 2L, then legend name of categ2 + } + # box.legend.name not NULL anymore (character string) + # management of categ.color + if( ! is.null(categ.color)){ + # check the nature of color + # integer colors into gg_palette + tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem + if(tempo.check.color == FALSE){ + # convert integers into colors + categ.color <- fun_gg_palette(max(categ.color, na.rm = TRUE))[categ.color] + } + # end integer colors into gg_palette + if( ! (all(categ.color %in% colors() | grepl(pattern = "^#", categ.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because categ.color cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors() OR A COLUMN NAME OF THE data1 PARAMETER: ", paste(unique(categ.color), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(is.na(categ.color)) & box.alpha != 0){ # normally no NA with is.na + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT CONTAINS NA") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end check the nature of color + # check the length of color + categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 + if(length(data1[, categ[categ.len]]) == length(levels(data1[, categ[categ.len]])) & length(categ.color) == length(data1[, categ[categ.len]])){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", categ[categ.len], " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE categ.color ARGUMENT ARE ALL EQUAL. BOX COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", categ[categ.len], ", NOT ACCORDING TO THE ROWS OF ", categ[categ.len]) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if(length(categ.color) == length(levels(data1[, categ[categ.len]]))){ # here length(categ.color) is equal to the different number of categ + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) # no need stringsAsFactors here for stat.nolog as factors remain factors + data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]]) + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(categ.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else if(length(categ.color) == length(data1[, categ[categ.len]])){# here length(categ.color) is equal to nrow(data1) -> Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]]))) + data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE) + tempo.check <- unique(data1[ , c(categ[categ.len], "categ.color")]) + if( ! (nrow(tempo.check) == length(unique(categ.color)) & nrow(tempo.check) == length(unique(data1[ , categ[categ.len]])))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], ":\n", paste(unique(mapply(FUN = "paste", data1[ ,categ[categ.len]], data1[ ,"categ.color"])), collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + categ.color <- unique(data1$categ.color[order(data1[, categ[categ.len]])]) # Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]]))) + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], " AS:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\n", paste(categ.color, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + }else if(length(categ.color) == 1L){ + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE) + categ.color <- rep(categ.color, length(levels(data1[, categ[categ.len]]))) + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(categ.color, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(categ.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + categ.color <- fun_gg_palette(length(levels(data1[, categ[categ.len]]))) + data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) + data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]]) + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL categ.color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", categ[categ.len], " IN data1:\n", paste(categ.color, collapse = " "), "\n", paste(levels(data1[, categ[categ.len]]), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # categ.color not NULL anymore + categ.color <- as.character(categ.color) + # categ.color is a character string representing the diff classes + data1$categ.color <- factor(data1$categ.color, levels = unique(categ.color)) # ok because if categ.color is a character string, the order make class 1, class 2, etc. unique() because no duplicates allowed + # data1$categ.color is a factor with order of levels -> categ.color + # end management of categ.color + # management of dot.color + if( ! is.null(dot.color)){ + # optional legend of dot colors + if( ! is.null(dot.categ)){ + ini.dot.categ <- dot.categ + if( ! dot.categ %in% names(data1)){ # no need to use all() because length(dot.categ) = 1 + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE A COLUMN NAME OF data1. HERE IT IS:\n", dot.categ) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(dot.categ %in% categ){ # no need to use all() because length(dot.categ) = 1. Do not use dot.categ %in% categ[length(categ)] -> error + # management of dot legend if dot.categ %in% categ (because legends with the same name are joined in ggplot2) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE COLUMN NAME OF data1 INDICATED IN THE dot.categ ARGUMENT (", dot.categ, ") HAS BEEN REPLACED BY ", paste0(dot.categ, "_DOT"), " TO AVOID MERGED LEGEND BY GGPLOT2") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + data1 <- data.frame(data1, dot.categ = data1[, dot.categ], stringsAsFactors = TRUE) # dot.categ is not a column name of data1 (checked above with reserved words) + dot.categ <- paste0(dot.categ, "_DOT") + names(data1)[names(data1) == "dot.categ"] <- dot.categ # paste0(dot.categ, "_DOT") is not a column name of data1 (checked above with reserved words) + # tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT BE A COLUMN NAME OF data1 ALREADY SPECIFIED IN THE categ ARGUMENT:\n", dot.categ, "\nINDEED, dot.categ ARGUMENT IS MADE TO HAVE MULTIPLE DOT COLORS NOT RELATED TO THE BOXPLOT CATEGORIES") + # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + tempo1 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ COLUMN MUST BE A FACTOR OR CHARACTER VECTOR") # + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + data1[, dot.categ] <- factor(data1[, dot.categ]) # if already a factor, change nothing, if characters, levels according to alphabetical order + # dot.categ column of data1 is factor from here + if( ! is.null(dot.categ.class.order)){ + if(any(duplicated(dot.categ.class.order), na.rm = TRUE)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(dot.categ.class.order, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! (all(dot.categ.class.order %in% levels(data1[, dot.categ])) & all(levels(data1[, dot.categ]) %in% dot.categ.class.order, na.rm = TRUE))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT MUST BE CLASSES OF dot.categ ARGUMENT\nHERE IT IS:\n", paste(dot.categ.class.order, collapse = " "), "\nFOR dot.categ.class.order AND IT IS:\n", paste(levels(data1[, dot.categ]), collapse = " "), "\nFOR dot.categ COLUMN (", ini.dot.categ, ") OF data1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor + } + }else{ + if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) + dot.categ.class.order <- unlist(categ.class.order[length(categ)]) + data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL AND dot.color IS \"same\". ORDER OF categ.class.order WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + dot.categ.class.order <- sort(levels(data1[, dot.categ])) + data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # dot.categ.class.order not NULL anymore (character string) if dot.categ is not NULL + if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) + if( ! identical(ini.dot.categ, categ[length(categ)])){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\", THE COLUMN NAME IN dot.categ ARGUMENT MUST BE IDENTICAL TO THE LAST COLUMN NAME IN categ ARGUMENT. HERE IT IS:\ndot.categ: ", paste(ini.dot.categ, collapse = " "), "\ncateg: ", paste(categ, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if( ! fun_comp_1d(unlist(categ.class.order[length(categ)]), dot.categ.class.order)$identical.content){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\",\nLAST COMPARTMENT OF categ.class.order ARGUMENT AND dot.categ.class.order ARGUMENT CANNOT BE DIFFERENT:\nLAST COMPARTMENT OF categ.class.order: ", paste(unlist(categ.class.order[length(categ)]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + for(i3 in 1:length(categ)){ + if(identical(categ[i3], ini.dot.categ) & ! identical(unlist(categ.class.order[i3]), dot.categ.class.order) & identical(sort(unlist(categ.class.order[i3])), sort(dot.categ.class.order))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE dot.categ ARGUMENT SETTING IS PRESENT IN THE categ ARGUMENT SETTING, BUT ORDER OF THE CLASSES IS NOT THE SAME:\ncateg.class.order: ", paste(unlist(categ.class.order[i3]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "), "\nNOTE THAT ORDER OF categ.class.order IS THE ONE USED FOR THE AXIS REPRESENTATION") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(is.null(dot.legend.name)){ + dot.legend.name <- if(ini.dot.categ %in% categ[length(categ)]){dot.categ}else{ini.dot.categ} # + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE dot.legend.name SETTING IS NULL -> ", dot.legend.name, " WILL BE USED AS LEGEND TITLE OF DOTS") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # dot.legend.name not NULL anymore (character string) + }else{ + if( ! is.null(dot.categ.class.order)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order ARGUMENT IS NOT NULL, BUT IS THE dot.categ ARGUMENT\n-> dot.categ.class.order NOT CONSIDERED AS NO LEGEND WILL BE DRAWN") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # But dot.categ.class.order will be converted to NULL below (not now) + } + # end optional legend of dot colors + # check the nature of color + # integer colors into gg_palette + tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem + if(tempo.check.color == FALSE){ + # convert integers into colors + dot.color <- fun_gg_palette(max(dot.color, na.rm = TRUE))[dot.color] + } + # end integer colors into gg_palette + if(all(dot.color == "same") & length(dot.color)== 1L){# all() without na.rm -> ok because dot.color cannot be NA (tested above) + dot.color <- categ.color # same color of the dots as the corresponding box color + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS BEEN SET TO \"same\"\nTHUS, DOTS WILL HAVE THE SAME COLORS AS THE CORRESPONDING BOXPLOT") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else if( ! (all(dot.color %in% colors() | grepl(pattern = "^#", dot.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because dot.color cannot be NA (tested above) + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR VECTOR STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGERS, OR THE STRING \"same\"\nHERE IT IS: ", paste(unique(dot.color), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(any(is.na(dot.color))){ # normally no NA with is.finite + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT CONTAINS NA") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end check the nature of color + # check the length of color + if( ! is.null(dot.categ)){ + # optional legend of dot colors + if(length(data1[, dot.categ]) == length(levels(data1[, dot.categ])) & length(dot.color) == length(data1[, dot.categ])){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", dot.categ, " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE dot.color ARGUMENT ARE ALL EQUAL. DOT COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", dot.categ, ", NOT ACCORDING TO THE ROWS OF ", dot.categ) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if(length(dot.color) > 1 & ! (length(dot.color) == length(unique(data1[, dot.categ])) | length(dot.color) == length(data1[, dot.categ]))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN LENGTH OF THE dot.color ARGUMENT IS MORE THAN 1, IT MUST BE EQUAL TO THE NUMBER OF 1) ROWS OR 2) LEVELS OF dot.categ COLUMN (", dot.categ, "):\ndot.color: ", paste(dot.color, collapse = " "), "\ndot.categ LEVELS: ", paste(levels(data1[, dot.categ]), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else if(length(dot.color) > 1 & length(dot.color) == length(unique(data1[, dot.categ]))){ + data1 <- data.frame(data1, dot.color = data1[, dot.categ], stringsAsFactors = TRUE) + data1$dot.color <- factor(data1$dot.color, labels = dot.color) # do not use labels = unique(dot.color). Otherwise, we can have green1 green2 when dot.color is c("green", "green") + }else if(length(dot.color) > 1 & length(dot.color) == length(data1[, dot.categ])){ + data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) + }else if(length(dot.color)== 1L){ # to deal with single color. Warning: & length(dot.categ.class.order) > 1 removed because otherwise, the data1 is not with dot.color column when length(dot.categ.class.order) == 1 + data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) + } + dot.color <- as.character(unique(data1$dot.color[order(data1[, dot.categ])])) # reorder the dot.color character vector + if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color + dot.color <- rep(dot.color, length(dot.categ.class.order)) + } + tempo.check <- unique(data1[ , c(dot.categ, "dot.color")]) + if(length(unique(data1[ , "dot.color"])) > 1 & ( ! (nrow(tempo.check) == length(unique(data1[ , "dot.color"])) & nrow(tempo.check) == length(unique(data1[ , dot.categ]))))){ # length(unique(data1[ , "dot.color"])) > 1 because if only one color, can be attributed to each class of dot.categ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF dot.categ (", dot.categ, ") COLUMN:\n", paste(unique(mapply(FUN = "paste", data1[ , dot.categ], data1[ ,"dot.color"])), collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (", ini.dot.categ, "), THE FOLLOWING COLORS OF DOTS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(data1[, dot.categ]), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # dot.color is a character string representing the diff classes of dot.categ + # data1$dot.color is a factor with order of levels -> dot.categ + # end optional legend of dot colors + }else{ + categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2 + if(length(dot.color) == length(levels(data1[, categ[categ.len]]))){ # here length(dot.color) is equal to the different number of categ + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + data1 <- data.frame(data1, dot.color = data1[, categ[categ.len]], stringsAsFactors = TRUE) + data1$dot.color <- factor(data1$dot.color, labels = dot.color) + if(box.alpha != 0){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else if(length(dot.color) == length(data1[, categ[categ.len]])){# here length(dot.color) is equal to nrow(data1) -> Modif to have length(dot.color) equal to the different number of categ (length(dot.color) == length(levels(data1[, categ[categ.len]]))) + data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) + }else if(length(dot.color)== 1L & ! all(dot.color == "same")){ # all() without na.rm -> ok because dot.color cannot be NA (tested above) + # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor + data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE) + dot.color <- rep(dot.color, length(levels(data1[, categ[categ.len]]))) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(dot.color, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(dot.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end check the length of color + dot.color <- as.character(dot.color) + # dot.color is a character string representing the diff classes + data1$dot.color <- factor(data1$dot.color, levels = unique(dot.color)) # ok because if dot.color is a character string, the order make class 1, class 2, etc. If dot.color is a column of data1, then levels will be created, without incidence, except if dot.categ specified (see below). unique() because no duplicates allowed + # data1$dot.color is a factor with order of levels -> dot.color + } + # end optional legend of dot colors + }else if(is.null(dot.color) & ! (is.null(dot.categ) & is.null(dot.categ.class.order) & is.null(dot.legend.name))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") dot.categ OR dot.categ.class.order OR dot.legend.name ARGUMENT HAS BEEN SPECIFIED BUT dot.color ARGUMENT IS NULL (NO DOT PLOTTED)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # dot.color either NULL (no dot plotted) or character string (potentially representing the diff classes of dot.categ) + # data1$dot.color is either NA or a factor (with order of levels -> depending on dot.categ or categ[length(categ)], or other + if(is.null(dot.categ)){ + dot.categ.class.order <- NULL # because not used anyway + } + # dot.categ.class.order either NULL if dot.categ is NULL (no legend displayed) or character string (potentially representing the diff classes of dot.categ) + # end management of dot.color + if(is.null(dot.color) & box.fill == FALSE & dot.alpha <= 0.025){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.alpha = ", fun_round(dot.alpha, 4), "\n-> POTENTIAL OUTLIER DOTS MIGHT NOT BE VISIBLE BECAUSE ALMOST TRANSPARENT") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if(is.null(dot.color) & box.fill == FALSE & dot.border.size == 0){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.border.size = 0 (NO BORDER FOR POTENTIAL OUTLIER DOTS)\n-> THESE SETTINGS ARE NOT ALLOWED BECAUSE THE POTENTIAL OUTLIER DOTS WILL NOT BE VISIBLE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # integer dot.border.color into gg_palette + if( ! is.null(dot.border.color)){ + tempo <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) + if(tempo$problem == FALSE){ # convert integers into colors + dot.border.color <- fun_gg_palette(max(dot.border.color, na.rm = TRUE))[dot.border.color] + } + } + # end integer dot.border.color into gg_palette + # na detection and removal (done now to be sure of the correct length of categ) + column.check <- c("categ.color", if( ! is.null(dot.color)){"dot.color"}) # + if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + for(i2 in 1:length(column.check)){ + if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na + tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn))) + } + } + tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which))) + removed.row.nb <- c(removed.row.nb, tempo) + removed.rows <- rbind(removed.rows, data1[tempo, ], stringsAsFactors = FALSE) # here data1 used because categorical columns tested + if(length(tempo) != 0){ + data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former + data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former + for(i3 in 1:length(column.check)){ + if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + } + for(i2 in 1:length(column.check)){ + if(column.check[i2] == "categ.color"){ + categ.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector + if(length(categ.color)== 1L & length(unlist(categ.class.order[length(categ)])) > 1){ # to deal with single color + categ.color <- rep(categ.color, length(unlist(categ.class.order[length(categ)]))) + } + data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.color)) + } + if(column.check[i2] == "dot.color"){ + dot.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector + if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color. If dot.categ.class.order == NULL (which is systematically the case if dot.categ == NULL), no rep(dot.color, length(dot.categ.class.order) + dot.color <- rep(dot.color, length(dot.categ.class.order)) + } + data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.color)) + } + } + } + # end na detection and removal (done now to be sure of the correct length of categ) + # From here, data1 and data.ini have no more NA or NaN + # end other checkings + # reserved word checking + #already done above + # end reserved word checking + # end second round of checking and data preparation + + + # package checking + fun_pack(req.package = c( + "ggplot2", + "gridExtra", + "lemon", + "scales" + ), lib.path = lib.path) + # end package checking + + + + + + # main code + # y coordinates recovery (create ini.box.coord, dot.coord and modify data1) + if(length(categ)== 1L){ + # width commputations + box.width2 <- box.width + box.space <- 0 # to inactivate the shrink that add space between grouped boxes, because no grouped boxes here + # end width commputations + # data1 check categ order for dots coordinates recovery + data1 <- data.frame(data1, categ.check = data1[, categ[1]], stringsAsFactors = TRUE) + data1$categ.check <- as.integer(data1$categ.check) # to check that data1[, categ[1]] and dot.coord$group are similar, during merging + # end data1 check categ order for dots coordinates recovery + # per box dots coordinates recovery + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[1]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21)) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[1]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[1]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) + # end per box dots coordinates recovery + }else if(length(categ) == 2L){ + # width commputations + box.width2 <- box.width / length(unique(data1[, categ[length(categ)]])) # real width of each box in x-axis unit, among the set of grouped box. Not relevant if no grouped boxes length(categ)== 1L + # end width commputations + # data1 check categ order for dots coordinates recovery + tempo.factor <- paste0(data1[order(data1[, categ[2]], data1[, categ[1]]), categ[2]], "_", data1[order(data1[, categ[2]], data1[, categ[1]]), categ[1]]) + data1 <- data.frame(data1[order(data1[, categ[2]], data1[, categ[1]]), ], categ.check = factor(tempo.factor, levels = unique(tempo.factor)), stringsAsFactors = TRUE) + data1$categ.check <- as.integer(data1$categ.check) + # end data1 check categ order for dots coordinates recovery + # per box dots coordinates recovery + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[2]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21)) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[2]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[2]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) + # end per box dots coordinates recovery + }else{ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if( ! is.null(stat.pos)){ + stat.just <- fun_gg_just( + angle = stat.angle, + pos = ifelse( + vertical == TRUE, + ifelse(stat.pos == "top", "bottom", "top"), # "bottom" because we want justification for text that are below the ref point which is the top of the graph. The opposite for "above" + ifelse(stat.pos == "top", "left", "right") # "left" because we want justification for text that are on the left of the ref point which is the right border of the graph. The opposite for "above" + ), + kind = "text" + ) + } + # has in fact no interest because ggplot2 does not create room for geom_text() + tempo.data.max <- data1[which.max(data1[, y]), ] + tempo.data.max <- data.frame(tempo.data.max, label = formatC(tempo.data.max[, y], digit = 2, drop0trailing = TRUE, format = "f"), stringsAsFactors = TRUE) + # end has in fact no interest because ggplot2 does not create room for geom_text() + tempo.graph.info.ini <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if( ! is.null(stat.pos)){' + ggplot2::geom_text(data = tempo.data.max, mapping = ggplot2::aes_string(x = 1, y = y, label = "label"), size = stat.size, color = "black", angle = stat.angle, hjust = stat.just$hjust, vjust = stat.just$vjust)'})))) # added here to have room for annotation + dot.coord <- tempo.graph.info.ini$data[[1]] + dot.coord$x <- as.numeric(dot.coord$x) # because weird class + dot.coord$PANEL <- as.numeric(dot.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? + tempo.mean <- aggregate(x = dot.coord$y, by = list(dot.coord$group, dot.coord$PANEL), FUN = mean, na.rm = TRUE) + names(tempo.mean)[names(tempo.mean) == "x"] <- "MEAN" + names(tempo.mean)[names(tempo.mean) == "Group.1"] <- "BOX" + names(tempo.mean)[names(tempo.mean) == "Group.2"] <- "PANEL" + dot.coord <- data.frame( + dot.coord[order(dot.coord$group, dot.coord$y), ], # dot.coord$PANEL deals below + y.check = as.double(data1[order(data1$categ.check, data1[, y]), y]), + categ.check = data1[order(data1$categ.check, data1[, y]), "categ.check"], + dot.color = if(is.null(dot.color)){NA}else{data1[order(data1$categ.check, data1[, y]), "dot.color"]}, + data1[order(data1$categ.check, data1[, y]), ][categ], # avoid the renaming below + stringsAsFactors = TRUE + ) # y.check to be sure that the order is the same between the y of data1 and the y of dot.coord + # names(dot.coord)[names(dot.coord) == "tempo.categ1"] <- categ[1] + if( ! is.null(dot.categ)){ + dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][dot.categ], stringsAsFactors = TRUE) # avoid the renaming + } + if( ! is.null(facet.categ)){ + dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][facet.categ], stringsAsFactors = TRUE) # for facet panels + tempo.test <- NULL + for(i2 in 1:length(facet.categ)){ + tempo.test <- paste0(tempo.test, ".", formatC(as.numeric(dot.coord[, facet.categ[i2]]), width = nchar(max(as.numeric(dot.coord[, facet.categ[i2]]), na.rm = TRUE)), flag = "0")) # convert factor into numeric with leading zero for proper ranking # merge the formatC() to create a new factor. The convertion to integer should recreate the correct group number. Here as.numeric is used and not as.integer in case of numeric in facet.categ (because comes from add and not checked by fun_check, contrary to categ) + } + tempo.test <- as.integer(factor(tempo.test)) + if( ! identical(as.integer(dot.coord$PANEL), tempo.test)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nas.integer(dot.coord$PANEL) AND tempo.test MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(dot.tidy == TRUE){ + if( ! is.null(dot.categ)){ + dot.coord <- data.frame(dot.coord, tidy_group = data1[order(data1$categ.check, data1[, y]), ][, dot.categ], stringsAsFactors = TRUE) # avoid the renaming + # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates + if(dot.categ %in% categ){ + dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE) + }else{ + dot.coord <- data.frame(dot.coord, tidy_group_coord = as.integer(factor(paste0( + formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking + ".", + if(length(categ) == 2L){formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")}, # convert factor into numeric with leading zero for proper ranking + if(length(categ) == 2L){"."}, + formatC(as.integer(dot.coord[, dot.categ]), width = nchar(max(as.integer(dot.coord[, dot.categ]), na.rm = TRUE)), flag = "0") # convert factor into numeric with leading zero for proper ranking + )), stringsAsFactors = TRUE) # merge the 2 or 3 formatC() to create a new factor. The convertion to integer should recreate the correct group number + ) # for tidy dot plots + } + }else{ + dot.coord <- data.frame(dot.coord, tidy_group = if(length(categ)== 1L){ + dot.coord[, categ]}else{as.integer(factor(paste0( + formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking + ".", + formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking + )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number + }) # for tidy dot plots + # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates + dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE) + } + } + if( ! (identical(dot.coord$y, dot.coord$y.check) & identical(dot.coord$group, dot.coord$categ.check))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(dot.coord$y AND dot.coord$y.check) AS WELL AS (dot.coord$group AND dot.coord$categ.check) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + if( ! identical(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ]$BOX, unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c("group", "PANEL")])$group)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(tempo.mean$BOX, tempo.mean$PANEL) AND (dot.coord$group, dot.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + tempo <- unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}), drop = FALSE]) + # names(tempo) <- paste0(names(tempo), ".mean") + tempo.mean <- data.frame(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ], tempo, stringsAsFactors = TRUE) + } + } + # at that stage, categ color and dot color are correctly attributed in data1, box.coord and dot.coord + # end y dot coordinates recovery (create ini.box.coord, dot.coord and modify data1) + # ylim range + if(is.null(y.lim)){ + y.lim <- tempo.graph.info.ini$layout$panel_params[[1]]$y.range # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + if(any(( ! is.finite(y.lim)) | is.na(y.lim)) | length(y.lim) != 2){ # kept but normally no more Inf in data1 # normally no NA with is.finite, etc. + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntempo.graph.info.ini$layout$panel_params[[1]]$y.range[1] CONTAINS NA OR Inf OR HAS LENGTH 1") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else if(y.log != "no"){ + y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + } + if(y.log != "no"){ + # normally this control is not necessary anymore + if(any( ! is.finite(y.lim))){ # normally no NA with is.finite + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # all() without na.rm -> ok because y.lim cannot be NA (tested above) + # normally this control is not necessary anymore + tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS Inf VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + if(suppressWarnings(any(is.na(y.lim)))){ # normally no NA with is.na + # normally this control is not necessary anymore + tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS NA OR NaN VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + y.lim.order <- order(y.lim) # to deal with inverse axis + y.lim <- sort(y.lim) + y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis + y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis + if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before + y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + } + y.lim <- y.lim[y.lim.order] + if(any(is.na(y.lim))){ # normally no NA with is.na + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end ylim range + + + + + + + # drawing + # constant part + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add is directly put here to deal with additional variable of data, like when using facet_grid. No problem if add is a theme, will be dealt below + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){categ[1]}else{x.lab})) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y}else{y.lab})) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) + # text angle management + axis.just <- fun_gg_just(angle = x.angle, pos = ifelse(vertical == TRUE, "bottom", "left"), kind = "axis") + # end text angle management + add.check <- TRUE + if( ! is.null(add)){ # if add is NULL, then = 0 + if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + add.check <- FALSE + } + } + if(add.check == TRUE & article == TRUE){ + # WARNING: not possible to add theme()several times. NO message but the last one overwrites the others + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) + if(grid == TRUE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_line(size = 0.5), + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis + axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis + panel.grid.major.x = if(vertical == TRUE){NULL}else{ggplot2::element_line(colour = "grey85", size = 0.75)}, + panel.grid.major.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey85", size = 0.75)}else{NULL}, + panel.grid.minor.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey90", size = 0.25)}else{NULL}, + axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, + axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}, + strip.background = ggplot2::element_rect(fill = NA, colour = NA) # for facet background + )) + }else{ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_line(size = 0.5), + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + axis.line.y.left = ggplot2::element_line(colour = "black"), + axis.line.x.bottom = ggplot2::element_line(colour = "black"), + axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, + axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}, + strip.background = ggplot2::element_rect(fill = NA, colour = NA) + )) + } + }else if(add.check == TRUE & article == FALSE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_line(size = 0.5), + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + panel.background = ggplot2::element_rect(fill = "grey95"), + axis.line.y.left = ggplot2::element_line(colour = "black"), + axis.line.x.bottom = ggplot2::element_line(colour = "black"), + panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.minor.x = ggplot2::element_blank(), + panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), + strip.background = ggplot2::element_rect(fill = NA, colour = NA), + axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL}, + axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)} + )) + } + # Contrary to fun_gg_bar(), cannot plot the boxplot right now, because I need the dots plotted first + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, group = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), color = NA, width = box.width, fill = NA)) # this is to set the graph (i.e., a blanck boxplot to be able to use x coordinates to plot dots before boxes) + # end constant part + + + + + # graphic info recovery (including means) + tempo.graph.info <- ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}) + ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color})')))) # will be recovered later again, when ylim will be considered + tempo.yx.ratio <- (tempo.graph.info$layout$panel_params[[1]]$y.range[2] - tempo.graph.info$layout$panel_params[[1]]$y.range[1]) / (tempo.graph.info$layout$panel_params[[1]]$x.range[2] - tempo.graph.info$layout$panel_params[[1]]$x.range[1]) + box.coord <- tempo.graph.info$data[[2]] # to have the summary statistics of the plot. Contrary to ini.box.plot, now integrates ylim Here because can be required for stat.pos when just box are plotted + box.coord$x <- as.numeric(box.coord$x) # because x is of special class that block comparison of values using identical + box.coord$PANEL <- as.numeric(box.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? + box.coord <- box.coord[order(box.coord$group, box.coord$PANEL), ] + if( ! (identical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL) DO NOT HAVE THE SAME VALUE ORDER") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + # tempo <- c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}) + if(any(names(tempo.mean) %in% names(box.coord), na.rm = TRUE)){ + names(tempo.mean)[names(tempo.mean) %in% names(box.coord)] <- paste0(names(tempo.mean)[names(tempo.mean) %in% names(box.coord)], ".mean") + } + box.coord <- data.frame(box.coord, tempo.mean, stringsAsFactors = TRUE) + } + # end graphic info recovery (including means) + + + + # stat output (will also serve for boxplot and mean display) + # x not added now (to do not have them in stat.nolog) + stat <- data.frame( + MIN = box.coord$ymin_final, + QUART1 = box.coord$lower, + MEDIAN = box.coord$middle, + MEAN = box.coord$MEAN, + QUART3 = box.coord$upper, + MAX = box.coord$ymax_final, + WHISK_INF = box.coord$ymin, + BOX_INF = box.coord$lower, + NOTCH_INF = box.coord$notchlower, + NOTCH_SUP = box.coord$notchupper, + BOX_SUP = box.coord$upper, + WHISK_SUP = box.coord$ymax, + OUTLIERS = box.coord["outliers"], + tempo.mean[colnames(tempo.mean) != "MEAN"], + COLOR = box.coord$fill, + stringsAsFactors = TRUE + ) # box.coord["outliers"] written like this because it is a list. X coordinates not put now because several features to set + names(stat)[names(stat) == "outliers"] <- "OUTLIERS" + stat.nolog <- stat # stat.nolog ini will serve for outputs + if(y.log != "no"){ + stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")] <- ifelse(y.log == "log2", 2, 10)^(stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")]) + stat.nolog$OUTLIERS <- lapply(stat.nolog$OUTLIERS, FUN = function(X){ifelse(y.log == "log2", 2, 10)^X}) + } + # end stat output (will also serve for boxplot and mean display) + + + + + + + # x coordinates management (for random plotting and for stat display) + # width commputations + width.ini <- c(box.coord$xmax - box.coord$xmin)[1] # all the box widths are equal here. Only the first one taken + width.correct <- width.ini * box.space / 2 + if( ! (identical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + stat <- data.frame( + stat, + X = box.coord$x, + X_BOX_INF = box.coord$xmin + width.correct, + X_BOX_SUP = box.coord$xmax - width.correct, + X_NOTCH_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) / 2, + X_NOTCH_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) / 2, + X_WHISK_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, + X_WHISK_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, + # tempo.mean[colnames(tempo.mean) != "MEAN"], # already added above + stringsAsFactors = TRUE + ) + stat$COLOR <- factor(stat$COLOR, levels = unique(categ.color)) + if( ! all(stat$NOTCH_SUP < stat$BOX_SUP & stat$NOTCH_INF > stat$BOX_INF, na.rm = TRUE) & box.notch == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") SOME NOTCHES ARE BEYOND BOX HINGES. TRY ARGUMENT box.notch = FALSE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + dot.jitter <- c((box.coord$xmax - width.correct) - (box.coord$xmin + width.correct))[1] * dot.jitter # real dot.jitter. (box.coord$xmin + width.correct) - (box.coord$xmax - width.correct))[1] is the width of the box. Is equivalent to (box.coord$x - (box.coord$xmin + width.correct))[1] * 2 + # end width commputations + if( ! is.null(dot.color)){ + # random dots + if(dot.tidy == FALSE){ + dot.coord.rd1 <- merge(dot.coord, box.coord[c("fill", "PANEL", "group", "x")], by = c("PANEL", "group"), sort = FALSE) # rd for random. Send the coord of the boxes into the coord data.frame of the dots (in the column x.y). WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column + if(nrow(dot.coord.rd1) != nrow(dot.coord)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd1 DATA FRAME. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + sampled.dot.jitter <- if(nrow(dot.coord.rd1)== 1L){runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2)}else{sample(x = runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2), size = nrow(dot.coord.rd1), replace = FALSE)} + dot.coord.rd2 <- data.frame(dot.coord.rd1, dot.x = dot.coord.rd1$x.y + sampled.dot.jitter, stringsAsFactors = TRUE) # set the dot.jitter thanks to runif and dot.jitter range. Then, send the coord of the boxes into the coord data.frame of the dots (in the column x.y) + if(length(categ)== 1L){ + tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor + names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") + verif <- paste0(categ[1], ".check") + }else if(length(categ) == 2L){ + tempo.data1 <- unique( + data.frame( + data1[c(categ[1], categ[2])], + group = as.integer(factor(paste0( + formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking + ".", + formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking + )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number + ) + ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis + names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") + names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check") + verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check")) + }else{ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + dot.coord.rd3 <- merge(dot.coord.rd2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have replaced by = "group" by intersect("group", "group") because of an error due to wrong group group merging in dot.coord.rd3 + if(nrow(dot.coord.rd3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.rd3[categ], dot.coord.rd3[verif])$identical.content)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd3 DATA FRAME. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end random dots + } + # tidy dots + # coordinates are recovered during plotting (see dot.coord.tidy1 below) + # end tidy dots + } + # end x coordinates management (for random plotting and for stat display) + + + + + + # boxplot display before dot display if box.fill = TRUE + coord.names <- NULL + # creation of the data frame for (main box + legend) and data frame for means + if(box.notch == FALSE){ + for(i3 in 1:length(categ)){ + if(i3== 1L){ + tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE) + }else{ + tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE) + } + } + names(tempo.polygon) <- categ + tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "BOX_SUP", "BOX_SUP", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE) + if( ! is.null(facet.categ)){ + for(i4 in 1:length(facet.categ)){ + tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE) + names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4] + } + } + }else{ + for(i3 in 1:length(categ)){ + if(i3== 1L){ + tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE) + }else{ + tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE) + } + } + names(tempo.polygon) <- categ + tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_NOTCH_SUP", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF", "X_NOTCH_INF", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "NOTCH_INF", "MEDIAN", "NOTCH_SUP", "BOX_SUP", "BOX_SUP", "NOTCH_SUP", "MEDIAN", "NOTCH_INF", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE) + if( ! is.null(facet.categ)){ + for(i4 in 1:length(facet.categ)){ + tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE) + names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4] + } + } + } + tempo.polygon$COLOR <- factor(tempo.polygon$COLOR, levels = unique(categ.color)) + if( ! is.null(categ.class.order)){ + for(i3 in 1:length(categ)){ + tempo.polygon[, categ[i3]] <- factor(tempo.polygon[, categ[i3]], levels = categ.class.order[[i3]]) + } + } + # modified name of dot.categ column (e.g., "Categ1_DOT") must be included for boxplot using ridy dots + if( ! is.null(dot.color) & ! is.null(dot.categ)){ + if(dot.categ != ini.dot.categ){ + tempo.polygon <- data.frame(tempo.polygon, GROUPX = tempo.polygon[, ini.dot.categ], stringsAsFactors = TRUE) + names(tempo.polygon)[names(tempo.polygon) == "GROUPX"] <- dot.categ + + } + } + tempo.diamon.mean <- data.frame(X = c(t(stat[, c("X", "X_NOTCH_INF", "X", "X_NOTCH_SUP", "X")])), Y = c(t(cbind(stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] + (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stringsAsFactors = TRUE))), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), GROUP = c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")])), stringsAsFactors = TRUE) # stringsAsFactors = TRUE for cbind() because stat["MEAN"] is a data frame. Otherwise, stringsAsFactors is not an argument for cbind() on vectors + if( ! is.null(facet.categ)){ + for(i3 in 1:length(facet.categ)){ + tempo.diamon.mean <- data.frame(tempo.diamon.mean, c(t(stat[, c(facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3])])), stringsAsFactors = TRUE) + names(tempo.diamon.mean)[length(names(tempo.diamon.mean))] <- facet.categ[i3] + } + } + tempo.diamon.mean$COLOR <- factor(tempo.diamon.mean$COLOR, levels = unique(categ.color)) + # end creation of the data frame for (main box + legend) and data frame for means + if(box.fill == TRUE){ + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, alpha = box.alpha, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{dot.border.color}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted. Finally, boxplot redrawn (see below) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon( + data = tempo.polygon, + mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", fill = categ[length(categ)], color = categ[length(categ)]), + size = box.line.size, + alpha = box.alpha # works only for fill, not for color + )) + coord.names <- c(coord.names, "main.box") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # + coord.names <- c(coord.names, "sup.whisker") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # + coord.names <- c(coord.names, "inf.whisker") + if(box.whisker.width > 0){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # + coord.names <- c(coord.names, "sup.whisker.edge") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # + coord.names <- c(coord.names, "inf.whisker.edge") + } + if(box.mean == TRUE){ + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, fill = stat$COLOR, size = box.mean.size, color = "black", alpha = box.alpha)) # group used in aesthetic to do not have it in the legend + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon( + data = tempo.diamon.mean, + mapping = ggplot2::aes(x = X, y = Y, group = GROUP), + fill = tempo.diamon.mean[, "COLOR"], + color = hsv(0, 0, 0, alpha = box.alpha), # outline of the polygon in black but with alpha + size = box.line.size, + alpha = box.alpha + )) + coord.names <- c(coord.names, "mean") + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = "black", size = box.line.size * 2, alpha = box.alpha)) # + coord.names <- c(coord.names, "median") + } + # end boxplot display before dot display if box.fill = TRUE + + + + + + + # dot display + if( ! is.null(dot.color)){ + if(dot.tidy == FALSE){ + if(is.null(dot.categ)){ + if(dot.border.size == 0){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( + data = dot.coord.rd3, + mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), + size = dot.size, + shape = 19, + color = dot.coord.rd3$dot.color, + alpha = dot.alpha + )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic + }else{ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( + data = dot.coord.rd3, + mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), + shape = 21, + stroke = dot.border.size, + color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, + size = dot.size, + fill = dot.coord.rd3$dot.color, + alpha = dot.alpha + )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic + } + }else{ + if(dot.border.size == 0){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( + data = dot.coord.rd3, + mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), + size = dot.size, + shape = 19, + color = dot.coord.rd3$dot.color + )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic + }else{ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point( + data = dot.coord.rd3, + mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), + size = dot.size, + shape = 21, + stroke = dot.border.size, + color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, + fill = dot.coord.rd3$dot.color + )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), guide = ggplot2::guide_legend(override.aes = list(fill = dot.color, color = if(is.null(dot.border.color)){dot.color}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + } + coord.names <- c(coord.names, "dots") + }else if(dot.tidy == TRUE){ + # here plot using group -> no scale + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot( + data = dot.coord, + mapping = ggplot2::aes_string(x = categ[1], y = "y", group = "group"), # not dot.categ here because the classes of dot.categ create new separations + position = ggplot2::position_dodge(width = box.width), + binpositions = "all", + binaxis = "y", + stackdir = "center", + alpha = dot.alpha, + fill = dot.coord$dot.color, + stroke = dot.border.size, + color = if(is.null(dot.border.color)){dot.coord$dot.color}else{rep(dot.border.color, nrow(dot.coord))}, + show.legend = FALSE, # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual() + binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb + )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) + coord.names <- c(coord.names, "dots") + if( ! is.null(dot.categ)){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot( + data = dot.coord, + mapping = ggplot2::aes_string(x = categ[1], y = "y", alpha = dot.categ), # not dot.categ here because the classes of dot.categ create new separations + position = ggplot2::position_dodge(width = box.width), + binpositions = "all", + binaxis = "y", + stackdir = "center", + fill = NA, + stroke = NA, + color = NA, + # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual() + binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb + )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.) + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = dot.legend.name, values = rep(1, length(categ.color)))) # values = rep("black", length(categ.color)) are the values of color (which is the border color of dots), and this modify the border color on the plot. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + coord.names <- c(coord.names, "bad_remove") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), labels = dot.categ.class.order, guide = ggplot2::guide_legend(title = if(ini.dot.categ == categ[length(categ)]){dot.categ}else{ini.dot.categ}, override.aes = list(fill = levels(dot.coord$dot.color), color = if(is.null(dot.border.color)){levels(dot.coord$dot.color)}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + } + # coordinates of tidy dots + tempo.coord <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))$data # to have the tidy dot coordinates + if(length(which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))) != 1){ # detect the compartment of tempo.coord which is the binned data frame + # if(length(which(sapply(tempo.coord, FUN = nrow) == nrow(data1))) > if(is.null(dot.categ)){1}else{2}){ # this does not work if only one dot per class, thus replaced by above # if(is.null(dot.categ)){1}else{2} because 1 dotplot if dot.categ is NULL and 2 dotplots if not, with the second being a blank dotplot with wrong coordinates. Thus take the first in that situation + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nEITHER MORE THAN 1 OR NO COMPARTMENT HAVING A DATA FRAME WITH binwidth AS COLUMN NAME IN THE tempo.coord LIST (FOR TIDY DOT COORDINATES). CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + # dot.coord.tidy1 <- tempo.coord[[which(sapply(tempo.coord, FUN = nrow) == nrow(data1))[1]]] # this does not work if only one dot per class, thus replaced by above # the second being a blank dotplot with wrong coordinates. Thus take the first whatever situation + dot.coord.tidy1 <- tempo.coord[[which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))]] # detect the compartment of tempo.coord which is the binned data frame + dot.coord.tidy1$x <- as.numeric(dot.coord.tidy1$x) # because weird class + dot.coord.tidy1$PANEL <- as.numeric(dot.coord.tidy1$PANEL) # because numbers as levels. But may be a problem is facet are reordered ? + } + # tempo.box.coord <- merge(box.coord, unique(dot.coord[, c("PANEL", "group", categ)]), by = c("PANEL", "group"), sort = FALSE) # not required anymore because box.coord already contains categ do not add dot.categ and tidy_group_coord here because the coordinates are for stats. Add the categ in box.coord. WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column + # below inactivated because not true when dealing with dot.categ different from categ + # if(nrow(tempo.box.coord) != nrow(box.coord)){ + # tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT tempo.box.coord DATA FRAME. CODE HAS TO BE MODIFIED") + # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + # } + dot.coord.tidy2 <- merge(dot.coord.tidy1, box.coord[c("fill", "PANEL", "group", "x", categ)], by = c("PANEL", "group"), sort = FALSE) # send the coord of the boxes into the coord data.frame of the dots (in the column x.y).WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in tempo.box.coord. Thus, no need to consider fill colum # DANGER: from here the fill.y and x.y (from tempo.box.coord) are not good in dot.coord.tidy2. It is ok because Categ1 Categ2 from tempo.box.coord are ok with the group column from dot.coord.tidy1. This is due to the fact that dot.coord.tidy resulting from geom_dotplot does not make the same groups as the other functions + if(nrow(dot.coord.tidy2) != nrow(dot.coord)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy2 DATA FRAME. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # From here, check for dot.coord.tidy3 which wil be important for stat over the plot. WARNING: dot.categ has nothing to do here for stat coordinates. Thus, not in tempo.data1 + if(length(categ)== 1L){ + tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor + names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") + verif <- paste0(categ[1], ".check") + }else if(length(categ) == 2L){ + tempo.data1 <- unique( + data.frame( + data1[c(categ[1], categ[2])], + group = as.integer(factor(paste0( + formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking + ".", + formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking + )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number + ) + ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis + names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check") + names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check") + verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check")) + }else{ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + dot.coord.tidy3 <- merge(dot.coord.tidy2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have tested intersect("group", "group") instead of by = "group". May be come back to by = "group" in case of error. But I did this because of an error in dot.coord.rd3 above + if(nrow(dot.coord.tidy3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.tidy3[categ], dot.coord.tidy3[verif])$identical.content)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy3 DATA FRAME. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end coordinates of tidy dots + } + } + # end dot display + + + + # boxplot display (if box.fill = FALSE, otherwise, already plotted above) + if(box.fill == TRUE){ + # overcome "work only for the filling of boxes, not for the frame. See https://github.com/tidyverse/ggplot2/issues/252" + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(order = 1))) #, guide = ggplot2::guide_legend(override.aes = list(fill = levels(tempo.polygon$COLOR), color = "black")))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = rep(hsv(0, 0, 0, alpha = box.alpha), length(unique(data1[, categ[length(categ)]]))), guide = ggplot2::guide_legend(order = 1))) # , guide = ggplot2::guide_legend(override.aes = list(color = "black", alpha = box.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor # outline of the polygon in black but with alpha + }else{ + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, alpha = box.alpha, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{if(dot.border.size == 0){NA}else{dot.border.color}}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path( + data = tempo.polygon, + mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", color = categ[length(categ)]), + size = box.line.size, + alpha = box.alpha, + lineend = "round", + linejoin = "round" + )) + coord.names <- c(coord.names, "main.box") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size * 2, alpha = box.alpha)) # + coord.names <- c(coord.names, "median") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # + coord.names <- c(coord.names, "sup.whisker") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # + coord.names <- c(coord.names, "inf.whisker") + if(box.whisker.width > 0){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # + coord.names <- c(coord.names, "sup.whisker.edge") + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # + coord.names <- c(coord.names, "inf.whisker.edge") + } + if(box.mean == TRUE){ + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, color = stat$COLOR, size = box.mean.size, fill = NA, alpha = box.alpha)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path( + data = tempo.diamon.mean, + mapping = ggplot2::aes(x = X, y = Y, group = GROUP), + color = tempo.diamon.mean[, "COLOR"], + size = box.line.size, + alpha = box.alpha, + lineend = "round", + linejoin = "round" + )) + coord.names <- c(coord.names, "mean") + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = rep(NA, length(unique(data1[, categ[length(categ)]]))))) #, guide = ggplot2::guide_legend(override.aes = list(color = categ.color)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(override.aes = list(alpha = if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){1}else{box.alpha})))) # , guide = ggplot2::guide_legend(override.aes = list(color = as.character(categ.color))))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor + if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(box.alpha == 0){ # remove box legend because no boxes drawn + # add this after the scale_xxx_manual() for boxplots + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none")) # inactivate the legend + } + # end boxplot display (if box.fill = FALSE, otherwise, already plotted above) + + + + + # stat display + # layer after dots but ok, behind dots on the plot + if( ! is.null(stat.pos)){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NUMBERS DISPLAYED ARE ", ifelse(stat.mean == FALSE, "MEDIANS", "MEANS")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + if(stat.pos == "top"){ + tempo.stat <- data.frame(stat, Y = y.lim[2], stringsAsFactors = TRUE) # I had to create a data frame for geom_tex() so that facet is taken into account, (ggplot2::annotate() does not deal with facet because no data and mapping arguments). Of note, facet.categ is in tempo.stat, via tempo.mean, via dot.coord + if(stat.mean == FALSE){tempo.stat$MEDIAN <- formatC(stat.nolog$MEDIAN, digit = 2, drop0trailing = TRUE, format = "f")}else{tempo.stat$MEAN <- formatC(stat.nolog$MEAN, digit = 2, drop0trailing = TRUE, format = "f")} + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( + data = tempo.stat, + mapping = ggplot2::aes_string(x = "X", y = "Y", label = ifelse(stat.mean == FALSE, "MEDIAN", "MEAN")), + size = stat.size, + color = "black", + angle = stat.angle, + hjust = stat.just$hjust, + vjust = stat.just$vjust + )) # stat$X used here because identical to stat.nolog but has the X. WARNING: no need of order() for labels because box.coord$x set the order. For justification, see https://stackoverflow.com/questions/7263849/what-do-hjust-and-vjust-do-when-making-a-plot-using-ggplot + coord.names <- c(coord.names, "stat.pos") + }else if(stat.pos == "above"){ + # stat coordinates + if( ! is.null(dot.color)){ # for text just above max dot + if(dot.tidy == FALSE){ + tempo.stat.ini <- dot.coord.rd3 + }else if(dot.tidy == TRUE){ + tempo.stat.ini <- dot.coord.tidy3 + tempo.stat.ini$x.y <- tempo.stat.ini$x.x # this is just to be able to use tempo.stat.ini$x.y for untidy or tidy dots (remember that dot.coord.tidy3$x.y is not good, see above) + } + stat.coord1 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = min, na.rm = TRUE) + names(stat.coord1)[names(stat.coord1) == "y"] <- "dot.min" + stat.coord2 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = max, na.rm = TRUE) + names(stat.coord2) <- paste0(names(stat.coord2), "_from.dot.max") + names(stat.coord2)[names(stat.coord2) == "y_from.dot.max"] <- "dot.max" + stat.coord3 <- cbind(box.coord[order(box.coord$group, box.coord$PANEL), ], stat.coord1[order(stat.coord1$group, stat.coord1$x.y), ], stat.coord2[order(stat.coord2$group, stat.coord2$x.y), ], stringsAsFactors = TRUE) # + if( ! all(identical(round(stat.coord3$x, 9), round(as.numeric(stat.coord3$x.y), 9)), na.rm = TRUE)){ # as.numeric() because stat.coord3$x is class "mapped_discrete" "numeric" + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nFUSION OF box.coord, stat.coord1 AND stat.coord2 ACCORDING TO box.coord$x, stat.coord1$x.y AND stat.coord2$x.y IS NOT CORRECT. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + # text.coord <- stat.coord3[, c("x", "group", "dot.min", "dot.max")] + # names(text.coord)[names(text.coord) == "dot.min"] <- "text.min.pos" + #names(text.coord)[names(text.coord) == "dot.max"] <- "text.max.pos" + box.coord <- box.coord[order(box.coord$x, box.coord$group, box.coord$PANEL), ] + # text.coord <- text.coord[order(text.coord$x), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same + stat.coord3 <- stat.coord3[order(stat.coord3$x, stat.coord3$group, stat.coord3$PANEL), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same + if( ! (identical(box.coord$x, stat.coord3$x) & identical(box.coord$group, stat.coord3$group) & identical(box.coord$PANEL, stat.coord3$PANEL))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntext.coord AND box.coord DO NOT HAVE THE SAME x, group AND PANEL COLUMN CONTENT") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + }else{ + stat.coord3 <- box.coord + } + stat.coord3 <- data.frame( + stat.coord3, + Y = stat.coord3[, ifelse( + is.null(dot.color), + ifelse(diff(y.lim) > 0, "ymax", "ymin"), + ifelse(diff(y.lim) > 0, "ymax_final", "ymin_final") + )], + stringsAsFactors = TRUE + ) # ymax is top whisker, ymax_final is top dot + # stat.coord3 <- data.frame(stat.coord3, Y = vector("numeric", length = nrow(stat.coord3)), stringsAsFactors = TRUE) + # check.Y <- as.logical(stat.coord3$Y) # convert everything in Y into FALSE (because Y is full of zero) + # end stat coordinates + # stat display + # performed twice: first for y values >=0, then y values < 0, because only a single value allowed for hjust anf vjust + if(stat.mean == FALSE){ + tempo.center.ref <- "middle" + }else{ + tempo.center.ref <- "MEAN" + } + # if(is.null(dot.color)){ + # tempo.low.ref <- "ymin" + # tempo.high.ref <- "ymax" + # }else{ + # tempo.low.ref <- "ymin_final" + # tempo.high.ref <- "ymax_final" + # } + # tempo.log.high <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] >= 0}else{stat.coord3[, tempo.center.ref] < 0} + # tempo.log.low <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] < 0}else{stat.coord3[, tempo.center.ref] >= 0} + # stat.coord3$Y[tempo.log.high] <- stat.coord3[tempo.log.high, tempo.high.ref] + # stat.coord3$Y[tempo.log.low] <- stat.coord3[tempo.log.low, tempo.low.ref] + # add distance + stat.coord3$Y <- stat.coord3$Y + diff(y.lim) * stat.dist / 100 + # end add distance + # correct median or mean text format + if(y.log != "no"){ + stat.coord3[, tempo.center.ref] <- ifelse(y.log == "log2", 2, 10)^(stat.coord3[, tempo.center.ref]) + } + stat.coord3[, tempo.center.ref] <- formatC(stat.coord3[, tempo.center.ref], digit = 2, drop0trailing = TRUE, format = "f") + # end correct median or mean text format + # if(any(tempo.log.high) == TRUE){ + # tempo.stat <- stat.coord3[tempo.log.high,] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( + data = stat.coord3, + mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref), + size = stat.size, + color = "black", + angle = stat.angle, + hjust = stat.just$hjust, + vjust = stat.just$vjust + )) # WARNING: no need of order() for labels because box.coord$x set the order + coord.names <- c(coord.names, "stat.pos") + # } + # if(any(tempo.log.low) == TRUE){ + # tempo.stat <- stat.coord3[tempo.log.low,] + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text( + # data = tempo.stat, + # mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref), + # size = stat.size, + # color = "black", + # hjust = ifelse(vertical == TRUE, 0.5, 0.5 + stat.dist), + # vjust = ifelse(vertical == TRUE, 0.5 + stat.dist, 0.5) + # )) # WARNING: no need of order() for labels because box.coord$x set the order + # coord.names <- c(coord.names, "stat.pos.negative") + # } + # end stat display + }else{ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + } + # end stat display + # legend management + if(legend.show == FALSE){ # must be here because must be before bef.final.plot <- + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend + } + # end legend management + + + + # y scale management (cannot be before dot plot management) + # the rescaling aspect is complicated and not intuitive. See: + # explaination: https://github.com/tidyverse/ggplot2/issues/3948 + # the oob argument of scale_y_continuous() https://ggplot2.tidyverse.org/reference/scale_continuous.html + # see also https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf + # secondary ticks + bef.final.plot <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + if(vertical == TRUE){ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)}else{ggplot2::coord_flip(ylim = y.lim)}')))) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks and the legend. I DI NOT UNDERSTAND THE COMMENT HERE BECAUSE WE NEED COORD_FLiP + tempo.coord <- bef.final.plot$layout$panel_params[[1]] + # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non NULL or if y.log argument is different from "no") + if(y.log != "no"){ # integer main ticks for log2 and log10 + tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1) + }else{ + tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))} + if(all(is.na(tempo))){# all() without na.rm -> ok because is.na() cannot be NA + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + } + tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb)) + } + y.second.tick.values <- NULL + y.second.tick.pos <- NULL + if(y.log != "no"){ + tempo <- fun_inter_ticks(lim = y.lim, log = y.log) + y.second.tick.values <- tempo$values + y.second.tick.pos <- tempo$coordinates + # if(vertical == TRUE){ # do not remove in case the bug is fixed + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = y.second.tick.pos, yend = y.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80)) + # }else{ # not working because of the ggplot2 bug + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80)) + # } + coord.names <- c(coord.names, "y.second.tick.positions") + }else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){ + # if(y.second.tick.nb > 0){ #inactivated because already checked before + if(length(tempo.scale) < 2){ + tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb") + tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ") + tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb) + } + y.second.tick.values <- tempo$values + y.second.tick.pos <- tempo$coordinates + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( + geom = "segment", + y = y.second.tick.pos, + yend = y.second.tick.pos, + x = if(vertical == TRUE){tempo.coord$x.range[1]}else{tempo.coord$y.range[1]}, + xend = if(vertical == TRUE){tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80}else{tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80} + )) + coord.names <- c(coord.names, "y.second.tick.positions") + } + # end y.second.tick.positions + # for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous"))) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous( + breaks = tempo.scale, + minor_breaks = y.second.tick.pos, + labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 6") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, # == in stop() to be able to add several messages between == + expand = c(0, 0), # remove space after after axis limits + limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used + oob = scales::rescale_none, + trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this + )) + if(vertical == TRUE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(ylim = y.lim)) # problem of ggplot2::ylim() is that it redraws new breaks # coord_cartesian(ylim = y.lim)) not used because bug -> y-axis label disappearance with y.lim decreasing I DO NOT UNDERSTAND THIS MESSAGE WHILE I USE COORD_CARTESIAN # clip = "off" to have secondary ticks outside plot region does not work + }else{ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_flip(ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region does not work # create the problem of y-axis label disappearance with y.lim decreasing. IDEM ABOVE + + } + # end y scale management (cannot be before dot plot management) + + + # legend management + if( ! is.null(legend.width)){ + legend.final <- fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path) # get legend + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend + if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE + legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end legend management + + + # drawing + fin.plot <- suppressMessages(suppressWarnings(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))) + grob.save <- NULL + if(plot == TRUE){ + # following lines inactivated because of problem in warn.recov and message.recov + # assign("env_fun_get_message", new.env()) + # assign("tempo.gg.name", tempo.gg.name, envir = env_fun_get_message) + # assign("tempo.gg.count", tempo.gg.count, envir = env_fun_get_message) + # assign("add", add, envir = env_fun_get_message) + # two next line: for the moment, I cannot prevent the warning printing + # warn.recov <- fun_get_message(paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}), kind = "warning", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering warnings printed by ggplot() functions + # message.recov <- fun_get_message('print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))', kind = "message", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering messages printed by ggplot() functions + # if( ! (return == TRUE & return.ggplot == TRUE)){ # because return() plots when return.ggplot is TRUE # finally not used -> see return.ggplot description + if(is.null(legend.width)){ + grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot))) + }else{ + grob.save <-suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width)))) + } + # } + # suppressMessages(suppressWarnings(print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add})))))) + }else{ + # following lines inactivated because of problem in warn.recov and message.recov + # message.recov <- NULL + # warn.recov <- NULL + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end drawing + + + + # output + # following lines inactivated because of problem in warn.recov and message.recov + # if( ! (is.null(warn) & is.null(warn.recov) & is.null(message.recov))){ + # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0 | length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", ifelse( ! is.null(warn.recov), unique(message.recov), ""), ifelse( ! is.null(message.recov), unique(message.recov), ""), collapse = "\n\n"), "\n\n")}) + # }else if( ! (is.null(warn) & is.null(warn.recov)) & is.null(message.recov)){ + # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(warn.recov), collapse = "\n\n"), "\n\n")}) + # }else if( ! (is.null(warn) & is.null(message.recov)) & is.null(warn.recov)){ + # warn <- paste0(warn, "\n\n", if(length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(message.recov), collapse = "\n\n"), "\n\n")}) + # } + if(warn.print == TRUE & ! is.null(warn)){ + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + } + on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) + if(return == TRUE){ + tempo.output <- ggplot2::ggplot_build(fin.plot) + tempo.output$data <- tempo.output$data[-1] # remove the first data because corresponds to the initial empty boxplot + if(length(tempo.output$data) != length(coord.names)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nlength(tempo.output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + names(tempo.output$data) <- coord.names + tempo.output$data <- tempo.output$data[coord.names != "bad_remove"] + } + tempo <- tempo.output$layout$panel_params[[1]] + output <- list( + data = data1.ini, + stat = stat.nolog, + removed.row.nb = removed.row.nb, + removed.rows = removed.rows, + plot = c(tempo.output$data, y.second.tick.values = list(y.second.tick.values)), + panel = facet.categ, + axes = list( + x.range = tempo$x.range, + x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE) + x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, + y.range = tempo$y.range, + y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, + y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))} + ), + warn = paste0("\n", warn, "\n\n"), + ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE + gtable = if(return.gtable == TRUE){grob.save}else{NULL} + ) + return(output) # this plots the graph if return.ggplot is TRUE and if no assignment + } + # end output + # end main code } -# end loop part +# add density +# rasterise all kind: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html -# legend display -tempo.legend.final <- 'ggplot2::guides( + +fun_gg_scatter <- function( + data1, + x, + y, + categ = NULL, + categ.class.order = NULL, + color = NULL, + geom = "geom_point", + geom.step.dir = "hv", + geom.stick.base = NULL, + alpha = 0.5, + dot.size = 2, + dot.shape = 21, + dot.border.size = 0.5, + dot.border.color = NULL, + line.size = 0.5, + line.type = "solid", + x.lim = NULL, + x.lab = NULL, + x.log = "no", + x.tick.nb = NULL, + x.second.tick.nb = NULL, + x.include.zero = FALSE, + x.left.extra.margin = 0.05, + x.right.extra.margin = 0.05, + x.text.angle = 0, + y.lim = NULL, + y.lab = NULL, + y.log = "no", + y.tick.nb = NULL, + y.second.tick.nb = NULL, + y.include.zero = FALSE, + y.top.extra.margin = 0.05, + y.bottom.extra.margin = 0.05, + y.text.angle = 0, + raster = FALSE, + raster.ratio = 1, + raster.threshold = NULL, + text.size = 12, + title = "", + title.text.size = 12, + legend.show = TRUE, + legend.width = 0.5, + legend.name = NULL, + article = TRUE, + grid = FALSE, + add = NULL, + return = FALSE, + return.ggplot = FALSE, + return.gtable = TRUE, + plot = TRUE, + warn.print = FALSE, + lib.path = NULL +){ + # AIM + # Plot ggplot2 scatterplot with the possibility to overlay dots from up to 3 different data frames (-> three different legends) and lines from up to 3 different data frames (-> three different legends) -> up to 6 overlays totally + # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html + # WARNINGS + # Rows containing NA in data1[, c(x, y, categ)] will be removed before processing, with a warning (see below) + # Size arguments (dot.size, dot.border.size, line.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE)) + # ARGUMENTS + # data1: a dataframe compatible with ggplot2, or a list of data frames. Order matters for the order of the legend and for the layer staking (starting from below to top) + # x: single character string of the data1 column name for x-axis coordinates. If data1 is a list, then x must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_hline" in geom argument + # y: single character string of the data1 column name for y-axis coordinates. If data1 is a list, then y must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_vline" in geom argument + # categ: either NULL or a single character string or a list of single character strings, indicating the data1 column names to use for categories which creates legend display + # If categ == NULL, no categories -> no legend displayed + # If data1 is a data frame, categ must be a single character string of the data1 column name for categories + # If data1 is a list, then categ must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (no legend display for these compartments), and other not + # categ.class.order: either (1) NULL or (2) a vector of character strings or (3) a list of these vectors, setting the order of the classes of categ in the legend display + # If categ.class.order is NULL, classes are represented according to the alphabetical order + # If data1 is a data frame, categ.class.order must be a vector of character strings specifying the different classes in the categ column name of data1 + # If data1 is a list, then categ.class.order must be a list of vector of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (alphabetical order for these compartments), and other not + # color: either (1) NULL, or (2) a vector of character strings or integers, or (3) a list of vectors of character strings or integers + # If color is NULL, default colors of ggplot2 + # If data1 is a data frame, color argument can be either: + # (1) a single color string. All the dots of the corresponding data1 will have this color, whatever the categ value (NULL or not) + # (2) if categ is non-null, a vector of string colors, one for each class of categ. Each color will be associated according to the categ.class.order argument if specified, or to the alphabetical order of categ classes otherwise + # (3) if categ is non-null, a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ and a single class of categ per color must be respected + # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the max integer value among all the integers in color (see fun_gg_palette()) + # If data1 is a list, then color argument must be either: + # (1) a list of character strings or integers, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. + # (2) a single character string or a single integer + # With a list (first possibility), the rules described for when data1 is a data frame apply to each compartment of the list. Some of the compartments can be NULL. In that case, a different grey color will be used for each NULL compartment. With a single value (second possibility), the same color will be used for all the dots and lines, whatever the data1 list + # geom: single character string of the kind of plot, or a list of single character strings + # Either: + # "geom_point" (scatterplot) + # "geom_line" (coordinates plotted then line connection, from the lowest to highest x coordinates first and from the lowest to highest y coordinates thenafter) + # "geom_path" (coordinates plotted then line connection respecting the row order in data1) + # "geom_step" coordinates plotted then line connection respecting the row order in data1 but drawn in steps). See the geom.step.dir argument + # "geom_hline" (horizontal line, no x value provided) + # "geom_vline" (vertical line, no y value provided) + # "geom_stick" (dots as vertical bars) + # If data1 is a list, then geom must be either: + # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. + # (2) a single character string. In that case the same kind of plot will apply for the different compartments of the data1 list + # WARNING concerning "geom_hline" or "geom_vline": + # (1) x or y argument must be NULL, respectively + # (2) x.lim or y.lim argument must NOT be NULL, respectively, if only these kind of lines are drawn (if other geom present, then x.lim = NULL and y.lim = NULL will generate x.lim and y.lim defined by these other geom, which is not possible with "geom_hline" or "geom_vline" alone) + # (3) the function will draw n lines for n values in the x argument column name of the data1 data frame. If several colors required, the categ argument must be specified and the corresponding categ column name must exist in the data1 data frame with a different class name for each row + # geom.step.dir: single character string indicating the direction when using "geom_step" of the geom argument, or a list of single character strings + # Either: + # "vh" (vertical then horizontal) + # "hv" (horizontal then vertical) + # "mid" (step half-way between adjacent x-values) + # See https://ggplot2.tidyverse.org/reference/geom_path.html + # If data1 is a list, then geom.step.dir must be either: + # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. The value in compartments related to other geom values than "geom_step" will be ignored + # (2) a single character string, which will be used for all the "geom_step" values of the geom argument, whatever the data1 list + # geom.stick.base: either (1) NULL or (2) a single numeric value or (3) a list of single numeric values, setting the base of the sticks when using "geom_stick" of the geom argument + # If geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base + # If data1 is a list, then geom.stick.base must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the values in compartments related to other geom values than "geom_stick" will be ignored. With a single value (latter possibility), the same base will be used for all the sticks, whatever the data1 list + # Warning: the y-axis limits are not modified by the value of geom.stick.base, meaning that this value can be outside of the range of y.lim. Add the value of geom.stick.base also in the y.lim argument if required + # Warning: if geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base. Thus, be careful with inverted y-axis + # alpha: single numeric value (from 0 to 1) of transparency. If data1 is a list, then alpha must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. In that case the same transparency will apply for the different compartments of the data1 list + # dot.size: single numeric value of dot shape radius? in mm. If data1 is a list, then dot.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.size will be used for all the dots, whatever the data1 list + # dot.shape: value indicating the shape of the dots (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then dot.shape must be either (1) a list of single shape values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single shape value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.shape will be used for all the dots, whatever the data1 list + # dot.border.size: single numeric value of border dot width in mm. Write zero for no dot border. If data1 is a list, then dot.border.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.border.size will be used for all the dots, whatever the data1 list + # dot.border.color: single character color string defining the color of the dot border (same border color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette() + # line.size: single numeric value of line width in mm. If data1 is a list, then line.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.size will be used for all the lines, whatever the data1 list + # line.type: value indicating the kind of lines (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then line.type must be either (1) a list of single line kind values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single line kind value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.type will be used for all the lines, whatever the data1 list + # x.lim: 2 numeric values setting the x-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the x column name of data1 will be used + # x.lab: a character string or expression for x-axis label. If NULL, will use the first value of x (x column name of the first data frame in data1). Warning message if the elements in x are different between data frames in data1 + # x.log: either "no", "log2" (values in the x column name of the data1 data frame will be log2 transformed and x-axis will be log2 scaled) or "log10" (values in the x column name of the data1 data frame will be log10 transformed and x-axis will be log10 scaled) + # x.tick.nb: approximate number of desired values labeling the x-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if x.log is "no", then the number of labeling values is set by ggplot2. If NULL and if x.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the x.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for x.lim = c(9, 1200)). WARNING: if non-NULL and if x.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) + # x.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if x.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$x.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks + # x.include.zero: logical. Does x.lim range include 0? Ignored if x.log is "log2" or "log10" + # x.left.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to x.lim. If different from 0, add the range of the axis multiplied by x.left.extra.margin (e.g., abs(x.lim[2] - x.lim[1]) * x.left.extra.margin) to the left of x-axis + # x.right.extra.margin: idem as x.left.extra.margin but to the right of x-axis + # x.text.angle: integer value of the text angle for the x-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. + # y.lim: 2 numeric values setting the y-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the y column name of data1 will be used + # y.lab: a character string or expression for y-axis label. If NULL, will use the first value of y (y column name of the first data frame in data1). Warning message if the elements in y are different between data frames in data1 + # y.log: either "no", "log2" (values in the y column name of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y column name of the data1 data frame will be log10 transformed and y-axis will be log10 scaled) + # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...) + # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks + # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10" + # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis + # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis + # y.text.angle: integer value of the text angle for the y-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc. + # raster: logical. Dots in raster mode? If FALSE, dots from each "geom_point" from geom argument are plotted in vectorial mode (bigger pdf and long to display if lots of dots). If TRUE, dots from each "geom_point" from geom argument are plotted in matricial mode (smaller pdf and easy display if lots of dots, but it takes time to generate the layer). If TRUE, the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If TRUE, solve the transparency problem with some GUI. Overriden by the non-NULL raster.threshold argument + # raster.ratio: single numeric value indicating the height / width ratio of the graphic device used (for instance provided by the $dim compartment in the output of the fun_open() function). The default value is 1 because by default R opens a square graphic device. But this argument has to be set when using other device dimensions. Ignored if raster == FALSE + # raster.threshold: positive integer value indicating the limit of the dot number above which "geom_point" layers from the geom argument switch from vectorial mode to matricial mode (see the raster argument). If any layer is matricial, then the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If non-NULL, it overrides the raster argument + # text.size: numeric value of the font size of the (1) axis numbers and axis legends and (2) texts in the graphic legend (in mm) + # title: character string of the graph title + # title.text.size: numeric value of the title font size in mm + # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not + # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend + # legend.name: character string of the legend title. If legend.name is NULL and categ argument is not NULL, then legend.name <- categ. If data1 is a list, then legend.name must be a list of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL, and other not + # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme + # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present) + # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL + # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()" + # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_scatter() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments) + # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions + # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_scatter() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))' + # return: logical. Return the graph parameters? + # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_scatter() function (e.g., a <- fun_gg_scatter()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details + # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details + # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting + # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE + # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders + # RETURN + # a scatter plot if plot argument is TRUE + # a list of the graph info if return argument is TRUE: + # $data: the initial data with graphic information added. WARNING: if the x.log or y.log argument is not "no", x or y argument column of the data1 data frame are log2 or log10 converted in $data, respectively. Use 2^values or 10^$values to recover the initial values + # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed + # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed + # $plot: the graphic box and dot coordinates + # $dots: dot coordinates + # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no") + # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-null or if y.log argument is different from "no") + # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0 + # $axes: the x-axis and y-axis info + # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE + # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot + # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot + # REQUIRED PACKAGES + # ggplot2 + # gridExtra + # lemon (in case of use in the add argument) + # scales + # if raster plots are drawn (see the raster and raster.threshold arguments): + # Cairo + # grid + # REQUIRED FUNCTIONS FROM THE cute PACKAGE + # fun_gg_empty_graph() + # fun_gg_palette() + # fun_gg_point_rast() + # fun_pack() + # fun_check() + # fun_round() + # fun_scale() + # fun_inter_ticks() + # EXAMPLES + # set.seed(1) ; obs1 <- data.frame(Km = c(2, 1, 6, 5, 4, 7), Time = c(2, 1, 6, 5, 4, 7)^2, Car = c("TUUT", "TUUT", "TUUT", "WIIM", "WIIM", "WIIM"), Color1 = rep(c("coral", "lightblue"), each = 3), stringsAsFactors = TRUE) ; fun_gg_scatter(data1 = obs1, x = "Km", y = "Time") + # DEBUGGING + # set.seed(1) ; obs1 <- data.frame(km = rnorm(1000, 10, 3), time = rnorm(1000, 10, 3), group1 = rep(c("A1", "A2"), 500), stringsAsFactors = TRUE) ; obs2 <-data.frame(km = rnorm(1000, 15, 3), time = rnorm(1000, 15, 3), group2 = rep(c("G1", "G2"), 500), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$km[2:3] <- NA ; data1 = list(L1 = obs1, L2 = obs2) ; x = list(L1 = "km", L2 = "km") ; y = list(L1 = "time", L2 = "time") ; categ = list(L1 = "group1", L2 = "group2") ; categ = NULL ; categ.class.order = NULL ; color = NULL ; geom = "geom_point" ; geom.step.dir = "hv" ; geom.stick.base = NULL ; alpha = 0.5 ; dot.size = 2 ; dot.shape = 21 ; dot.border.size = 0.5 ; dot.border.color = NULL ; line.size = 0.5 ; line.type = "solid" ; x.lim = NULL ; x.lab = NULL ; x.log = "no" ; x.tick.nb = NULL ; x.second.tick.nb = NULL ; x.include.zero = FALSE ; x.left.extra.margin = 0.05 ; x.right.extra.margin = 0.05 ; x.text.angle = 0 ; y.lim = NULL ; y.lab = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = NULL ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; y.text.angle = 0 ; raster = FALSE ; raster.ratio = 1 ; raster.threshold = NULL ; text.size = 12 ; title = "" ; title.text.size = 12 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL + # function name + function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()") + arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments + arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user) + # end function name + # required function checking + req.function <- c( + "fun_check", + "fun_gg_just", + "fun_gg_empty_graph", + "fun_gg_palette", + "fun_gg_point_rast", + "fun_round", + "fun_pack", + "fun_scale", + "fun_inter_ticks" + ) + tempo <- NULL + for(i1 in req.function){ + if(length(find(i1, mode = "function"))== 0L){ + tempo <- c(tempo, i1) + } + } + if( ! is.null(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end required function checking + # reserved words to avoid bugs (used in this function) + reserved.words <- c("fake_x", "fake_y", "fake_categ") + # end reserved words to avoid bugs (used in this function) + # arg with no default values + mandat.args <- c( + "data1", + "x", + "y" + ) + tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")"))) + if(any(tempo)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end arg with no default values + # argument primary checking + arg.check <- NULL # + text.check <- NULL # + checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools + ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name)) + tempo1 <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = data1, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(x)){ + tempo1 <- fun_check(data = x, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = x, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(y)){ + tempo1 <- fun_check(data = y, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = y, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(categ)){ + tempo1 <- fun_check(data = categ, class = "vector", mode = "character", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = categ, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = categ, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(categ.class.order)){ + if(is.null(categ)){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT IS NOT NULL, BUT categ IS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = categ.class.order, class = "vector", mode = "character", fun.name = function.name) + tempo2 <- fun_check(data = categ.class.order, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A VECTOR OF CHARACTER STRINGS OR A LIST OF VECTOR OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = categ.class.order, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(legend.name)){ + tempo1 <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = legend.name, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = legend.name, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(color)){ + tempo1 <- fun_check(data = color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = color, class = "factor", na.contain = TRUE, fun.name = function.name) + tempo3 <- fun_check(data = color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name) + tempo4 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo4$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE & tempo4$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A VECTOR (OF CHARACTER STRINGS OR INTEGERS) OR A FACTOR OR A LIST OF THESE POSSIBILITIES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = color, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo1 <- fun_check(data = geom, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = geom, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = geom.step.dir, options = c("vh", "hv", "mid"), na.contain = FALSE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = geom.step.dir, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A SINGLE CHARACTER STRING (\"vh\" OR \"hv\" OR \"mid\") OR A LIST OF THESE CHARACTER STRINGS") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(geom.stick.base)){ + tempo1 <- fun_check(data = geom.stick.base, class = "vector", mode = "numeric", na.contain = FALSE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = geom.stick.base, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo1 <- fun_check(data = alpha, prop = TRUE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = alpha, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A SINGLE NUMERIC VALUE BETWEEN 0 AND 1 OR A LIST OF SUCH VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) + tempo2 <- fun_check(data = dot.size, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = dot.shape, class = "vector", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = dot.shape, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A SINGLE SHAPE VALUE OR A LIST OF SINGLE SHAPE VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) + tempo2 <- fun_check(data = dot.border.size, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(dot.border.color)){ + tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name) + tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + # integer colors -> gg_palette + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color MUST BE A SINGLE CHARACTER STRING OF COLOR OR A SINGLE INTEGER VALUE") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = dot.border.color, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo1 <- fun_check(data = line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) + tempo2 <- fun_check(data = line.size, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo2$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + tempo1 <- fun_check(data = line.type, class = "vector", typeof = "integer", double.as.integer.allowed = FALSE, length = 1, fun.name = function.name) + tempo2 <- fun_check(data = line.type, class = "vector", mode = "character", length = 1, fun.name = function.name) + tempo3 <- fun_check(data = line.type, class = "list", na.contain = TRUE, fun.name = function.name) + checked.arg.names <- c(checked.arg.names, tempo3$object.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A SINGLE LINE KIND VALUE OR A LIST OF SINGLE LINE KIND VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + if( ! is.null(x.lim)){ + tempo <- fun_check(data = x.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & any(x.lim %in% c(Inf, -Inf))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x.lim, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(x.lab)){ + if(all(class(x.lab) %in% "expression")){ # to deal with math symbols + tempo <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee) + }else{ + tempo <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x.lab, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = x.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(x.tick.nb)){ + tempo <- fun_check(data = x.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & x.tick.nb < 0){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(x.second.tick.nb)){ + tempo <- fun_check(data = x.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & x.second.tick.nb <= 0){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = x.second.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = x.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.left.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.right.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = x.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) + if( ! is.null(y.lim)){ + tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & any(y.lim %in% c(Inf, -Inf))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.lim, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(y.lab)){ + if(all(class(y.lab) %in% "expression")){ # to deal with math symbols + tempo <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee) + }else{ + tempo <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.lab, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(y.tick.nb)){ + tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & y.tick.nb < 0){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if( ! is.null(y.second.tick.nb)){ + tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE & y.second.tick.nb <= 0){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER") + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = y.second.tick.nb, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = y.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = raster, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = raster.ratio, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + if( ! is.null(raster.threshold)){ + tempo <- fun_check(data = raster.threshold, class = "vector", typeof = "integer", neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = raster.threshold, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(legend.width)){ + tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = legend.width, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(add)){ + tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee) + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = add, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee) + if( ! is.null(lib.path)){ + tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) + if(tempo$problem == FALSE){ + if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA + tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n")) + text.check <- c(text.check, tempo.cat) + arg.check <- c(arg.check, TRUE) + } + } + }else{ + # no fun_check test here, it is just for checked.arg.names + tempo <- fun_check(data = lib.path, class = "vector") + checked.arg.names <- c(checked.arg.names, tempo$object.name) + } + if(any(arg.check) == TRUE){ + stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # + } + # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check() + # end argument primary checking + + + # second round of checking and data preparation + # management of NA arguments + tempo.arg <- names(arg.user.setting) # values provided by the user + tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NA arguments + # management of NULL arguments + tempo.arg <-c( + "data1", + # "x", # inactivated because of hline or vline + # "y", # inactivated because of hline or vline + "geom", + "geom.step.dir", + # "geom.stick.base", # inactivated because can be null + "alpha", + "dot.size", + "dot.shape", + "dot.border.size", + "line.size", + "line.type", + "x.log", + "x.include.zero", + "x.left.extra.margin", + "x.right.extra.margin", + "x.text.angle", + "y.log", + "y.include.zero", + "y.top.extra.margin", + "y.bottom.extra.margin", + "y.text.angle", + "raster", + "raster.ratio", + "text.size", + "title", + "title.text.size", + "legend.show", + # "legend.width", # inactivated because can be null + "article", + "grid", + "return", + "return.ggplot", + "return.gtable", + "plot", + "warn.print" + ) + tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null) + if(any(tempo.log) == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == + } + # end management of NULL arguments + # code that protects set.seed() in the global environment + # end code that protects set.seed() in the global environment + # warning initiation + ini.warning.length <- options()$warning.length + options(warning.length = 8170) + warn <- NULL + warn.count <- 0 + # end warning initiation + # other checkings + # check list lengths (and names of data1 compartments if present) + list.color <- NULL + list.geom <- NULL + list.geom.step.dir <- NULL + list.geom.stick.base <- NULL + list.alpha <- NULL + list.dot.size <- NULL + list.dot.shape <- NULL + list.dot.border.size <- NULL + list.dot.border.color <- NULL + list.line.size <- NULL + list.line.type <- NULL + if(all(class(data1) == "list")){ + if(length(data1) > 6){ + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A LIST OF 6 DATA FRAMES MAXIMUM (6 OVERLAYS MAX)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(is.null(names(data1))){ + names(data1) <- paste0("L", 1:length(data1)) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL NAME COMPARTMENT OF data1 LIST -> NAMES RESPECTIVELY ATTRIBUTED TO EACH COMPARTMENT:\n", paste(names(data1), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + if( ! is.null(x)){ + if( ! (all(class(x) == "list") & length(data1) == length(x))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + }else{ + x <- vector("list", length(data1)) + } + if( ! is.null(y)){ + if( ! (all(class(y) == "list") & length(data1) == length(y))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + }else{ + y <- vector("list", length(data1)) + } + if( ! is.null(categ)){ + if( ! (all(class(categ) == "list") & length(data1) == length(categ))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if( ! is.null(categ.class.order)){ + if( ! (all(class(categ.class.order) == "list") & length(data1) == length(categ.class.order))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if( ! is.null(color)){ + if( ! ((all(class(color) == "list") & length(data1) == length(color)) | ((all(mode(color) == "character") | all(mode(color) == "numeric")) & length(color)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER STRING OR INTEGER") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if((all(mode(color) == "character") | all(mode(color) == "numeric")) & length(color)== 1L){ # convert the single value into a list of single value + list.color <- vector(mode = "list", length = length(data1)) + list.color[] <- color + } + } + if( ! ((all(class(geom) == "list") & length(data1) == length(geom)) | (all(mode(geom) == "character") & length(geom)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(geom) == "character") & length(geom)== 1L){ # convert the single value into a list of single value + list.geom <- vector(mode = "list", length = length(data1)) + list.geom[] <- geom + } + if( ! ((all(class(geom.step.dir) == "list") & length(data1) == length(geom.step.dir)) | (all(mode(geom.step.dir) == "character") & length(geom.step.dir)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(geom.step.dir) == "character") & length(geom.step.dir)== 1L){ # convert the single value into a list of single value + list.geom.step.dir <- vector(mode = "list", length = length(data1)) + list.geom.step.dir[] <- geom.step.dir + } + if( ! is.null(geom.stick.base)){ + if( ! ((all(class(geom.stick.base) == "list") & length(data1) == length(geom.stick.base)) | (all(mode(geom.stick.base) == "numeric") & length(geom.stick.base)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(geom.stick.base) == "numeric") & length(geom.stick.base)== 1L){ # convert the single value into a list of single value + list.geom.stick.base <- vector(mode = "list", length = length(data1)) + list.geom.stick.base[] <- geom.stick.base + } + } + if( ! ((all(class(alpha) == "list") & length(data1) == length(alpha)) | (all(mode(alpha) == "numeric") & length(alpha)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(alpha) == "numeric") & length(alpha)== 1L){ # convert the single value into a list of single value + list.alpha <- vector(mode = "list", length = length(data1)) + list.alpha[] <- alpha + } + if( ! ((all(class(dot.size) == "list") & length(data1) == length(dot.size)) | (all(mode(dot.size) == "numeric") & length(dot.size)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(dot.size) == "numeric") & length(dot.size)== 1L){ # convert the single value into a list of single value + list.dot.size <- vector(mode = "list", length = length(data1)) + list.dot.size[] <- dot.size + } + if( ! ((all(class(dot.shape) == "list") & length(data1) == length(dot.shape)) | (all(mode(dot.shape) != "list") & length(dot.shape)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE SHAPE VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(dot.shape) != "list") & length(dot.shape)== 1L){ # convert the single value into a list of single value + list.dot.shape <- vector(mode = "list", length = length(data1)) + list.dot.shape[] <- dot.shape + } + if( ! ((all(class(dot.border.size) == "list") & length(data1) == length(dot.border.size)) | (all(mode(dot.border.size) == "numeric") & length(dot.border.size)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(dot.border.size) == "numeric") & length(dot.border.size)== 1L){ # convert the single value into a list of single value + list.dot.border.size <- vector(mode = "list", length = length(data1)) + list.dot.border.size[] <- dot.border.size + } + if( ! is.null(dot.border.color)){ + if( ! ((all(class(dot.border.color) == "list") & length(data1) == length(dot.border.color)) | ((all(mode(dot.border.color) == "character") | all(mode(dot.border.color) == "numeric")) & length(dot.border.color)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE CHARACTER STRING OR INTEGER") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if((all(mode(dot.border.color) == "character") | all(mode(dot.border.color) == "numeric")) & length(dot.border.color)== 1L){ # convert the single value into a list of single value + list.dot.border.color <- vector(mode = "list", length = length(data1)) + list.dot.border.color[] <- dot.border.color + } + } + if( ! ((all(class(line.size) == "list") & length(data1) == length(line.size)) | (all(mode(line.size) == "numeric") & length(line.size)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE NUMERIC VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(line.size) == "numeric") & length(line.size)== 1L){ # convert the single value into a list of single value + list.line.size <- vector(mode = "list", length = length(data1)) + list.line.size[] <- line.size + } + if( ! ((all(class(line.type) == "list") & length(data1) == length(line.type)) | (all(mode(line.type) != "list") & length(line.type)== 1L))){ # list of same length as data1 or single value + tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST, OR A SINGLE LINE KIND VALUE") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(all(mode(line.type) != "list") & length(line.type)== 1L){ # convert the single value into a list of single value + list.line.type <- vector(mode = "list", length = length(data1)) + list.line.type[] <- line.type + } + if( ! is.null(legend.name)){ + if( ! (all(class(legend.name) == "list") & length(data1) == length(legend.name))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A LIST OF SAME LENGTH AS data1 IF data1 IS A LIST") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + } + # end check list lengths (and names of data1 compartments if present) + # conversion into lists + if(all(is.data.frame(data1))){ + data1 <- list(L1 = data1) + if(all(class(x) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + x <- list(L1 = x) + } + if(all(class(y) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + y <- list(L1 = y) + } + if( ! is.null(categ)){ + if(all(class(categ) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + categ <- list(L1 = categ) + } + } + if( ! is.null(categ.class.order)){ + if(all(class(categ.class.order) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + categ.class.order <- list(L1 = categ.class.order) + } + } + if( ! is.null(color)){ + if(all(class(color) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + color <- list(L1 = color) + } + } + if(all(class(geom) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + geom <- list(L1 = geom) + } + if(all(class(geom.step.dir) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + geom.step.dir <- list(L1 = geom.step.dir) + } + if( ! is.null(geom.stick.base)){ + if(all(class(geom.stick.base) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + geom.stick.base <- list(L1 = geom.stick.base) + } + } + if(all(class(alpha) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + alpha <- list(L1 = alpha) + } + if(all(class(dot.size) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + dot.size <- list(L1 = dot.size) + } + if(all(class(dot.shape) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + dot.shape <- list(L1 = dot.shape) + } + if(all(class(dot.border.size) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + dot.border.size <- list(L1 = dot.border.size) + } + if( ! is.null(dot.border.color)){ + if(all(class(dot.border.color) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + dot.border.color <- list(L1 = dot.border.color) + } + } + if(all(class(line.size) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + line.size <- list(L1 = line.size) + } + if(all(class(line.type) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + line.type <- list(L1 = line.type) + } + if( ! is.null(legend.name)){ + if(all(class(legend.name) == "list")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT CANNOT BE A LIST IF data1 IS A DATA FRAME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + legend.name <- list(L1 = legend.name) + } + } + }else if( ! all(sapply(data1, FUN = "class") == "data.frame")){ # if not a data frame, data1 can only be a list, as tested above + tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + # single value converted into list now reattributed to the argument name + if( ! is.null(color)){ + if( ! is.null(list.color)){ + color <- list.color + } + } + if( ! is.null(list.geom)){ + geom <- list.geom + } + if( ! is.null(list.geom.step.dir)){ + geom.step.dir <- list.geom.step.dir + } + if( ! is.null(geom.stick.base)){ + if( ! is.null(list.geom.stick.base)){ + geom.stick.base <- list.geom.stick.base + } + } + if( ! is.null(list.alpha)){ + alpha <- list.alpha + } + if( ! is.null(list.dot.size)){ + dot.size <- list.dot.size + } + if( ! is.null(list.dot.shape)){ + dot.shape <- list.dot.shape + } + if( ! is.null(list.dot.border.size)){ + dot.border.size <- list.dot.border.size + } + if( ! is.null(dot.border.color)){ + if( ! is.null(list.dot.border.color)){ + dot.border.color <- list.dot.border.color + } + } + if( ! is.null(list.line.size)){ + line.size <- list.line.size + } + if( ! is.null(list.line.type)){ + line.type <- list.line.type + } + # end single value converted into list now reattributed to the argument name + # data, x, y, geom, alpha, dot.size, shape, dot.border.size, line.size, line.type, legend.name are list now + # if non-null, categ, categ.class.order, legend.name, color, dot.border.color are list now + # end conversion into lists + # verif of add + if( ! is.null(add)){ + if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by + + tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + + }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ # + tempo.cat <- paste0("ERROR IN ", function.name, ": FOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by ) + tempo.cat <- paste0("ERROR IN ", function.name, ": add ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + # end verif of add + # management of add containing facet + facet.categ <- NULL + if( ! is.null(add)){ + facet.check <- TRUE + tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) # + tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap") + tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid") + tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep") + if(length(data1) > 1 & (any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")) | grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid"))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nfacet PANELS CANNOT BE USED IF MORE THAN ONE DATA FRAME IN THE data1 ARGUMENT\nPLEASE REWRITE THE add STRING AND RERUN") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"))){ + tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")]))) + facet.categ <- list(names(tempo1$params$facets)) # list of length 1 + tempo.text <- "facet_wrap OR facet_rep_wrap" + facet.check <- FALSE + }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){ + tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")]))) + facet.categ <- list(c(names(tempo1$params$rows), names(tempo1$params$cols))) # list of length 1 + tempo.text <- "facet_grid OR facet_rep_grid" + facet.check <- FALSE + } + if(facet.check == FALSE & ! all(facet.categ %in% names(data1[[1]]))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL + tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1[[1]]), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + } + # if facet.categ is not NULL, it is a list of length 1 now + # end management of add containing facet + # legend name filling + if(is.null(legend.name) & ! is.null(categ)){ + legend.name <- categ + }else if(is.null(legend.name) & is.null(categ)){ + legend.name <- vector("list", length(data1)) # null list + } + # legend.name not NULL anymore (list) + # end legend name filling + # ini categ for legend display + fin.lg.disp <- vector("list", 6) # will be used at the end to display or not legends + fin.lg.disp[] <- FALSE + legend.disp <- vector("list", length(data1)) + if(is.null(categ) | legend.show == FALSE){ + legend.disp[] <- FALSE + }else{ + for(i2 in 1:length(data1)){ + if(is.null(categ[[i2]])){ + legend.disp[[i2]] <- FALSE + }else{ + legend.disp[[i2]] <- TRUE + } + } + } + # end ini categ for legend display + # integer colors into gg_palette + tempo.check.color <- NULL + for(i1 in 1:length(data1)){ + if(any(is.na(color[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), ": color ARGUMENT CANNOT CONTAIN NA") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo.check.color <- c(tempo.check.color, fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem) + } + tempo.check.color <- ! tempo.check.color # invert TRUE and FALSE because if integer, then problem = FALSE + if(any(tempo.check.color == TRUE)){ # convert integers into colors + tempo.integer <- unlist(color[tempo.check.color]) + tempo.color <- fun_gg_palette(max(tempo.integer, na.rm = TRUE)) + for(i1 in 1:length(data1)){ + if(tempo.check.color[i1] == TRUE){ + color[[i1]] <-tempo.color[color[[i1]]] + } + } + } + # end integer colors into gg_palette + # loop (checking inside list compartment) + compart.null.color <- 0 # will be used to attribute a color when color is non-null but a compartment of color is NULL + data1.ini <- data1 # to report NA removal + removed.row.nb <- vector("list", length = length(data1)) # to report NA removal. Contains NULL + removed.rows <- vector("list", length = length(data1)) # to report NA removal. Contains NULL + for(i1 in 1:length(data1)){ + tempo <- fun_check(data = data1[[i1]], data.name = ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "data.frame", na.contain = TRUE, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + # reserved word checking + if(any(names(data1[[i1]]) %in% reserved.words)){ # I do not use fun_name_change() because cannot control y before creating "fake_y". But ok because reserved are not that common + tempo.cat <- paste0("ERROR IN ", function.name, ": COLUMN NAMES OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " ARGUMENT CANNOT BE ONE OF THESE WORDS\n", paste(reserved.words, collapse = " "), "\nTHESE ARE RESERVED FOR THE ", function.name, " FUNCTION") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if( ! (is.null(add))){ + if(any(sapply(X = reserved.words, FUN = grepl, x = add))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nFOLLOWING COLUMN NAMES HAVE TO BE CHANGED:\n", paste(arg.names[sapply(X = reserved.words, FUN = grepl, x = add)], collapse = "\n"), "\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(any(sapply(X = arg.names, FUN = grepl, x = add))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end reserved word checking + # check of geom now because required for y argument + tempo <- fun_check(data = geom[[i1]], data.name = ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), options = c("geom_point", "geom_line", "geom_path", "geom_step", "geom_hline", "geom_vline", "geom_stick"), length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(geom[[i1]] == "geom_step" & is.null(geom.step.dir[[i1]])){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(geom.step.dir)== 1L, "geom.step.dir", paste0("ELEMENT ", i1, " OF geom.step.dir ARGUMENT")), ": geom.step.dir ARGUMENT CANNOT BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("ELEMENT ", i1, " OF geom")), " ARGUMENT IS \"geom_step\"\nHERE geom.step.dir ARGUMENT IS: ", paste(geom.step.dir[[i1]], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(geom[[i1]] == "geom_step" & ! is.null(geom.step.dir[[i1]])){ + tempo <- fun_check(data = geom.step.dir[[i1]], data.name = ifelse(length(geom.step.dir)== 1L, "geom.step.dir", paste0("geom.step.dir NUMBER ", i1)), options = c("vh", "hv", "mid"), length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if( ! (is.null(geom.stick.base))){ + if(geom[[i1]] == "geom_stick" & ! is.null(geom.stick.base[[i1]])){ + tempo <- fun_check(data = geom.stick.base[[i1]], data.name = ifelse(length(geom.stick.base)== 1L, "geom.stick.base", paste0("geom.stick.base NUMBER ", i1)), mode = "numeric", length = 1, na.contain = FALSE, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + } + # end check of geom now because required for y argument + if(is.null(x[[i1]])){ + if(all(geom[[i1]] != "geom_hline")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": x ARGUMENT CANNOT BE NULL EXCEPT IF ", ifelse(length(geom)== 1L, "x", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_hline\"\nHERE geom ARGUMENT IS: ", paste(geom[[i1]], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + x[[i1]] <- "fake_x" + data1[[i1]] <- cbind(data1[[i1]], fake_x = NA, stringsAsFactors = TRUE) + data1[[i1]][, "fake_x"] <- as.numeric(data1[[i1]][, "fake_x"]) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT ASSOCIATED TO ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT ", geom[[i1]], " -> FAKE COLUMN ADDED TO DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", NAMED \"fake_x\" FOR FINAL DRAWING") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(all(geom[[i1]] == "geom_hline")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": x ARGUMENT MUST BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_hline\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo <- fun_check(data = x[[i1]], data.name = ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), class = "vector", mode = "character", length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(is.null(y[[i1]])){ + if(all(geom[[i1]] != "geom_vline")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": y ARGUMENT CANNOT BE NULL EXCEPT IF ", ifelse(length(geom)== 1L, "y", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_vline\"\nHERE geom ARGUMENT IS: ", paste(geom[[i1]], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + y[[i1]] <- "fake_y" + data1[[i1]] <- cbind(data1[[i1]], fake_y = NA, stringsAsFactors = TRUE) + data1[[i1]][, "fake_y"] <- as.numeric(data1[[i1]][, "fake_y"]) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT ASSOCIATED TO ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT ", geom[[i1]], " -> FAKE COLUMN ADDED TO DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", NAMED \"fake_y\" FOR FINAL DRAWING") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else{ + if(all(geom[[i1]] == "geom_vline")){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ": y ARGUMENT MUST BE NULL IF ", ifelse(length(geom)== 1L, "geom", paste0("geom NUMBER ", i1)), " ARGUMENT IS \"geom_vline\"") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo <- fun_check(data = y[[i1]], data.name = ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), class = "vector", mode = "character", length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + # x[[i1]] and y[[i1]] not NULL anymore + if( ! (x[[i1]] %in% names(data1[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(x[[i1]], collapse = " ")))) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if( ! (y[[i1]] %in% names(data1[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(y[[i1]], collapse = " ")))) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo <- fun_check(data = data1[[i1]][, x[[i1]]], data.name = ifelse(length(x)== 1L, "x ARGUMENT (AS COLUMN NAME OF data1 DATA FRAME)", paste0("ELEMENT ", i1, " OF x ARGUMENT", " (AS COLUMN NAME OF data1 DATA FRAME NUMBER ", i1, ")")), class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo <- fun_check(data = data1[[i1]][, y[[i1]]], data.name = ifelse(length(y)== 1L, "y ARGUMENT (AS COLUMN NAME OF data1 DATA FRAME)", paste0("ELEMENT ", i1, " OF y ARGUMENT", " (AS COLUMN NAME OF data1 DATA FRAME NUMBER ", i1, ")")), class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(x[[i1]] == "fake_x" & y[[i1]] == "fake_y"){ # because the code cannot accept to be both "fake_x" and "fake_y" at the same time + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 2\nTHE CODE CANNOT ACCEPT x AND y TO BE \"fake_x\" AND \"fake_y\" IN THE SAME DATA FRAME ", i1, " ") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + + if(( ! is.null(categ)) & ( ! is.null(categ[[i1]]))){ # is.null(categ[[i1]]) works even if categ is NULL # is.null(categ[[i1]]) works even if categ is NULL # if categ[[i1]] = NULL, fake_categ will be created later on + tempo <- fun_check(data = categ[[i1]], data.name = ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")),, class = "vector", mode = "character", length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if( ! (categ[[i1]] %in% names(data1[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT MUST BE A COLUMN NAME OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT\nHERE IT IS: ", paste(categ[[i1]], collapse = " ")))) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + tempo1 <- fun_check(data = data1[[i1]][, categ[[i1]]], data.name = ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) + tempo2 <- fun_check(data = data1[[i1]][, categ[[i1]]], data.name = ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), class = "factor", na.contain = TRUE, fun.name = function.name) + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " MUST BE A FACTOR OR CHARACTER VECTOR") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(tempo1$problem == FALSE){ + data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + + } + if(geom[[i1]] == "geom_vline" | geom[[i1]] == "geom_hline"){ + if(length(unique(data1[[i1]][, categ[[i1]]])) != nrow(data1[[i1]])){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(geom)== 1L, "geom OF data1 ARGUMENT", paste0("geom NUMBER ", i1, " OF DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " ARGUMENT IS ", geom[[i1]], ", MEANING THAT ", ifelse(length(categ)== 1L, "categ OF data1 ARGUMENT", paste0("ELEMENT ", i1, " OF categ ARGUMENT IN DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " MUST HAVE A DIFFERENT CLASS PER LINE OF data1 (ONE x VALUE PER CLASS)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + }else if(( ! is.null(categ)) & is.null(categ[[i1]])){ # is.null(categ[[i1]]) works even if categ is NULL # if categ[[i1]] = NULL, fake_categ will be created. WARNING: is.null(categ[[i1]]) means no legend display (see above), because categ has not been precised. This also means a single color for data1[[i1]] + if(length(color[[i1]]) > 1){ # 0 means is.null(color[[i1]]) or is.null(color) and 1 is ok -> single color for data1[[i1]] + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT BUT CORRESPONDING COLORS IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " HAS LENGTH OVER 1\n", paste(color[[i1]], collapse = " "), "\nWHICH IS NOT COMPATIBLE WITH NULL CATEG -> COLOR RESET TO A SINGLE COLOR") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + color[i1] <- list(NULL) # will provide a single color below # Warning color[[i1]] <- NULL removes the compartment + } + categ[[i1]] <- "fake_categ" + data1[[i1]] <- cbind(data1[[i1]], fake_categ = "", stringsAsFactors = TRUE) + # inactivated because give a different color to different "Line_" categ while a single color for all the data1[[i1]] required. Thus, put back after the color management + # if(geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline"){ + # data1[[i1]][, "fake_categ"] <- paste0("Line_", 1:nrow(data1[[i1]])) + # }else{ + data1[[i1]][, "fake_categ"] <- data1[[i1]][, "fake_categ"] # as.numeric("") create a vector of NA but class numeric + # } + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ")), " ARGUMENT -> FOR DATA FRAME ", ifelse(length(data1)== 1L, "data1 ARGUMENT:", paste0("NUMBER ", i1, " OF data1 ARGUMENT:")), "\n- FAKE \"fake_categ\" COLUMN ADDED FILLED WITH \"\"(OR WITH \"Line_...\" FOR LINES)\n- SINGLE COLOR USED FOR PLOTTING\n- NO LEGEND DISPLAYED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # OK: if categ is not NULL, all the non-null categ columns of data1 are factors from here + + # management of log scale and Inf removal + if(x[[i1]] != "fake_x"){ + if(any(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN ", ifelse(length(categ)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(y[[i1]] != "fake_y"){ + if(any(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN ", ifelse(length(categ)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # log conversion + if(x.log != "no"){ + tempo1 <- ! is.finite(data1[[i1]][, x[[i1]]]) # where are initial NA and Inf + data1[[i1]][, x[[i1]]] <- suppressWarnings(get(x.log)(data1[[i1]][, x[[i1]]]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + if(any( ! (tempo1 | is.finite(data1[[i1]][, x[[i1]]])))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN ", ifelse(length(categ)== 1L, "x", paste0("ELEMENT ", i1, " OF x ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(y.log != "no"){ + tempo1 <- ! is.finite(data1[[i1]][, y[[i1]]]) # where are initial NA and Inf + data1[[i1]][, y[[i1]]] <- suppressWarnings(get(y.log)(data1[[i1]][, y[[i1]]]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + if(any( ! (tempo1 | is.finite(data1[[i1]][, y[[i1]]])))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN ", ifelse(length(categ)== 1L, "y", paste0("ELEMENT ", i1, " OF y ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # Inf removal + # removed.row.nb[[i1]] <- NULL # already NULL and Warning this removes the compartment + removed.rows[[i1]] <- data.frame(stringsAsFactors = FALSE) + if(any(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])) detects only Inf + removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], which(( ! is.finite(data1[[i1]][, x[[i1]]])) & ( ! is.na(data1[[i1]][, x[[i1]]])))) + } + if(any(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))){ # is.finite also detects NA: ( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])) detects only Inf + removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], which(( ! is.finite(data1[[i1]][, y[[i1]]])) & ( ! is.na(data1[[i1]][, y[[i1]]])))) + } + if( ! is.null(removed.row.nb[[i1]])){ + removed.row.nb[[i1]] <- unique(removed.row.nb[[i1]]) # to remove the duplicated positions (NA in both x and y) + removed.rows[[i1]] <- rbind(removed.rows[[i1]], data1.ini[[i1]][removed.row.nb[[i1]], ]) # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation + data1[[i1]] <- data1[[i1]][-removed.row.nb[[i1]], ] + data1.ini[[i1]] <- data1.ini[[i1]][-removed.row.nb[[i1]], ] # + } + # From here, data1 and data.ini have no more Inf + # end Inf removal + # x.lim and y.lim dealt later on, after the end f the loop + # end management of log scale and Inf removal + # na detection and removal + column.check <- unique(unlist(c( # unlist because creates a list + if(x[[i1]] == "fake_x"){NULL}else{x[[i1]]}, + if(y[[i1]] == "fake_y"){NULL}else{y[[i1]]}, + if( ! is.null(categ)){if(is.null(categ[[i1]])){NULL}else{categ[[i1]]}}, + if( ! is.null(facet.categ)){if(is.null(facet.categ[[i1]])){NULL}else{facet.categ[[i1]]}} + ))) # dot.categ because can be a 3rd column of data1 + if(any(is.na(data1[[i1]][, column.check]))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + for(i3 in 1:length(column.check)){ + if(any(is.na(data1[[i1]][, column.check[i3]]))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i3], " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT"))) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + tempo <- unique(unlist(lapply(lapply(c(data1[[i1]][column.check]), FUN = is.na), FUN = which))) + removed.row.nb[[i1]] <- c(removed.row.nb[[i1]], tempo) + removed.rows[[i1]] <- rbind(removed.rows[[i1]], data1.ini[[i1]][tempo, ]) # # tempo used because removed.row.nb is not empty. Here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance) + column.check <- column.check[ ! (column.check == x[[i1]] | column.check == y[[i1]])] # remove x and y to keep quali columns + if(length(tempo) != 0){ + data1[[i1]] <- data1[[i1]][-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former + data1.ini[[i1]] <- data1.ini[[i1]][-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former + for(i4 in 1:length(column.check)){ + if(any( ! unique(removed.rows[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]]))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i4], " OF ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA REMOVAL\n(IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[[i1]][, column.check[i4]])[ ! unique(removed.rows[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]])], collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + tempo.levels <- levels(data1[[i1]][, column.check[i4]])[levels(data1[[i1]][, column.check[i4]]) %in% unique(as.character(data1[[i1]][, column.check[i4]]))] + data1[[i1]][, column.check[i4]] <- factor(as.character(data1[[i1]][, column.check[i4]]), levels = tempo.levels) + if(column.check[i4] %in% categ[[i1]] & ! is.null(categ.class.order)){ + categ.class.order[[i1]] <- levels(data1[[i1]][, column.check[i4]])[levels(data1[[i1]][, column.check[i4]]) %in% unique(data1[[i1]][, column.check[i4]])] # remove the absent class in the categ.class.order vector + data1[[i1]][, column.check[i4]] <- factor(as.character(data1[[i1]][, column.check[i4]]), levels = unique(categ.class.order[[i1]])) + } + } + } + } + } + # end na detection and removal + # From here, data1 and data.ini have no more NA or NaN in x, y, categ (if categ != NULL) and facet.categ (if categ != NULL) + if( ! is.null(categ.class.order)){ + # the following check will be done several times but I prefer to keep it here, after the creation of categ + if(is.null(categ[[i1]]) & ! is.null(categ.class.order[[i1]])){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ ARGUMENT CANNOT BE NULL IF COMPARTMENT ", i1, " OF categ.class.order ARGUMENT IS NOT NULL: ", paste(categ.class.order, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + if(is.null(categ.class.order[[i1]])){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i1, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + data1[[i1]][, categ[[i1]]] <- factor(as.character(data1[[i1]][, categ[[i1]]])) # if already a factor, change nothing, if characters, levels according to alphabetical order + categ.class.order[[i1]] <- levels(data1[[i1]][, categ[[i1]]]) # character vector that will be used later + }else{ + tempo <- fun_check(data = categ.class.order[[i1]], data.name = paste0("COMPARTMENT ", i1 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[[i1]][, categ[[i1]]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(any(duplicated(categ.class.order[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i1]], collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if( ! (all(categ.class.order[[i1]] %in% unique(data1[[i1]][, categ[[i1]]])) & all(unique(data1[[i1]][, categ[[i1]]]) %in% categ.class.order[[i1]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i1, " OF categ.class.order ARGUMENT MUST BE CLASSES OF COMPARTMENT ", i1, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i1]], collapse = " "), "\nFOR COMPARTMENT ", i1, " OF categ.class.order AND IT IS:\n", paste(unique(data1[[i1]][, categ[[i1]]]), collapse = " "), "\nFOR COLUMN ", categ[[i1]], " OF data1 NUMBER ", i1) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]], levels = categ.class.order[[i1]]) # reorder the factor + } + names(categ.class.order)[i1] <- categ[[i1]] + } + } + # OK: if categ.class.order is not NULL, all the NULL categ.class.order columns of data1 are character from here + + if( ! is.null(legend.name[[i1]])){ + tempo <- fun_check(data = legend.name[[i1]], data.name = ifelse(length(legend.name)== 1L, "legend.name", paste0("legend.name NUMBER ", i1)),, class = "vector", mode = "character", length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if( ! is.null(color)){ # if color is NULL, will be filled later on + # check the nature of color + if(is.null(color[[i1]])){ + compart.null.color <- compart.null.color + 1 + color[[i1]] <- grey(compart.null.color / 8) # cannot be more than 7 overlays. Thus 7 different greys. 8/8 is excluded because white dots + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL COLOR IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " ASSOCIATED TO ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", SINGLE COLOR ", paste(color[[i1]], collapse = " "), " HAS BEEN ATTRIBUTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + tempo1 <- fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name) # na.contain = TRUE in case of colum of data1 + tempo2 <- fun_check(data = color[[i1]], data.name = ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), class = "factor", na.contain = TRUE, fun.name = function.name) # idem + if(tempo1$problem == TRUE & tempo2$problem == TRUE){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A FACTOR OR CHARACTER VECTOR OR INTEGER VECTOR") # integer possible because dealt above + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if( ! (all(color[[i1]] %in% colors() | grepl(pattern = "^#", color[[i1]])))){ # check that all strings of low.color start by # + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors() OR A COLUMN NAME OF THE data1 PARAMETER: ", paste(unique(color[[i1]]), collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(any(is.na(color[[i1]]))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), ", THE COLORS:\n", paste(unique(color[[i1]]), collapse = " "), "\nCONTAINS NA") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # end check the nature of color + # check the length of color + if(is.null(categ) & length(color[[i1]]) != 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A SINGLE COLOR IF categ IS NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if( ! is.null(categ)){ + # No problem of NA management by ggplot2 because already removed + if(categ[[i1]] == "fake_categ" & length(color[[i1]]) != 1){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE A SINGLE COLOR IF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IS NULL") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(length(color[[i1]]) == length(unique(data1[[i1]][, categ[[i1]]]))){ # here length(color) is equal to the different number of categ + data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", THE FOLLOWING COLORS:\n", paste(color[[i1]], collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else if(length(color[[i1]]) == length(data1[[i1]][, categ[[i1]]])){# here length(color) is equal to nrow(data1[[i1]]) -> Modif to have length(color) equal to the different number of categ (length(color) == length(levels(data1[[i1]][, categ[[i1]]]))) + data1[[i1]] <- cbind(data1[[i1]], color = color[[i1]], stringsAsFactors = TRUE) + tempo.check <- unique(data1[[i1]][ , c(categ[[i1]], "color")]) + if( ! (nrow(data1[[i1]]) == length(color[[i1]]) & nrow(tempo.check) == length(unique(data1[[i1]][ , categ[[i1]]])))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color")), " ARGUMENT HAS THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF THIS categ:\n", paste(unique(mapply(FUN = "paste", data1[[i1]][ ,categ[[i1]]], data1[[i1]][ ,"color"])), collapse = "\n")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order + color[[i1]] <- unique(color[[i1]][order(data1[[i1]][, categ[[i1]]])]) # Modif to have length(color) equal to the different number of categ (length(color) == length(levels(data1[[i1]][, categ[[i1]]]))) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count, ") FROM FUNCTION ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " HAS THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " COLUMN VALUES\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ AS:\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " "), "\n", paste(color[[i1]], collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else if(length(color[[i1]])== 1L){ + data1[[i1]][, categ[[i1]]] <- factor(data1[[i1]][, categ[[i1]]]) # if already a factor, change nothing, if characters, levels according to alphabetical order + color[[i1]] <- rep(color[[i1]], length(levels(data1[[i1]][, categ[[i1]]]))) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") IN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), ", COLOR HAS LENGTH 1 MEANING THAT ALL THE DIFFERENT CLASSES OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), "\n", paste(levels(factor(data1[[i1]][, categ[[i1]]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(color[[i1]], collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST BE\n(1) LENGTH 1\nOR (2) THE LENGTH OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), " COLUMN VALUES\nOR (3) THE LENGTH OF THE CLASSES IN THIS COLUMN\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LENGTH ", length(data1[[i1]][, categ[[i1]]]), " AND CATEG CLASS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]])), "\nPRESENCE OF NA IN THE COLUMN x, y OR categ OF data1 COULD BE THE PROBLEME") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + } + if((geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline") & ! is.null(categ[[i1]])){ # add here after the color management, to deal with the different lines to plot inside any data[[i1]] + if(categ[[i1]] == "fake_categ"){ + data1[[i1]][, "fake_categ"] <- factor(paste0("Line_", formatC(1:nrow(data1[[i2]]), width = nchar(nrow(data1[[i2]])), flag = "0"))) + } + } + tempo <- fun_check(data = alpha[[i1]], data.name = ifelse(length(alpha)== 1L, "alpha", paste0("alpha NUMBER ", i1)), prop = TRUE, length = 1, fun.name = function.name) + if(tempo$problem == TRUE){ + stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + # end loop (checking inside list compartment) + if(length(data1) > 1){ + if(length(unique(unlist(x)[ ! x == "fake_x"])) > 1){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE x ARGUMENT DOES NOT CONTAIN IDENTICAL COLUMN NAMES:\n", paste(unlist(x), collapse = " "), "\nX-AXIS OVERLAYING DIFFERENT VARIABLES?") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(length(data1) > 1){ + if(length(unique(unlist(y)[ ! y == "fake_y"])) > 1){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE y ARGUMENT DOES NOT CONTAIN IDENTICAL COLUMN NAMES:\n", paste(unlist(y), collapse = " "), "\nY-AXIS OVERLAYING DIFFERENT VARIABLES?") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + if(sum(geom %in% "geom_point") > 3){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT HAVE MORE THAN THREE \"geom_point\" ELEMENTS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(length(geom) - sum(geom %in% "geom_point") > 3){ + tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT CANNOT HAVE MORE THAN THREE LINE ELEMENTS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + # x.lim management before transfo by x.log + if(x.log != "no" & ! is.null(x.lim)){ + if(any(x.lim <= 0)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE x.log ARGUMENT SET TO ", x.log, ":\n", paste(x.lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(any( ! is.finite(if(x.log == "log10"){log10(x.lim)}else{log2(x.lim)}))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT RETURNS INF/NA WITH THE x.log ARGUMENT SET TO ", x.log, "\nAS SCALE COMPUTATION IS ", ifelse(x.log == "log10", "log10", "log2"), ":\n", paste(if(x.log == "log10"){log10(x.lim)}else{log2(x.lim)}, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(x.log != "no" & x.include.zero == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") x.log ARGUMENT SET TO ", x.log, " AND x.include.zero ARGUMENT SET TO TRUE -> x.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + x.include.zero <- FALSE + } + # end x.lim management before transfo by x.log + # y.lim management before transfo by y.log + if(y.log != "no" & ! is.null(y.lim)){ + if(any(y.lim <= 0)){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(y.log != "no" & y.include.zero == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + y.include.zero <- FALSE + } + # end y.lim management before transfo by y.log + # end other checkings + # reserved word checking + #already done above + # end reserved word checking + # end second round of checking and data preparation + + + # package checking + fun_pack(req.package = c( + "gridExtra", + "ggplot2", + "lemon", + "scales" + ), lib.path = lib.path) + # packages Cairo and grid tested by fun_gg_point_rast() + # end package checking + + + + + # main code + # axes management + if(is.null(x.lim)){ + if(any(unlist(mapply(FUN = "[[", data1, x, SIMPLIFY = FALSE)) %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE x COLUMN IN data1 CONTAINS -Inf OR Inf VALUES THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + x.lim <- suppressWarnings(range(unlist(mapply(FUN = "[[", data1, x, SIMPLIFY = FALSE)), na.rm = TRUE, finite = TRUE)) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only. y.lim added here. If NULL, ok if y argument has values + }else if(x.log != "no"){ + x.lim <- get(x.log)(x.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + } + if(x.log != "no"){ + if(any( ! is.finite(x.lim))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE x.log ARGUMENT SET TO ", x.log, ":\n", paste(x.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(suppressWarnings(all(x.lim %in% c(Inf, -Inf)))){ # happen when x is only NULL + if(all(unlist(geom) %in% c("geom_vline", "geom_stick"))){ + tempo.cat <- paste0("ERROR IN ", function.name, " NOT POSSIBLE TO DRAW geom_vline OR geom_stick KIND OF LINES ALONE IF x.lim ARGUMENT IS SET TO NULL, SINCE NO X-AXIS DEFINED (", ifelse(length(x)== 1L, "x", paste0("ELEMENT ", i1, " OF x")), " ARGUMENT MUST BE NULL FOR THESE KIND OF LINES)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, " x.lim ARGUMENT MADE OF NA, -Inf OR Inf ONLY: ", paste(x.lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + x.lim.order <- order(x.lim) # to deal with inverse axis + # print(x.lim.order) + x.lim <- sort(x.lim) + x.lim[1] <- x.lim[1] - abs(x.lim[2] - x.lim[1]) * ifelse(diff(x.lim.order) > 0, x.right.extra.margin, x.left.extra.margin) # diff(x.lim.order) > 0 means not inversed axis + x.lim[2] <- x.lim[2] + abs(x.lim[2] - x.lim[1]) * ifelse(diff(x.lim.order) > 0, x.left.extra.margin, x.right.extra.margin) # diff(x.lim.order) > 0 means not inversed axis + if(x.include.zero == TRUE){ # no need to check x.log != "no" because done before + x.lim <- range(c(x.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + } + x.lim <- x.lim[x.lim.order] + if(any(is.na(x.lim))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 3") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(is.null(y.lim)){ + if(any(unlist(mapply(FUN = "[[", data1, y, SIMPLIFY = FALSE)) %in% c(Inf, -Inf))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE y COLUMN IN data1 CONTAINS -Inf OR Inf VALUES THAT WILL NOT BE CONSIDERED IN THE PLOT RANGE") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + y.lim <- suppressWarnings(range(unlist(mapply(FUN = "[[", data1, y, SIMPLIFY = FALSE)), na.rm = TRUE, finite = TRUE)) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only. y.lim added here. If NULL, ok if y argument has values + }else if(y.log != "no"){ + y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope + } + if(y.log != "no"){ + if(any( ! is.finite(y.lim))){ + tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # happen when y is only NULL + if(all(unlist(geom) == "geom_vline")){ + tempo.cat <- paste0("ERROR IN ", function.name, " NOT POSSIBLE TO DRAW geom_vline KIND OF LINES ALONE IF y.lim ARGUMENT IS SET TO NULL, SINCE NO Y-AXIS DEFINED (", ifelse(length(y)== 1L, "y", paste0("ELEMENT ", i1, " OF y")), " ARGUMENT MUST BE NULL FOR THESE KIND OF LINES)") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, " y.lim ARGUMENT MADE OF NA, -Inf OR Inf ONLY: ", paste(y.lim, collapse = " ")) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + y.lim.order <- order(y.lim) # to deal with inverse axis + y.lim <- sort(y.lim) + y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 means not inversed axis + y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 means not inversed axis + if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before + y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only + } + y.lim <- y.lim[y.lim.order] + if(any(is.na(y.lim))){ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 4") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + # end axes management + + + + + # create a fake categ if NULL to deal with legend display + if(is.null(categ)){ + categ <- vector("list", length(data1)) + categ[] <- "fake_categ" + for(i2 in 1:length(data1)){ + data1[[i2]] <- cbind(data1[[i2]], fake_categ = "", stringsAsFactors = TRUE) + if(geom[[i2]] == "geom_hline" | geom[[i2]] == "geom_vline"){ + data1[[i2]][, "fake_categ"] <- factor(paste0("Line_", 1:nrow(data1[[i2]]))) + } + } + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL categ ARGUMENT -> FAKE \"fake_categ\" COLUMN ADDED TO EACH DATA FRAME OF data1, AND FILLED WITH \"\"") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + # categ is not NULL anymore + if(is.null(categ.class.order)){ + categ.class.order <- vector("list", length = length(data1)) + tempo.categ.class.order <- NULL + for(i2 in 1:length(categ.class.order)){ + categ.class.order[[i2]] <- levels(data1[[i2]][, categ[[i2]]]) + names(categ.class.order)[i2] <- categ[[i2]] + tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]]) + } + if(any(unlist(legend.disp))){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR CLASS ORDERING:\n", paste(tempo.categ.class.order, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end create a fake categ if NULL to deal with legend display + # categ.class.order is not NULL anymore + + + # vector of color with length as in levels(categ) of data1 + if(is.null(color)){ + color <- vector("list", length(data1)) + length.categ.list <- lapply(lapply(mapply(FUN = "[[", data1, categ, SIMPLIFY = FALSE), FUN = unique), FUN = function(x){length(x[ ! is.na(x)])}) + length.categ.list[sapply(categ, FUN = "==", "fake_categ")] <- 1 # when is.null(color), a single color for all the dots or lines of data[[i1]] that contain "fake_categ" category + total.categ.length <- sum(unlist(length.categ.list), na.rm = TRUE) + tempo.color <- fun_gg_palette(total.categ.length) + tempo.count <- 0 + for(i2 in 1:length(data1)){ + color[[i2]] <- tempo.color[(1:length.categ.list[[i2]]) + tempo.count] + tempo.count <- tempo.count + length.categ.list[[i2]] + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") NULL color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i2, " OF categ ARGUMENT")), " (", categ[[i2]], ") IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i2, " OF data1 ARGUMENT")), ":\n", paste(color[[i2]], collapse = " "), "\n", paste(if(all(levels(data1[[i2]][, categ[[i2]]]) == "")){'\"\"'}else{levels(data1[[i2]][, categ[[i2]]])}, collapse = " ")) + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end vector of color with length as in levels(categ) of data1 + # color is not NULL anymore + + + + + + # last check + for(i1 in 1:length(data1)){ + if(categ[[i1]] != "fake_categ" & length(color[[i1]]) != length(unique(data1[[i1]][, categ[[i1]]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, " LAST CHECK: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE THE LENGTH OF LEVELS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LEVELS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]])), "\nREMINDER: A SINGLE COLOR PER CLASS OF CATEG AND A SINGLE CLASS OF CATEG PER COLOR MUST BE RESPECTED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else if(categ[[i1]] == "fake_categ" & length(color[[i1]]) != 1){ + tempo.cat <- paste0("ERROR IN ", function.name, " LAST CHECK: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE LENGTH 1 WHEN ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IS NULL\nHERE IT IS COLOR LENGTH ", length(color[[i1]])) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + # end last check + + + + + + # conversion of geom_hline and geom_vline + for(i1 in 1:length(data1)){ + if(geom[[i1]] == "geom_hline" | geom[[i1]] == "geom_vline"){ + final.data.frame <- data.frame() + for(i3 in 1:nrow(data1[[i1]])){ + tempo.data.frame <- rbind(data1[[i1]][i3, ], data1[[i1]][i3, ], stringsAsFactors = TRUE) + if(geom[[i1]] == "geom_hline"){ + tempo.data.frame[, x[[i1]]] <- x.lim + }else if(geom[[i1]] == "geom_vline"){ + tempo.data.frame[, y[[i1]]] <- y.lim + }else{ + tempo.cat <- paste0("ERROR IN ", function.name, ": CODE INCONSISTENCY 5") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + # 3 lines below inactivated because I put that above + # if(is.null(categ[[i1]])){ + # data1[, "fake_categ"] <- paste0("Line_", i3) + # } + final.data.frame <- rbind(final.data.frame, tempo.data.frame, stringsAsFactors = TRUE) + } + data1[[i1]] <- final.data.frame + geom[[i1]] <- "geom_line" + if(length(color[[i1]])== 1L){ + color[[i1]] <- rep(color[[i1]], length(unique(data1[[i1]][ , categ[[i1]]]))) + }else if(length(color[[i1]]) != length(unique(data1[[i1]][ , categ[[i1]]]))){ + tempo.cat <- paste0("ERROR IN ", function.name, " geom_hline AND geom_vline CONVERSION TO FIT THE XLIM AND YLIM LIMITS OF THE DATA: ", ifelse(length(color)== 1L, "color", paste0("ELEMENT NUMBER ", i1, " OF color ARGUMENT")), " MUST HAVE THE LENGTH OF LEVELS OF ", ifelse(length(categ)== 1L, "categ", paste0("ELEMENT ", i1, " OF categ ARGUMENT")), " IN ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i1, " OF data1 ARGUMENT")), "\nHERE IT IS COLOR LENGTH ", length(color[[i1]]), " VERSUS CATEG LEVELS LENGTH ", length(unique(data1[[i1]][, categ[[i1]]]))) + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + } + } + # end conversion of geom_hline and geom_vline + + + + + # kind of geom_point (vectorial or raster) + scatter.kind <- vector("list", length = length(data1)) # list of same length as data1, that will be used to use either ggplot2::geom_point() (vectorial dot layer) or fun_gg_point_rast() (raster dot layer) + fix.ratio <- FALSE + if(is.null(raster.threshold)){ + if(raster == TRUE){ + scatter.kind[] <- "fun_gg_point_rast" # not important to fill everything: will be only used when geom == "geom_point" + fix.ratio <- TRUE + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") RASTER PLOT GENERATED -> ASPECT RATIO OF THE PLOT REGION SET BY THE raster.ratio ARGUMENT (", fun_round(raster.ratio, 2), ") TO AVOID A BUG OF ELLIPSOID DOT DRAWING") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + }else{ + scatter.kind[] <- "ggplot2::geom_point" + } + }else{ + for(i2 in 1:length(data1)){ + if(geom[[i2]] == "geom_point"){ + if(nrow(data1[[i2]]) <= raster.threshold){ + scatter.kind[[i2]] <- "ggplot2::geom_point" + }else{ + scatter.kind[[i2]] <- "fun_gg_point_rast" + fix.ratio <- TRUE + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") ", ifelse(length(data1)== 1L, "data1 ARGUMENT", paste0("DATA FRAME NUMBER ", i2, " OF data1 ARGUMENT")), " LAYER AS RASTER (NOT VECTORIAL)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + } + if(any(unlist(scatter.kind) == "fun_gg_point_rast")){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") RASTER PLOT GENERATED -> ASPECT RATIO OF THE PLOT REGION SET BY THE raster.ratio ARGUMENT (", fun_round(raster.ratio, 2), ") TO AVOID A BUG OF ELLIPSOID DOT DRAWING") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + } + # end kind of geom_point (vectorial or raster) + + + + + # no need loop part + coord.names <- NULL + tempo.gg.name <- "gg.indiv.plot." + tempo.gg.count <- 0 + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){x[[1]]}else{x.lab})) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y[[1]]}else{y.lab})) + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title)) + # text angle management + x.tempo.just <- fun_gg_just(angle = x.text.angle, pos = "bottom", kind = "axis") + y.tempo.just <- fun_gg_just(angle = y.text.angle, pos = "left", kind = "axis") + # end text angle management + add.check <- TRUE + if( ! is.null(add)){ # if add is NULL, then = 0 + if(grepl(pattern = "ggplot2::theme", add) == TRUE){ + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED\nIT IS RECOMMENDED TO USE \"+ theme(aspect.ratio = raster.ratio)\" IF RASTER MODE IS ACTIVATED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + add.check <- FALSE + } + } + if(add.check == TRUE & article == TRUE){ + # WARNING: not possible to add several times theme(). NO message but the last one overwrites the others + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size)) + if(grid == TRUE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + line = ggplot2::element_line(size = 0.5), + axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis + axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis + panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.minor.x = ggplot2::element_line(colour = "grey90", size = 0.25), + panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), + axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), + axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), + aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster + )) + }else{ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_line(size = 0.5), + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + axis.line.y.left = ggplot2::element_line(colour = "black"), + axis.line.x.bottom = ggplot2::element_line(colour = "black"), + axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), + axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), + aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster + )) + } + }else if(add.check == TRUE & article == FALSE){ + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme( + text = ggplot2::element_text(size = text.size), + plot.title = ggplot2::element_text(size = title.text.size), # stronger than text + line = ggplot2::element_line(size = 0.5), + legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend + panel.background = ggplot2::element_rect(fill = "grey95"), + axis.line.y.left = ggplot2::element_line(colour = "black"), + axis.line.x.bottom = ggplot2::element_line(colour = "black"), + panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.minor.x = ggplot2::element_line(colour = "grey90", size = 0.25), + panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), + panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), + strip.background = ggplot2::element_rect(fill = "white", colour = "black"), + axis.text.x = ggplot2::element_text(angle = x.tempo.just$angle, hjust = x.tempo.just$hjust, vjust = x.tempo.just$vjust), + axis.text.y = ggplot2::element_text(angle = y.tempo.just$angle, hjust = y.tempo.just$hjust, vjust = y.tempo.just$vjust), + aspect.ratio = if(fix.ratio == TRUE){raster.ratio}else{NULL} # for raster + # do not work -> legend.position = "none" # to remove the legend completely: https://www.datanovia.com/en/blog/how-to-remove-legend-from-a-ggplot/ + )) + } + # end no need loop part + + + # loop part + point.count <- 0 + line.count <- 0 + lg.order <- vector(mode = "list", length = 6) # order of the legend + lg.order <- lapply(lg.order, as.numeric) # order of the legend + lg.color <- vector(mode = "list", length = 6) # color of the legend + lg.dot.shape <- vector(mode = "list", length = 6) # etc. + lg.dot.size <- vector(mode = "list", length = 6) # etc. + lg.dot.size <- lapply(lg.dot.size, as.numeric) # etc. + lg.dot.border.size <- vector(mode = "list", length = 6) # etc. + lg.dot.border.size <- lapply(lg.dot.border.size, as.numeric) # etc. + lg.dot.border.color <- vector(mode = "list", length = 6) # etc. + lg.line.size <- vector(mode = "list", length = 6) # etc. + lg.line.size <- lapply(lg.line.size, as.numeric) # etc. + lg.line.type <- vector(mode = "list", length = 6) # etc. + lg.alpha <- vector(mode = "list", length = 6) # etc. + lg.alpha <- lapply(lg.alpha, as.numeric) # etc. + for(i1 in 1:length(data1)){ + if(geom[[i1]] == "geom_point"){ + point.count <- point.count + 1 + if(point.count== 1L){ + fin.lg.disp[[1]] <- legend.disp[[point.count + line.count]] + lg.order[[1]] <- point.count + line.count + lg.color[[1]] <- color[[i1]] # if color == NULL -> NULL + lg.dot.shape[[1]] <- dot.shape[[i1]] + lg.dot.size[[1]] <- dot.size[[i1]] + lg.dot.border.size[[1]] <- dot.border.size[[i1]] + lg.dot.border.color[[1]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL + if(plot == TRUE & fin.lg.disp[[1]] == TRUE & dot.shape[[1]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[1]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[1]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], fill = categ[[i1]]), shape = dot.shape[[i1]], size = dot.size[[i1]], stroke = dot.border.size[[i1]], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = if(i5== 1L){TRUE}else{FALSE})) # WARNING: a single color allowed for color argument outside aesthetic, but here a single color for border --> loop could be inactivated but kept for commodity # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_manual(name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = as.character(color[[i1]]), breaks = class.categ)) # values are the values of fill, breaks reorder the classes according to class.categ in the legend, order argument of guide_legend determines the order of the different aesthetics in the legend (not order of classes). See guide_legend settings of scale_..._manual below + } + if(point.count== 2L){ + fin.lg.disp[[2]] <- legend.disp[[point.count + line.count]] + lg.order[[2]] <- point.count + line.count + lg.color[[2]] <- color[[i1]] # if color == NULL -> NULL + lg.dot.shape[[2]] <- dot.shape[[i1]] + lg.dot.size[[2]] <- dot.size[[i1]] + lg.dot.border.size[[2]] <- dot.border.size[[i1]] + lg.dot.border.color[[2]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL + if(plot == TRUE & fin.lg.disp[[2]] == TRUE & dot.shape[[2]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[2]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[2]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], shape = categ[[i1]]), size = dot.size[[i1]], stroke = dot.border.size[[i1]], fill = color[[i1]][i5], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = FALSE)) # WARNING: a single color allowed for fill argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_shape_manual(name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(dot.shape[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of shape, breaks reorder the classes according to class.categ in the legend. See guide_legend settings of scale_..._manual below + + } + if(point.count== 3L){ + fin.lg.disp[[3]] <- legend.disp[[point.count + line.count]] + lg.order[[3]] <- point.count + line.count + lg.color[[3]] <- color[[i1]] # if color == NULL -> NULL + lg.dot.shape[[3]] <- dot.shape[[i1]] + lg.dot.size[[3]] <- dot.size[[i1]] + lg.dot.border.size[[3]] <- dot.border.size[[i1]] + lg.dot.border.color[[3]] <- dot.border.color[[i1]] # if dot.border.color == NULL -> NULL + if(plot == TRUE & fin.lg.disp[[3]] == TRUE & dot.shape[[3]] %in% 0:14 & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE DOTS (DOT LAYER NUMBER ", point.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[3]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[3]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = scatter.kind[[i1]]))(data = tempo.data.frame, mapping = ggplot2::aes_string(x = x[[i1]], y = y[[i1]], stroke = categ[[i1]]), shape = dot.shape[[i1]], size = dot.size[[i1]], fill = color[[i1]][i5], stroke = dot.border.size[[i1]], color = if(dot.shape[[i1]] %in% 21:24 & ! is.null(dot.border.color)){dot.border.color[[i1]]}else{color[[i1]][i5]}, alpha = alpha[[i1]], show.legend = FALSE)) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "stroke", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(dot.border.size[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of stroke, breaks reorder the classes according to class.categ in the legend. See guide_legend settings of scale_..._manual below + + } + }else{ + line.count <- line.count + 1 + if(line.count== 1L){ + fin.lg.disp[[4]] <- legend.disp[[point.count + line.count]] + lg.order[[4]] <- point.count + line.count + lg.color[[4]] <- color[[i1]] # if color == NULL -> NULL + lg.line.size[[4]] <- line.size[[i1]] + lg.line.type[[4]] <- line.type[[i1]] + if(plot == TRUE & fin.lg.disp[[4]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[4]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[4]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl + ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting + "(data = tempo.data.frame, mapping = ggplot2::aes(x = ", + x[[i1]], + ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), + y[[i1]], + if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, + ", linetype = ", + categ[[i1]], + "), color = \"", + color[[i1]][i5], + "\", size = ", + line.size[[i1]], + ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), + ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), + ", alpha = ", + alpha[[i1]], + ", show.legend = ", + ifelse(i5== 1L, TRUE, FALSE), + ")" + )))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(line.type[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, with different values, breaks reorder the classes according to class.categ in the legend + } + if(line.count== 2L){ + fin.lg.disp[[5]] <- legend.disp[[point.count + line.count]] + lg.order[[5]] <- point.count + line.count + lg.color[[5]] <- color[[i1]] # if color == NULL -> NULL + lg.line.size[[5]] <- line.size[[i1]] + lg.line.type[[5]] <- line.type[[i1]] + if(plot == TRUE & fin.lg.disp[[5]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[5]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[5]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl + ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting + "(data = tempo.data.frame, mapping = ggplot2::aes(x = ", + x[[i1]], + ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), + y[[i1]], + if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, + ", alpha = ", + categ[[i1]], + "), color = \"", + color[[i1]][i5], + "\", size = ", + line.size[[i1]], + ", linetype = ", + ifelse(is.numeric(line.type[[i1]]), "", "\""), + line.type[[i1]], + ifelse(is.numeric(line.type[[i1]]), "", "\""), + ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), + ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), + ", show.legend = FALSE)" + )))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(alpha[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, with different values, breaks reorder the classes according to class.categ in the legend + } + if(line.count== 3L){ + fin.lg.disp[[6]] <- legend.disp[[point.count + line.count]] + lg.order[[6]] <- point.count + line.count + lg.color[[6]] <- color[[i1]] # if color == NULL -> NULL + lg.line.size[[6]] <- line.size[[i1]] + lg.line.type[[6]] <- line.type[[i1]] + if(plot == TRUE & fin.lg.disp[[6]] == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list())== 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1 + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES (LINE LAYER NUMBER ", line.count, ") IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + lg.alpha[[6]] <- 1 # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf + }else{ + lg.alpha[[6]] <- alpha[[i1]] + } + class.categ <- levels(factor(data1[[i1]][, categ[[i1]]])) + for(i5 in 1:length(color[[i1]])){ # or length(class.categ). It is the same because already checked that lengths are the same + tempo.data.frame <- data1[[i1]][data1[[i1]][, categ[[i1]]] == class.categ[i5], ] + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::", # no CR here te0("ggpl + ifelse(geom[[i1]] == 'geom_stick', 'geom_segment', geom[[i1]]), # geom_segment because geom_stick converted to geom_segment for plotting + "(data = tempo.data.frame, mapping = ggplot2::aes(x = ", + x[[i1]], + ifelse(geom[[i1]] == 'geom_stick', ", yend = ", ", y = "), + y[[i1]], + if(geom[[i1]] == 'geom_stick'){paste0(', xend = ', x[[i1]], ', y = ', ifelse(is.null(geom.stick.base), y.lim[1], geom.stick.base[[i1]]))}, + ", size = ", + categ[[i1]], + "), color = \"", + color[[i1]][i5], + "\", linetype = ", + ifelse(is.numeric(line.type[[i1]]), "", "\""), + line.type[[i1]], + ifelse(is.numeric(line.type[[i1]]), "", "\""), + ifelse(geom[[i1]] == 'geom_path', ', lineend = \"round\"', ''), + ifelse(geom[[i1]] == 'geom_step', paste0(', direction = \"', geom.step.dir[[i1]], '\"'), ''), + ", alpha = ", + alpha[[i1]], + ", show.legend = FALSE)" + )))) # WARNING: a single color allowed for color argument outside aesthetic, hence the loop # legend.show option do not remove the legend, only the aesthetic of the legend (dot, line, etc.). Used here to avoid multiple layers of legend which corrupt transparency + coord.names <- c(coord.names, paste0(geom[[i1]], ".", class.categ[i5])) + } + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "size", name = if(is.null(legend.name)){NULL}else{legend.name[[i1]]}, values = rep(line.size[[i1]], length(color[[i1]])), breaks = class.categ)) # values are the values of linetype. 1 means solid. Regarding the alpha bug, I have tried different things without success: alpha in guide alone, in geom alone, in both, breaks reorder the classes according to class.categ in the legend + } + } + } + # end loop part + + + + + # legend display + tempo.legend.final <- 'ggplot2::guides( fill = if(fin.lg.disp[[1]] == TRUE){ ggplot2::guide_legend( order = lg.order[[1]], @@ -13495,25 +13501,25 @@ shape = NA } )' # clip = "off" to have secondary ticks outside plot region does not work if( ! is.null(legend.width)){ -if(any(unlist(legend.disp))){ # means some TRUE -tempo.graph.info <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ', tempo.legend.final))))) # will be recovered later again, when ylim will be considered -legend.final <- fun_gg_get_legend(ggplot_built = tempo.graph.info, fun.name = function.name) # get legend -fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend -if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE -legend.final <- fun_gg_empty_graph() # empty graph instead of legend -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} -}else if(plot == TRUE){ # means all FALSE -legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) -} + if(any(unlist(legend.disp))){ # means some TRUE + tempo.graph.info <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ', tempo.legend.final))))) # will be recovered later again, when ylim will be considered + legend.final <- fun_gg_get_legend(ggplot_built = tempo.graph.info, fun.name = function.name) # get legend + fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend + if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE + legend.final <- fun_gg_empty_graph() # empty graph instead of legend + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } + }else if(plot == TRUE){ # means all FALSE + legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + } } if( ! any(unlist(legend.disp))){ -fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend + fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend } assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = tempo.legend.final))) # end legend display @@ -13526,135 +13532,136 @@ assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(t tempo.coord <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ", ' + ggplot2::scale_x_continuous(expand = c(0, 0), limits = sort(x.lim), oob = scales::rescale_none) + ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)'))))$layout$panel_params[[1]]) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks # x.second.tick.positions # coordinates of secondary ticks (only if x.second.tick.nb argument is non-null or if x.log argument is different from "no") if(x.log != "no"){ # integer main ticks for log2 and log10 -tempo.scale <- (as.integer(min(x.lim, na.rm = TRUE)) - 1):(as.integer(max(x.lim, na.rm = TRUE)) + 1) -}else{ -tempo <- if(is.null(attributes(tempo.coord$x$breaks))){tempo.coord$x$breaks}else{unlist(attributes(tempo.coord$x$breaks))} -if(all(is.na(tempo))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$x$breaks") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(length(unique(x.lim)) <= 1){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT X-AXIS VALUES HAVE A NULL RANGE: ", paste(x.lim, collapse = " "), "\nPLEASE, USE THE x.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -tempo.scale <- fun_scale(lim = x.lim, n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) # in ggplot 3.3.0, tempo.coord$x.major_source replaced by tempo.coord$x$breaks. If fact: n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) replaced by n = ifelse(is.null(x.tick.nb), 4, x.tick.nb)) -} + tempo.scale <- (as.integer(min(x.lim, na.rm = TRUE)) - 1):(as.integer(max(x.lim, na.rm = TRUE)) + 1) +}else{ + tempo <- if(is.null(attributes(tempo.coord$x$breaks))){tempo.coord$x$breaks}else{unlist(attributes(tempo.coord$x$breaks))} + if(all(is.na(tempo))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$x$breaks") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(length(unique(x.lim)) <= 1){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT X-AXIS VALUES HAVE A NULL RANGE: ", paste(x.lim, collapse = " "), "\nPLEASE, USE THE x.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + tempo.scale <- fun_scale(lim = x.lim, n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) # in ggplot 3.3.0, tempo.coord$x.major_source replaced by tempo.coord$x$breaks. If fact: n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) replaced by n = ifelse(is.null(x.tick.nb), 4, x.tick.nb)) + } } x.second.tick.values <- NULL x.second.tick.pos <- NULL if(x.log != "no"){ -tempo <- fun_inter_ticks(lim = x.lim, log = x.log) -x.second.tick.values <- tempo$values -x.second.tick.pos <- tempo$coordinates -# if(vertical == TRUE){ # do not remove in case the bug is fixed -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( -geom = "segment", x = x.second.tick.pos, -xend = x.second.tick.pos, -y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, -yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80} -)) -# }else{ # not working because of the ggplot2 bug -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = x.second.tick.pos, yend = x.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80)) -# } -coord.names <- c(coord.names, "x.second.tick.positions") + tempo <- fun_inter_ticks(lim = x.lim, log = x.log) + x.second.tick.values <- tempo$values + x.second.tick.pos <- tempo$coordinates + # if(vertical == TRUE){ # do not remove in case the bug is fixed + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( + geom = "segment", + x = x.second.tick.pos, + xend = x.second.tick.pos, + y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, + yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80} + )) + # }else{ # not working because of the ggplot2 bug + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = x.second.tick.pos, yend = x.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80)) + # } + coord.names <- c(coord.names, "x.second.tick.positions") }else if(( ! is.null(x.second.tick.nb)) & x.log == "no"){ -# if(x.second.tick.nb > 0){ #inactivated because already checked before -if(length(tempo.scale) < 2){ -tempo.cat1 <- c("x.tick.nb", "x.second.tick.nb") -tempo.cat2 <- sapply(list(x.tick.nb, x.second.tick.nb), FUN = paste0, collapse = " ") -tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE X-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -tempo <- fun_inter_ticks(lim = x.lim, log = x.log, breaks = tempo.scale, n = x.second.tick.nb) -} -x.second.tick.values <- tempo$values -x.second.tick.pos <- tempo$coordinates -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( -geom = "segment", -x = x.second.tick.pos, -xend = x.second.tick.pos, -y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, -yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80} -)) -coord.names <- c(coord.names, "x.second.tick.positions") + # if(x.second.tick.nb > 0){ #inactivated because already checked before + if(length(tempo.scale) < 2){ + tempo.cat1 <- c("x.tick.nb", "x.second.tick.nb") + tempo.cat2 <- sapply(list(x.tick.nb, x.second.tick.nb), FUN = paste0, collapse = " ") + tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE X-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + tempo <- fun_inter_ticks(lim = x.lim, log = x.log, breaks = tempo.scale, n = x.second.tick.nb) + } + x.second.tick.values <- tempo$values + x.second.tick.pos <- tempo$coordinates + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( + geom = "segment", + x = x.second.tick.pos, + xend = x.second.tick.pos, + y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, + yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80} + )) + coord.names <- c(coord.names, "x.second.tick.positions") } # for the ggplot2 bug with x.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & x.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_x_continuous"))) assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_x_continuous( -breaks = tempo.scale, -minor_breaks = x.second.tick.pos, -labels = if(x.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(x.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(x.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, -expand = c(0, 0), # remove space after after axis limits -limits = sort(x.lim), # NA indicate that limits must correspond to data limits but xlim() already used -oob = scales::rescale_none, -trans = ifelse(diff(x.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_x_reverse() but create the problem of x-axis label disappearance with x.lim decreasing. Thus, do not use. Use xlim() below and after this + breaks = tempo.scale, + minor_breaks = x.second.tick.pos, + labels = if(x.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(x.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(x.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, + expand = c(0, 0), # remove space after after axis limits + limits = sort(x.lim), # NA indicate that limits must correspond to data limits but xlim() already used + oob = scales::rescale_none, + trans = ifelse(diff(x.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_x_reverse() but create the problem of x-axis label disappearance with x.lim decreasing. Thus, do not use. Use xlim() below and after this )) # end x.second.tick.positions # y.second.tick.positions # coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no") if(y.log != "no"){ # integer main ticks for log2 and log10 -tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1) -}else{ -tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))} -if(all(is.na(tempo))){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -} -if(length(unique(y.lim)) <= 1){ -tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT Y-AXIS VALUES HAVE A NULL RANGE: ", paste(y.lim, collapse = " "), "\nPLEASE, USE THE y.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb)) -} + tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1) +}else{ + tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))} + if(all(is.na(tempo))){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + } + if(length(unique(y.lim)) <= 1){ + tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT Y-AXIS VALUES HAVE A NULL RANGE: ", paste(y.lim, collapse = " "), "\nPLEASE, USE THE y.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb)) + } } y.second.tick.values <- NULL y.second.tick.pos <- NULL if(y.log != "no"){ -tempo <- fun_inter_ticks(lim = y.lim, log = y.log) -y.second.tick.values <- tempo$values -y.second.tick.pos <- tempo$coordinates -# if(vertical == TRUE){ # do not remove in case the bug is fixed -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( -geom = "segment", -y = y.second.tick.pos, -yend = y.second.tick.pos, -x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, -xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80} -)) -# }else{ # not working because of the ggplot2 bug -# assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80)) -# } -coord.names <- c(coord.names, "y.second.tick.positions") + tempo <- fun_inter_ticks(lim = y.lim, log = y.log) + y.second.tick.values <- tempo$values + y.second.tick.pos <- tempo$coordinates + # if(vertical == TRUE){ # do not remove in case the bug is fixed + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( + geom = "segment", + y = y.second.tick.pos, + yend = y.second.tick.pos, + x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, + xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80} + )) + # }else{ # not working because of the ggplot2 bug + # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80)) + # } + coord.names <- c(coord.names, "y.second.tick.positions") }else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){ -# if(y.second.tick.nb > 0){ #inactivated because already checked before -if(length(tempo.scale) < 2){ -tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb") -tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ") -tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") -tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == -}else{ -tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb) -} -y.second.tick.values <- tempo$values -y.second.tick.pos <- tempo$coordinates -assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( -geom = "segment", -y = y.second.tick.pos, -yend = y.second.tick.pos, -x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, -xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80} -)) -coord.names <- c(coord.names, "y.second.tick.positions") + # if(y.second.tick.nb > 0){ #inactivated because already checked before + if(length(tempo.scale) < 2){ + tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb") + tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ") + tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "") + tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between == + }else{ + tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb) + } + y.second.tick.values <- tempo$values + y.second.tick.pos <- tempo$coordinates + assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate( + geom = "segment", + y = y.second.tick.pos, + yend = y.second.tick.pos, + x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, + xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80} + )) + coord.names <- c(coord.names, "y.second.tick.positions") } # for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous"))) assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous( -breaks = tempo.scale, -minor_breaks = y.second.tick.pos, -labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, -expand = c(0, 0), # remove space after axis limits -limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used -oob = scales::rescale_none, -trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this + breaks = tempo.scale, + minor_breaks = y.second.tick.pos, + labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, + expand = c(0, 0), # remove space after axis limits + limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used + oob = scales::rescale_none, + trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this )) # end y.second.tick.positions assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(xlim = x.lim, ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region. The problem is that points out of bounds are also drawn outside the plot region. Thus, I cannot use it # at that stage, x.lim and y.lim not NULL anymore @@ -13667,15 +13674,15 @@ assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coo fin.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))) grob.save <- NULL if(plot == TRUE){ -if( ! is.null(legend.width)){ # any(unlist(legend.disp)) == TRUE removed to have empty legend space # not & any(unlist(fin.lg.disp)) == TRUE here because converted to FALSE -grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width)))) -}else{ -grob.save <- suppressMessages(suppressWarnings(print(fin.plot))) -} + if( ! is.null(legend.width)){ # any(unlist(legend.disp)) == TRUE removed to have empty legend space # not & any(unlist(fin.lg.disp)) == TRUE here because converted to FALSE + grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width)))) + }else{ + grob.save <- suppressMessages(suppressWarnings(print(fin.plot))) + } }else{ -warn.count <- warn.count + 1 -tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") -warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) + warn.count <- warn.count + 1 + tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED") + warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn))) } # end drawing @@ -13683,49 +13690,49 @@ warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn # output if(warn.print == TRUE & ! is.null(warn)){ -on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) + on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE)) } on.exit(exp = options(warning.length = ini.warning.length), add = TRUE) if(return == TRUE){ -output <- suppressMessages(ggplot2::ggplot_build(fin.plot)) -# output$data <- output$data[-1] # yes for boxplot but not for scatter # remove the first data because corresponds to the initial empty boxplot -if(length(output$data) != length(coord.names)){ -tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": length(output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") -stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) -}else{ -names(output$data) <- coord.names -} -if(is.null(unlist(removed.row.nb))){ -removed.row.nb <- NULL -removed.rows <- NULL -}else{ -for(i3 in 1:length(data1)){ -if( ! is.null(removed.row.nb[[i3]])){ -removed.row.nb[[i3]] <- sort(removed.row.nb[[i3]]) -removed.rows[[i3]] <- data1.ini[[i3]][removed.row.nb[[i3]], ] -} -} -} -tempo <- output$layout$panel_params[[1]] -output <- list( -data = data1, -removed.row.nb = removed.row.nb, -removed.rows = removed.rows, -plot = c(output$data, x.second.tick.values = list(x.second.tick.values), y.second.tick.values = list(y.second.tick.values)), -panel = facet.categ, -axes = list( -x.range = tempo$x.range, -x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE) -x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, -y.range = tempo$y.range, -y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, -y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))} -), -warn = paste0("\n", warn, "\n\n"), -ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE -gtable = if(return.gtable == TRUE){grob.save}else{NULL} # -) -return(output) # this plots the graph if return.ggplot is TRUE and if no assignment + output <- suppressMessages(ggplot2::ggplot_build(fin.plot)) + # output$data <- output$data[-1] # yes for boxplot but not for scatter # remove the first data because corresponds to the initial empty boxplot + if(length(output$data) != length(coord.names)){ + tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": length(output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED") + stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) + }else{ + names(output$data) <- coord.names + } + if(is.null(unlist(removed.row.nb))){ + removed.row.nb <- NULL + removed.rows <- NULL + }else{ + for(i3 in 1:length(data1)){ + if( ! is.null(removed.row.nb[[i3]])){ + removed.row.nb[[i3]] <- sort(removed.row.nb[[i3]]) + removed.rows[[i3]] <- data1.ini[[i3]][removed.row.nb[[i3]], ] + } + } + } + tempo <- output$layout$panel_params[[1]] + output <- list( + data = data1, + removed.row.nb = removed.row.nb, + removed.rows = removed.rows, + plot = c(output$data, x.second.tick.values = list(x.second.tick.values), y.second.tick.values = list(y.second.tick.values)), + panel = facet.categ, + axes = list( + x.range = tempo$x.range, + x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE) + x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, + y.range = tempo$y.range, + y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, + y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))} + ), + warn = paste0("\n", warn, "\n\n"), + ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE + gtable = if(return.gtable == TRUE){grob.save}else{NULL} # + ) + return(output) # this plots the graph if return.ggplot is TRUE and if no assignment } # end output # end main code @@ -13733,3 +13740,7 @@ return(output) # this plots the graph if return.ggplot is TRUE and if no assignm + + + + diff --git a/cute_little_R_functions.docx b/cute_little_R_functions.docx index e0905868b371d07c5e89ecf67192f21afbdaf85e..a42532236df2c47feaf0ecd05f13141efb27063b 100755 GIT binary patch delta 426086 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úW%T~+xMUq9`o(%tsD&^)|<0s%%8OJ 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�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	La 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 delta 425738 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	VC$C(#NoGopvh(p5j@t~XCe2-QMTs+X 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 TTQVn4XPVEh#5Q3XDBA%5LNk_J diff --git a/fun_gg_boxplot.docx b/fun_gg_boxplot.docx index 275bf32f0d8b4eac93608dc15dca16a2b8625644..3cab248e103f7784c258232ae7f8ca5dca6167cc 100755 GIT binary patch delta 102999 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 delta 102881 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�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 ViZV^yJDvR~qY~TJ1B?s|3;?h3)Z+jE diff --git a/fun_gg_scatter.docx b/fun_gg_scatter.docx index f3605cccde00d915611f7fefbaadb229383748ad..5e322b4aceb287f5acf07e405e8826e32125aa73 100755 GIT binary patch delta 49155 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 delta 49121 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 CA;vra -- GitLab