diff --git a/DESCRIPTION b/DESCRIPTION index f2abcb4..6c5c989 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: BoutrosLab.plotting.general -Version: 7.0.10 +Version: 7.1.0 Type: Package Title: Functions to Create Publication-Quality Plots -Date: 2023-10-09 +Date: 2023-10-26 Authors@R: c(person("Paul Boutros", role = c("aut", "cre"), email = "PBoutros@mednet.ucla.edu"), person("Christine P'ng", role = "ctb"), person("Jeff Green", role = "ctb"), @@ -15,14 +15,16 @@ Authors@R: c(person("Paul Boutros", role = c("aut", "cre"), email = "PBoutros@me person("Caden Bugh", role = "ctb"), person("Dan Knight", role = "ctb"), person("Stefan Eng", role = "ctb"), - person("Mohammed Faizal Eeman Mootor", role = "ctb")) + person("Mohammed Faizal Eeman Mootor", role = "ctb"), + person("Rachel Dang", role = "ctb"), + person("John Sahrmann", role = "ctb")) Maintainer: Paul Boutros Depends: R (>= 3.5.0), lattice (>= 0.20-35), latticeExtra (>= 0.6-27), cluster (>= 2.0.0), hexbin (>= 1.27.0), grid Imports: gridExtra, tools, methods, gtable, e1071, MASS(>= 7.3-29) -Suggests: +Suggests: Cairo (>= 1.5-1), knitr, - testthat + testthat (>= 3.0.0) Description: Contains several plotting functions such as barplots, scatterplots, heatmaps, as well as functions to combine plots and assist in the creation of these plots. These functions will give users great ease of use and customization options in broad use for biomedical applications, as well as general purpose plotting. Each of the functions also provides valid default settings to make plotting data more efficient and producing high quality plots with standard colour schemes simpler. All functions within this package are capable of producing plots that are of the quality to be presented in scientific publications and journals. P'ng et al.; BPG: Seamless, automated and interactive visualization of scientific data; BMC Bioinformatics 2019 . License: GPL-2 URL: /~https://github.com/uclahs-cds/package-BoutrosLab-plotting-general @@ -30,3 +32,4 @@ BugReports: /~https://github.com/uclahs-cds/package-BoutrosLab-plotting-general/is LazyLoad: yes LazyData: yes VignetteBuilder: knitr +Config/testthat/edition: 3 diff --git a/NEWS b/NEWS index 55fd511..983bb89 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,15 @@ +BoutrosLab.plotting.general 7.1.0 2023-10-26 + +ADDED +* Add testthat framework for package tests +* Use mockr for unit testing +* Improve documentation for automatic axis scaling in: + - create.scatterplot + - create.boxplot + - create.manhattanplot + - create.hexbinplot + +-------------------------------------------------------------------------- BoutrosLab.plotting.general 7.0.10 2023-10-09 UPDATE diff --git a/R/auto.axis.R b/R/auto.axis.R index b0b3e4e..6619c7a 100755 --- a/R/auto.axis.R +++ b/R/auto.axis.R @@ -1,215 +1,223 @@ -#SMC.Benchmarking package is copxright (c) 2014 Ontario Institute for Cancer Research (OICR) -# This package and its accompanxing libraries is free software; xou can redistribute it and/or modifx it under the terms of the GPL -# (either version 1, or at xour option, anx later version) or the Artistic License 2.0. Refer to LICENSE for the full license text. +# The BoutrosLab.plotting.general package is copyright (c) 2014 Ontario Institute for Cancer Research (OICR) +# This package and its accompanying libraries is free software; you can redistribute it and/or modify it under the terms of the GPL +# (either version 1, or at your option, any later version) or the Artistic License 2.0. Refer to LICENSE for the full license text. # OICR makes no representations whatsoever as to the SOFTWARE contained herein. It is experimental in nature and is provided WITHOUT -# WARRANTx OF MERCHANTABILITx OR FITNESS FOR A PARTICULAR PURPOSE OR ANx OTHER WARRANTx, EXPRESS OR IMPLIED. OICR MAKES NO REPRESENTATION -# OR WARRANTx THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANx PATENT OR OTHER PROPRIETARx RIGHT. -# Bx downloading this SOFTWARE, xour Institution herebx indemnifies OICR against anx loss, claim, damage or liabilitx, of whatsoever kind or -# nature, which max arise from xour Institution's respective use, handling or storage of the SOFTWARE. +# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE OR ANY OTHER WARRANTY, EXPRESS OR IMPLIED. OICR MAKES NO REPRESENTATION +# OR WARRANTY THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANY PATENT OR OTHER PROPRIETARY RIGHT. +# Bx downloading this SOFTWARE, your Institution hereby indemnifies OICR against any loss, claim, damage or liability, of whatsoever kind or +# nature, which max arise from your Institution's respective use, handling or storage of the SOFTWARE. # If publications result from research using this SOFTWARE, we ask that the Ontario Institute for Cancer Research be acknowledged and/or -# credit be given to OICR scientists, as scientificallx appropriate. - -auto.axis <- function(x, pretty = TRUE, log.scaled = NA, log.zero = 0.1, max.factor = 1, - min.factor = 1, include.origin = TRUE, num.labels = 5, max.min.log10.diff = 2) { - - out <- list(); - - x <- as.numeric(x); - - #get max and min to plot - max.x <- max(x, na.rm = TRUE) * max.factor; - min.x <- min(x, na.rm = TRUE) * min.factor; - - #make sure x is all > 0 to consider log scale - if (all(x > 0, na.rm = TRUE)) { - - #determine scale based on sckewness - skewness.x <- skewness(x, na.rm = TRUE); - # handle zero values so that thex can still be plotted even log(0) = -Inf - zero.i <- which(0 == x); - logx <- log(x, 10); - - - if (length(zero.i) > 0) { - # use a proxx for log(0) - logx[zero.i] <- log.zero; - } - - if (max.x - min.x != 0) { - - cond1 <- log10(max.x - min.x) > max.min.log10.diff; #mamin.x.log10.diff scale should be more than 2 - - } - else { - - cond1 <- FALSE; # vector x onlx contains 0 - - } - - cond2 <- (skewness.x > skewness(logx, na.rm = TRUE)); #skewness.x should be larger than skewness.log10(x) - - } - else { - - cond1 <- FALSE; - cond2 <- FALSE; - - } - - #decide log scale or not based on cond1 and cond2 - if (cond1 && cond2) { - - out$log.scaled <- TRUE; - if (!is.na(log.scaled) && !log.scaled) { - - out$log.scaled <- FALSE; - - } - - } else { - - out$log.scaled <- FALSE; - - #force log scale - if (!is.na(log.scaled) && log.scaled) { - - out$log.scaled <- TRUE; - - if (!all(x > 0, na.rm = TRUE)) { - stop('can not use log-scale as the input vector contains negative values.'); - } - } - } - - message('log.scaled', out$log.scaled); - - if (out$log.scaled) { - - out$x <- logx; - min.x <- min(out$x, na.rm = TRUE); - max.x <- max(out$x, na.rm = TRUE); - - out$at <- generate.at(min.x, max.x, pretty, include.origin, num.labels); - # set axis labels - out$axis.lab <- sapply( - out$at[-1], #remove 1 - FUN = function(x) { - substitute(bold('10' ^ a), list(a = as.character(x))); - } - ); - - out$axis.lab <- c(expression(bold('0')), out$axis.lab); - - } else { - # for variables that are continuous and will not be plotted on a log-scale - # set axis increments - out$x <- x; - - out$at <- generate.at(min.x, max.x, pretty, include.origin, num.labels); - - # set axis labels - if (abs(mean(x, na.rm = TRUE)) > 1000 || abs(mean(x, na.rm = TRUE)) < 0.001 ) { - - out$axis.lab <- as.power10.expression(out$at); - - } - else { - - out$axis.lab <- out$at; - - } - } - - return(out); - } +# credit be given to OICR scientists, as scientifically appropriate. + +prep.axis <- function( + at, + data, + which.arg + ) { + + if (is.null(at)) { + return(at); + } + else if (is.logical(at)) { + return(at); + } + else if (length(at) > 1) { + return(at); + } + + out <- list(); + if ('auto' == at) { + out <- auto.axis( + x = data + ); + } + else if ('auto.linear' == at) { + out <- auto.axis( + x = data, + log.scaled = FALSE + ); + } + else if ('auto.log' == at) { + out <- auto.axis( + x = data, + log.scaled = TRUE + ); + } + else { + stop(paste0('Invalid input to ', which.arg, ': ', at)); + } + return(out); + + } + +auto.axis <- function( + x, + pretty = TRUE, + log.scaled = NA, + log.zero = 0.1, + max.factor = 1, + min.factor = 1, + include.origin = TRUE, + num.labels = 5, + max.min.log10.diff = 2 + ) { + + out <- list(); + + x <- as.numeric(x); + + # Get max and min to plot + max.x <- max(x, na.rm = TRUE) * max.factor; + min.x <- min(x, na.rm = TRUE) * min.factor; + + # Make sure x is all > 0 to consider log scale + if (all(x > 0, na.rm = TRUE)) { + # Determine scale based on skewness + skewness.x <- skewness(x, na.rm = TRUE); + # Handle zero values so that they can still be plotted even log(0) = -Inf + zero.i <- which(0 == x); + logx <- log(x, 10); + + if (length(zero.i) > 0) { + # use a proxy for log(0) + logx[zero.i] <- log.zero; + } + + if (max.x - min.x != 0) { + # x.log10.diff scale should be more than 2 + cond1 <- log10(max.x - min.x) > max.min.log10.diff; + + } + else { + cond1 <- FALSE; # vector x only contains 0 + } + + #skewness.x should be larger than skewness.log10(x) + cond2 <- (skewness.x > skewness(logx, na.rm = TRUE)); + } + else { + cond1 <- FALSE; + cond2 <- FALSE; + } + + # Decide log scale or not based on cond1 and cond2 + if (cond1 && cond2) { + out$log.scaled <- TRUE; + + if (!is.na(log.scaled) && !log.scaled) { + out$log.scaled <- FALSE; + } + } + else { + out$log.scaled <- FALSE; + + # Force log scale + if (!is.na(log.scaled) && log.scaled) { + out$log.scaled <- TRUE; + + if (!all(x > 0, na.rm = TRUE)) { + stop('can not use log-scale as the input vector contains negative values.'); + } + } + } + + if (out$log.scaled) { + out$x <- logx; + min.x <- min(out$x, na.rm = TRUE); + max.x <- max(out$x, na.rm = TRUE); + + out$at <- generate.at(min.x, max.x, pretty, include.origin, num.labels); + + # set axis labels + out$axis.lab <- sapply( + out$at[-1], # Remove 1 + FUN = function(x) { + substitute(bold('10' ^ a), list(a = as.character(x))); + } + ); + + out$axis.lab <- c(expression(bold('0')), out$axis.lab); + } + else { + # For variables that are continuous and will not be plotted on a log-scale + # Set axis increments + out$x <- x; + + out$at <- generate.at(min.x, max.x, pretty, include.origin, num.labels); + + # Set axis labels + if (abs(mean(x, na.rm = TRUE)) > 1000 || abs(mean(x, na.rm = TRUE)) < 0.001 ) { + out$axis.lab <- as.power10.expression(out$at); + } + else { + out$axis.lab <- out$at; + } + } + + return(out); + } generate.at <- function(min.x, max.x, pretty = TRUE, include.origin = TRUE, num.labels = 4) { - out <- c(); - - if (pretty) { - - if (max.x * min.x <= 0 || include.origin == FALSE) { - - out <- pretty(c(min.x, max.x), n = num.labels - 1); - - } - else { - - if (min.x > 0 && include.origin == TRUE) { - - out <- pretty(c(0, max.x), n = num.labels - 1); - - } - - if (max.x < 0 && include.origin == TRUE) { - - out <- pretty(c(min.x, 0), n = num.labels - 1); - - } - } - } - - else { - - if (max.x * min.x <= 0 || include.origin == FALSE) { - - out <- seq(min.x, max.x, length.out = num.labels); - - } - else { - - if (min.x > 0 && include.origin == TRUE) { - - out <- seq(0, max.x, length.out = num.labels); - - } - - if (max.x < 0 && include.origin == TRUE) { - - out <- seq(min.x, 0, length.out = num.labels); - - } - } - } - - return(out); - - } + out <- c(); + + if (pretty) { + if (max.x * min.x <= 0 || include.origin == FALSE) { + out <- pretty(c(min.x, max.x), n = num.labels - 1); + } + else { + if (min.x > 0 && include.origin == TRUE) { + out <- pretty(c(0, max.x), n = num.labels - 1); + } + if (max.x < 0 && include.origin == TRUE) { + out <- pretty(c(min.x, 0), n = num.labels - 1); + } + } + } + else { + if (max.x * min.x <= 0 || include.origin == FALSE) { + out <- seq(min.x, max.x, length.out = num.labels); + } + else { + if (min.x > 0 && include.origin == TRUE) { + out <- seq(0, max.x, length.out = num.labels); + } + if (max.x < 0 && include.origin == TRUE) { + out <- seq(min.x, 0, length.out = num.labels); + } + } + } + + return(out); + } as.power10.expression <- function(x) { - x <- unlist(x); - - out <- sapply(x, function(y) { - - y <- as.numeric(y); - # no need to do anything if x = 0 - if (0 == y) { - return(expression(bold('0'))); - } - - #otherwiese, convert x to power of 10 and split by e - y <- as.numeric(unlist(strsplit(sprintf('%e', y), split = 'e'))); - - #if x[1] = 1, then a should be omitted. - if (1 != y[1]) { - - y <- substitute(bold(a %*% '10' ^ b), list(a = as.character(y[1]), b = as.character(y[2]))); - - } - else { - - y <- substitute(bold('10' ^ b), list(b = as.character(y[2]))); - - } - - #retrun as expression otherwise list - as.expression(y); - - }); - - #return as expression - return(out); - } + x <- unlist(x); + + out <- sapply(x, function(y) { + y <- as.numeric(y); + # No need to do anything if x = 0 + if (0 == y) { + return(expression(bold('0'))); + } + + # Otherwise, convert x to power of 10 and split by e + y <- as.numeric(unlist(strsplit(sprintf('%e', y), split = 'e'))); + + # If x[1] = 1, then a should be omitted. + if (1 != y[1]) { + y <- substitute(bold(a %*% '10' ^ b), list(a = as.character(y[1]), b = as.character(y[2]))); + } + else { + y <- substitute(bold('10' ^ b), list(b = as.character(y[2]))); + } + + # Return as expression otherwise list + as.expression(y); + }); + + # Return as expression + return(out); + } diff --git a/R/create.boxplot.R b/R/create.boxplot.R index f2ba55e..90d4b17 100644 --- a/R/create.boxplot.R +++ b/R/create.boxplot.R @@ -62,50 +62,28 @@ create.boxplot <- function( fontface = text.fontface ); - if (!is.null(yat) && length(yat) == 1) { - if (yat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[2]])])); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = FALSE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = TRUE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - } + out <- prep.axis( + at = xat, + data = unlist(data[toString(formula[[3]])]), + which.arg = 'xat' + ); + if (is.list(out)) { + data[toString(formula[[3]])] <- out$x; + xat <- out$at; + xaxis.lab <- out$axis.lab; + } + + out <- prep.axis( + at = yat, + data = unlist(data[toString(formula[[2]])]), + which.arg = 'yat' + ); + if (is.list(out)) { + data[toString(formula[[2]])] <- out$x; + yat <- out$at; + yaxis.lab <- out$axis.lab; + } - if (!is.null(xat) && length(xat) == 1) { - if (xat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[3]])])); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = FALSE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = TRUE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - } - # add preloaded defaults if (preload.default == 'paper') { } diff --git a/R/create.hexbinplot.R b/R/create.hexbinplot.R index 4de0b45..5ab88c5 100644 --- a/R/create.hexbinplot.R +++ b/R/create.hexbinplot.R @@ -65,49 +65,27 @@ create.hexbinplot <- function( fontface = text.fontface ); - if (!is.null(yat) && length(yat) == 1) { - if (yat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[2]])])); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = FALSE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = TRUE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - } - - if (!is.null(xat) && length(xat) == 1) { - if (xat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[3]])])); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = FALSE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = TRUE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - } + out <- prep.axis( + at = xat, + data = unlist(data[toString(formula[[3]])]), + which.arg = 'xat' + ); + if (is.list(out)) { + data[toString(formula[[3]])] <- out$x; + xat <- out$at; + xaxis.lab <- out$axis.lab; + } + + out <- prep.axis( + at = yat, + data = unlist(data[toString(formula[[2]])]), + which.arg = 'yat' + ); + if (is.list(out)) { + data[toString(formula[[2]])] <- out$x; + yat <- out$at; + yaxis.lab <- out$axis.lab; + } # check class of conditioning variable if ('|' %in% all.names(formula)) { diff --git a/R/create.manhattanplot.R b/R/create.manhattanplot.R index 7a8b901..6b1d6bb 100755 --- a/R/create.manhattanplot.R +++ b/R/create.manhattanplot.R @@ -49,48 +49,28 @@ create.manhattanplot <- function( fontface = text.fontface ); - if (!is.null(yat) && length(yat) == 1) { - if (yat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[2]])])); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } + out <- prep.axis( + at = xat, + data = unlist(data[toString(formula[[3]])]), + which.arg = 'xat' + ); + if (is.list(out)) { + data[toString(formula[[3]])] <- out$x; + xat <- out$at; + xaxis.lab <- out$axis.lab; + } - else if (yat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = FALSE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } + out <- prep.axis( + at = yat, + data = unlist(data[toString(formula[[2]])]), + which.arg = 'yat' + ); + if (is.list(out)) { + data[toString(formula[[2]])] <- out$x; + yat <- out$at; + yaxis.lab <- out$axis.lab; + } - else if (yat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = TRUE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - } - if (!is.null(xat) && length(xat) == 1) { - if (xat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[3]])])); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = FALSE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = TRUE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - } # add preloaded defaults if (preload.default == 'paper') { } diff --git a/R/create.scatterplot.R b/R/create.scatterplot.R index 7afe4cf..9887f31 100644 --- a/R/create.scatterplot.R +++ b/R/create.scatterplot.R @@ -238,49 +238,27 @@ create.lollipopplot <- create.scatterplot <- function( } } - if (!is.null(yat) && length(yat) == 1) { - if (yat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[2]])])); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = FALSE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - - else if (yat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[2]])]), log.scaled = TRUE); - data[toString(formula[[2]])] <- out$x; - yat <- out$at; - yaxis.lab <- out$axis.lab; - } - } - - if (!is.null(xat) && length(xat) == 1) { - if (xat == 'auto') { - out <- auto.axis(unlist(data[toString(formula[[3]])])); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.linear') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = FALSE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - else if (xat == 'auto.log') { - out <- auto.axis(unlist(data[toString(formula[[3]])]), log.scaled = TRUE); - data[toString(formula[[3]])] <- out$x; - xat <- out$at; - xaxis.lab <- out$axis.lab; - } - } + out <- prep.axis( + at = xat, + data = unlist(data[toString(formula[[3]])]), + which.arg = 'xat' + ); + if (is.list(out)) { + data[toString(formula[[3]])] <- out$x; + xat <- out$at; + xaxis.lab <- out$axis.lab; + } + + out <- prep.axis( + at = yat, + data = unlist(data[toString(formula[[2]])]), + which.arg = 'yat' + ); + if (is.list(out)) { + data[toString(formula[[2]])] <- out$x; + yat <- out$at; + yaxis.lab <- out$axis.lab; + } # add preloaded defaults if (preload.default == 'paper') { diff --git a/man/auto.axis.Rd b/man/auto.axis.Rd index ccd05c7..5f1d459 100755 --- a/man/auto.axis.Rd +++ b/man/auto.axis.Rd @@ -1,22 +1,22 @@ \name{auto.axis} \alias{auto.axis} -\title{Create ideal labels and values for a given dataset (detects log scales)} -\description{Takes a dataset and several parameters and outputs an object with values and labels ideal for that dataset} +\title{Create ideal labels and values for a given numeric vector (detects log scales)} +\description{Takes a numeric vector and several parameters and outputs an object with values and labels ideal for given data} \usage{ auto.axis( - x, - pretty = TRUE, - log.scaled = NA, - log.zero = 0.1, - max.factor = 1, - min.factor = 1, - include.origin = TRUE, - num.labels = 5, + x, + pretty = TRUE, + log.scaled = NA, + log.zero = 0.1, + max.factor = 1, + min.factor = 1, + include.origin = TRUE, + num.labels = 5, max.min.log10.diff = 2 ) } -\arguments{ - \item{x}{The dataset that is intended to be used for the labels and redisdributed data} +\arguments{ + \item{x}{Numeric vector to be scaled} \item{pretty}{Parameter flag for if output should be in pretty format} \item{log.scaled}{parameter set to determine if scaling is logarithmic or not} \item{log.zero}{log 0 starting point} @@ -26,7 +26,7 @@ auto.axis( \item{num.labels}{number of labels to output} \item{max.min.log10.diff}{the max and min diffrence for dataset to be determined logarithmic} } -\author{Takafumi Yamaguch} +\author{Takafumi N. Yamaguchi} \seealso{\code{\link[lattice]{stripplot}}, \code{\link[lattice]{lattice}} or the Lattice book for an overview of the package.} \examples{ set.seed(223); diff --git a/man/create.barplot.Rd b/man/create.barplot.Rd index 3b092c3..ed8ff7e 100644 --- a/man/create.barplot.Rd +++ b/man/create.barplot.Rd @@ -128,7 +128,7 @@ create.barplot( ); } \arguments{ - \item{formula}{The formula used to extract the x & y components from the data-frame} + \item{formula}{The formula used to extract the x & y components from the data-frame. Transforming data within formula is not compatible with automatic scaling with `xat` or `yat`} \item{data}{The data-frame to plot} \item{groups}{Optional grouping variable. Expression or variable.} \item{stack}{Logical, relevant when groups is non-null. If FALSE (the default), bars for different values of the grouping variable are drawn side by side, otherwise they are stacked} @@ -161,8 +161,8 @@ create.barplot( \item{ygrid.at}{Specify where to draw y-axis grid lines (defaults to yat)} \item{grid.lwd}{Specify width of grid line (defaults to 5)} \item{grid.col}{Specify colour of grid line. Currently only supports one colour. Defaults to NULL, which uses the colour of the reference line.} - \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic} - \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic} + \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with xat will overwrite user input. Set to NULL to remove x-axis labels.} + \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with yat will overwrite user input. Set to NULL to remove y-axis labels.} \item{xaxis.col}{Colour of the x-axis tick labels, defaults to black} \item{yaxis.col}{Colour of the y-axis tick labels, defaults to black} \item{xaxis.fontface}{Fontface for the x-axis scales} @@ -175,8 +175,8 @@ create.barplot( \item{yaxis.tck}{Specifies the length of the tick marks for y-axis, defaults to 1} \item{xlimits}{Two-element vector giving the x-axis limits. Useful when plot.horizontal = TRUE} \item{ylimits}{Two-element vector giving the y-axis limits} - \item{xat}{Vector listing where the x-axis labels should be drawn. Useful when plot.horizontal = TRUE} - \item{yat}{Vector listing where the y-axis labels should be drawn} + \item{xat}{Accepts a vector listing where x-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes x-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`. Useful when plot.horizontal = TRUE} + \item{yat}{Accepts a vector listing where y-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes y-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} \item{layout}{A vector specifying the number of columns, rows (e.g., c(2,1). Default is NULL; see lattice::xyplot for more details}. \item{as.table}{Specifies panel drawing order, default is FALSE which draws panels from bottom left corner, moving right then up. Set to TRUE to draw from top left corner, moving right then down} \item{x.spacing}{A number specifying the distance between panels along the x-axis, defaults to 0} @@ -386,6 +386,22 @@ create.barplot( resolution = 100 ) +# Log-Scaled Axis +log.data <- data.frame( + x = 10 ** sample(1:15, 5), + y = LETTERS[1:5] + ); + +create.barplot( + formula = x ~ y, + data = log.data, + # Log base 10 scale y-axis + yat = 'auto.log', + main = 'Log Scaled', + description = 'Barplot created by BoutrosLab.plotting.general', + resolution = 100 + ); + # Colour changes sex.colours <- replace(as.vector(barplot.data$sex), which(barplot.data$sex == 'male'),'dodgerblue'); sex.colours <- replace(sex.colours, which(barplot.data$sex == 'female'), 'pink'); diff --git a/man/create.boxplot.Rd b/man/create.boxplot.Rd index e81b3a3..819d9b7 100644 --- a/man/create.boxplot.Rd +++ b/man/create.boxplot.Rd @@ -110,7 +110,7 @@ create.boxplot( ); } \arguments{ - \item{formula}{The formula used to extract the x & y components from the data-frame} + \item{formula}{The formula used to extract the x & y components from the data-frame. Transforming data within formula is not compatible with automatic scaling with `xat` or `yat`.} \item{data}{The data-frame to plot} \item{filename}{Filename for tiff output, or if NULL returns the trellis object itself} \item{main}{The main title for the plot (space is reclaimed if NULL)} @@ -161,10 +161,10 @@ create.boxplot( \item{xlab.top.y}{The y location of the top y-axis label} \item{xlimits}{Two-element vector giving the x-axis limits} \item{ylimits}{Two-element vector giving the y-axis limits} - \item{xat}{Vector listing where the x-axis labels should be drawn} - \item{yat}{Vector listing where the y-axis labels should be drawn} - \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Set to NULL to remove x-axis labels.} - \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Set to NULL to remove y-axis labels.} + \item{xat}{Accepts a vector listing where x-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes x-axis tick locations, labels, and data values dependent on given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{yat}{Accepts a vector listing where y-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes y-axis tick locations, labels, and data values dependent on given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with xat will overwrite user input. Set to NULL to remove x-axis labels.} + \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with yat will overwrite user input. Set to NULL to remove y-axis labels.} \item{xaxis.cex}{Size of x-axis tick labels, defaults to 2} \item{yaxis.cex}{Size of y-axis tick labels, defaults to 2} \item{xaxis.col}{Colour of the x-axis tick labels, defaults to \dQuote{black}} @@ -384,6 +384,22 @@ create.boxplot( resolution = 100 ); +# Log Scaled Axis +log.data <- data.frame( + x = 10 ** rnorm(1000, 5, 2), + y = rep('A',1000) + ); + +create.boxplot( + formula = x ~ y, + data = log.data, + # Log base 10 scale y axis + yat = 'auto.log', + main = 'Log Scale', + description = 'Boxplot created by BoutrosLab.plotting.general', + resolution = 100 + ); + # Legend create.boxplot( # filename = tempfile(pattern = 'Boxplot_Legend', fileext = '.tiff'), diff --git a/man/create.hexbinplot.Rd b/man/create.hexbinplot.Rd index e00a5e4..0628252 100644 --- a/man/create.hexbinplot.Rd +++ b/man/create.hexbinplot.Rd @@ -112,7 +112,7 @@ create.hexbinplot( ); } \arguments{ - \item{formula}{The formula used to extract the x & y components from the data-frame} + \item{formula}{The formula used to extract the x & y components from the data-frame. Transforming data within formula is not compatible with automatic scaling with `xat` or `yat`.} \item{data}{The data-frame to plot} \item{filename}{Filename for tiff output, or if NULL (default value) returns the trellis object itself} \item{main}{The main title for the plot (space is reclaimed if NULL)} @@ -144,10 +144,10 @@ create.hexbinplot( \item{xlab.top.y}{The y location of the top y-axis label} \item{xlimits}{Two-element vector giving the x-axis limits} \item{ylimits}{Two-element vector giving the y-axis limits} - \item{xat}{Vector listing where the x-axis labels should be drawn} - \item{yat}{Vector listing where the y-axis labels should be drawn} - \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic} - \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic} + \item{xat}{Accepts a vector listing where x-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes x-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{yat}{Accepts a vector listing where y-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes y-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with xat will overwrite user input. Set to NULL to remove x-axis labels.} + \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with yat will overwrite user input. Set to NULL to remove y-axis labels.} \item{xaxis.cex}{Size of x-axis scales, defaults to 2} \item{yaxis.cex}{Size of y-axis scales, defaults to 2} \item{xaxis.rot}{Rotation of x-axis tick labels; defaults to 0} @@ -303,6 +303,22 @@ create.hexbinplot( resolution = 100 ); +# Log Scaled Axis +log.data <- data.frame( + x = microarray[,1], + y = 10 ** microarray[,2] + ); + +create.hexbinplot( + formula = y ~ x, + data = log.data, + main = 'Log Scaled', + # Log base 10 scale y-axis + yat = 'auto.log', + description = 'Hexbinplot created by BoutrosLab.plotting.general', + resolution = 100 + ); + \donttest{ # Aspect Ratio create.hexbinplot( diff --git a/man/create.manhattanplot.Rd b/man/create.manhattanplot.Rd index aecb4ff..fdec72f 100755 --- a/man/create.manhattanplot.Rd +++ b/man/create.manhattanplot.Rd @@ -108,7 +108,7 @@ create.manhattanplot( ); } \arguments{ - \item{formula}{The formula used to extract the x & y components from the data-frame} + \item{formula}{The formula used to extract the x & y components from the data-frame. Transforming data within formula is not compatible with automatic scaling with `xat` or `yat`.} \item{data}{The data-frame to plot} \item{filename}{Filename for tiff output, or if NULL returns the trellis object itself} \item{main}{The main title for the plot (space is reclaimed if NULL)} @@ -130,10 +130,10 @@ create.manhattanplot( \item{xlab.top.y}{The y location of the top y-axis label} \item{xlimits}{Two-element vector giving the x-axis limits, defaults to automatic} \item{ylimits}{Two-element vector giving the y-axis limits, defaults to automatic} - \item{xat}{Vector listing where the x-axis labels should be drawn, defaults to automatic} - \item{yat}{Vector listing where the y-axis labels should be drawn, defaults to automatic} - \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic} - \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic} + \item{xat}{Accepts a vector listing where x-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes x-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{yat}{Accepts a vector listing where y-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes y-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with xat will overwrite user input. Set to NULL to remove x-axis labels.} + \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with yat will overwrite user input. Set to NULL to remove y-axis labels.} \item{xaxis.log}{Logical indicating whether x-variable should be in logarithmic scale (and what base if numeric)} \item{yaxis.log}{Logical indicating whether y-variable should be in logarithmic scale (and what base if numeric)} \item{xaxis.cex}{Size of x-axis scales, defaults to 1} @@ -300,6 +300,22 @@ create.manhattanplot( resolution = 100 ); +# Log-Scaled Axis +log.data <- data.frame( + x = 10 ** runif(20000, 1, 5), + y = 1:20000 + ); + +create.manhattanplot( + formula = x ~ y, + data = log.data, + main = 'Log Scaled', + # Log base 10 scale x-axis + xat = 'auto.log', + description = 'Manhattan plot created using BoutrosLab.plotting.general', + resolution = 50 + ); + # Colour scheme create.manhattanplot( # filename = tempfile(pattern = 'Manhattan_Colour_Scheme', fileext = '.tiff'), diff --git a/man/create.scatterplot.Rd b/man/create.scatterplot.Rd index e1c7c9d..e877a1f 100644 --- a/man/create.scatterplot.Rd +++ b/man/create.scatterplot.Rd @@ -159,7 +159,7 @@ create.scatterplot( ); } \arguments{ - \item{formula}{The formula used to extract the x & y components from the data-frame} + \item{formula}{The formula used to extract the x & y components from the data-frame. Transforming data within formula is not compatible with automatic scaling with `xat` or `yat`.} \item{data}{The data-frame to plot} \item{filename}{Filename for tiff output, or if NULL returns the trellis object itself} \item{groups}{The grouping variable in the data-frame} @@ -182,10 +182,10 @@ create.scatterplot( \item{xlab.top.y}{The y location of the top y-axis label} \item{xlimits}{Two-element vector giving the x-axis limits, defaults to automatic} \item{ylimits}{Two-element vector giving the y-axis limits, defaults to automatic} - \item{xat}{Vector listing where the x-axis labels should be drawn, defaults to automatic} - \item{yat}{Vector listing where the y-axis labels should be drawn, defaults to automatic} - \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic} - \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic} + \item{xat}{Accepts a vector listing where x-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes x-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{yat}{Accepts a vector listing where y-axis ticks should be drawn or if automatic scaling is desired, one of three strings: \dQuote{auto}, \dQuote{auto.linear} or \dQuote{auto.log}. Automatic scaling fixes y-axis tick locations, labels, and data values dependent given data. \dQuote{auto} will determine whether linear or logarithmic scaling fits the given data best, \dQuote{auto.linear} or \dQuote{auto.log} will force data to be scaled linearly or logarithmically respectively. Defaults to lattice automatic (TRUE). For more details see `auto.axis()`.} + \item{xaxis.lab}{Vector listing x-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with xat will overwrite user input. Set to NULL to remove x-axis labels.} + \item{yaxis.lab}{Vector listing y-axis tick labels, defaults to automatic (TRUE). Using automatic scaling with yat will overwrite user input. Set to NULL to remove y-axis labels.} \item{xaxis.log}{Logical indicating whether x-variable should be in logarithmic scale (and what base if numeric)} \item{yaxis.log}{Logical indicating whether y-variable should be in logarithmic scale (and what base if numeric)} \item{xaxis.cex}{Size of x-axis scales, defaults to 2} @@ -376,37 +376,19 @@ create.scatterplot( resolution = 50 ); -# Plotting space +# Log-Scaled Axis +log.data <- data.frame( + x = rnorm(800), + y = 10 ** rnorm(800, mean = 5, sd = 2) + ); + create.scatterplot( - # filename = tempfile(pattern = 'Scatterplot_Plotting_Space', fileext = '.tiff'), - formula = sample.two ~ sample.one, - data = scatter.data, - main = 'Plotting Space', - xlab.label = colnames(microarray[1]), - ylab.label = colnames(microarray[2]), - # change axes accordingly - xat = 2 ** (0:4), - yat = seq(0, 16, 2), - # change axes range accordingly - xlimits = c(2 ** 0,16), - ylimits = c(0, 15), - # format labels - xaxis.lab = c( - expression('2'^'0'), - expression('2'^'1'), - expression('2'^'2'), - expression('2'^'3'), - expression('2'^'4') - ), - xaxis.cex = 1, - yaxis.cex = 1, - xaxis.fontface = 1, - yaxis.fontface = 1, - xlab.cex = 1.5, - ylab.cex = 1.5, - # Transform the x-axis into log-2 space - xaxis.log = 2, - description = 'Scatter plot created by BoutrosLab.plotting.general', + formula = y ~ x, + data = log.data, + # Log base 10 scale y-axis + yat = 'auto.log', + main = 'Log Scaled', + description = 'Scatter created by BoutrosLab.plotting.general', resolution = 50 ); diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..aacd65b --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(BoutrosLab.plotting.general) + +test_check('BoutrosLab.plotting.general') diff --git a/tests/testthat/test-auto.axis.R b/tests/testthat/test-auto.axis.R new file mode 100644 index 0000000..c4aaf60 --- /dev/null +++ b/tests/testthat/test-auto.axis.R @@ -0,0 +1,166 @@ +test_that( + 'prep.axis returns unchanged data with length > 1', { + yat <- seq(0, 10, 2); + result <- prep.axis(yat, 1:10, 'yat'); + + expect_equal(result, yat); + } + ); + +test_that( + 'prep.axis returns unchanged boolean value', { + xat <- TRUE; + result <- prep.axis(xat, 1:10, 'xat'); + + expect_equal(result, xat); + } + ); + +test_that( + 'prep.axis returns unchanged NULL value', { + yat <- NULL; + result <- prep.axis(yat, 1:10, 'yat'); + + expect_equal(result, yat); + } + ); + +test_that( + 'prep.axis returns a list with "auto" setting', { + result <- prep.axis('auto', 1:10, 'yat'); + expect_true(is.list(result)); + } + ); + +test_that( + 'prep.axis sets $at using auto.axis with "auto" setting', { + local({ + expected.at <- 1:10; + local_mocked_bindings(auto.axis = function(...) list(at = expected.at)); + result <- prep.axis('auto', 1:10, 'yat'); + + expect_equal(result$at, expected.at); + }); + } + ); + +test_that( + 'prep.axis sets $labels using auto.axis with "auto" setting', { + local({ + expected.labels <- 1:5 * 2; + local_mocked_bindings(auto.axis = function(...) list(labels = expected.labels)); + result <- prep.axis('auto', 1:10, 'yat'); + + expect_equal(result$labels, expected.labels); + }); + } + ); + +test_that( + 'prep.axis sets $data using auto.axis with "auto" setting', { + local({ + expected.data <- 11:20 ** 2; + local_mocked_bindings(auto.axis = function(...) list(data = expected.data)); + result <- prep.axis('auto', 1:10, 'yat'); + + expect_equal(result$data, expected.data); + }); + } + ); + +test_that( + 'prep.axis returns a list with "auto.linear" setting', { + result <- prep.axis('auto.linear', 1:10, 'yat'); + expect_true(is.list(result)); + } + ); + +test_that( + 'prep.axis sets $at using auto.axis with "auto.linear" setting', { + local({ + expected.at <- 1:10; + local_mocked_bindings(auto.axis = function(...) list(at = expected.at)); + result <- prep.axis('auto.linear', 1:10, 'yat'); + + expect_equal(result$at, expected.at); + }); + } + ); + +test_that( + 'prep.axis sets $labels using auto.axis with "auto.linear" setting', { + local({ + expected.labels <- 6:11 * 2; + local_mocked_bindings(auto.axis = function(...) list(labels = expected.labels)); + result <- prep.axis('auto.linear', 1:10, 'yat'); + + expect_equal(result$labels, expected.labels); + }); + } + ); + +test_that( + 'prep.axis sets $data using auto.axis with "auto.linear" setting', { + local({ + expected.data <- 4:16 ** 2; + local_mocked_bindings(auto.axis = function(...) list(data = expected.data)); + result <- prep.axis('auto.linear', 1:10, 'yat'); + + expect_equal(result$data, expected.data); + }); + } + ); + +test_that( + 'prep.axis returns a list with "auto.log" setting', { + result <- prep.axis('auto.log', 1:10, 'yat'); + expect_true(is.list(result)); + } + ); + +test_that( + 'prep.axis sets $at using auto.axis with "auto.log" setting', { + local({ + expected.at <- 1:10; + local_mocked_bindings(auto.axis = function(...) list(at = expected.at)); + result <- prep.axis('auto.log', 1:10, 'yat'); + + expect_equal(result$at, expected.at); + }); + } + ); + +test_that( + 'prep.axis sets $labels using auto.axis with "auto.log" setting', { + local({ + expected.labels <- 1:10 * 10; + local_mocked_bindings(auto.axis = function(...) list(labels = expected.labels)); + result <- prep.axis('auto.log', 1:10, 'yat'); + + expect_equal(result$labels, expected.labels); + }); + } + ); + +test_that( + 'prep.axis sets $data using auto.axis with "auto.log" setting', { + local({ + expected.data <- 1:6 ** 3; + local_mocked_bindings(auto.axis = function(...) list(data = expected.data)); + result <- prep.axis('auto.log', 1:10, 'yat'); + + expect_equal(result$data, expected.data); + }); + } + ); + +test_that( + 'prep.axis throws error on invalid setting', { + expect_error( + object = { + prep.axis('invalid', 1:10, 'yat'); + }, + regexp = 'invalid' + ); + } + ); diff --git a/tests/testthat/test-plotting.functions.R b/tests/testthat/test-plotting.functions.R new file mode 100644 index 0000000..e181709 --- /dev/null +++ b/tests/testthat/test-plotting.functions.R @@ -0,0 +1,187 @@ +### TEST-SUITE ##################################################################################### +# Description: This script runs the plotting code in BoutrosLab.plotting.general for testing purposes +# The plots are stored in variables and plotted together in a single multiple in order to save plot-generation time + +test_that( + 'Run plotting functions', { + ### PLOTTING FUNCTIONS ############################################################################# + test.data <- data.frame( + x = 1:10, + y = LETTERS[1:10], + z = rnorm(10), + type = rep(LETTERS[1:2], 5), + stringsAsFactors = TRUE + ); + + # testing legend.grob + covariate.legend <- list( + legend = list( + colours = default.colours(2), + labels = levels(test.data$type)[1:2] + ) + ); + + legend.grob2 <- legend.grob( + legends = covariate.legend + ); + + # testing covariates.grob + covariate.colours1 <- as.character(test.data$type); + covariate.colours1[covariate.colours1 == 'A'] <- default.colours(2)[1]; + covariate.colours1[covariate.colours1 == 'B'] <- default.colours(2)[2]; + + # create an object to draw the covariates from + covariates1 <- list( + rect = list( + col = 'black', + fill = covariate.colours1, + lwd = 1.5 + ) + ); + + covariates.grob1 <- covariates.grob( + covariates = covariates1, + ord = c(1:10), + side = 'top', + size = .8 + ); + + barplot <- create.barplot( + formula = y ~ x, + data = test.data, + legend = list( + bottom = list( + fun = covariates.grob1 + ), + right = list( + fun = legend.grob2 + ), + inside = list( + fun = draw.key, + args = list( + key = get.corr.key( + x = test.data$x, + y = test.data$y, + label.items = c('spearman', 'spearman.p', 'kendall', 'beta1') + ) + ), + x = 0.5, + y = 0.5 + ) + ), + description = 'testing metadata' + ); + + # this isn't printed anywhere + dend <- create.dendrogram( + x = data.frame(test.data$z, rnorm(10)) + ); + + dotmap <- create.dotmap( + x = test.data$z + ); + + hexbin <- create.hexbinplot( + formula = z ~ x, + data = test.data + ); + + # Also using generate.at.final() + density <- create.densityplot( + x = list( + a = test.data$x, + b = test.data$z + ), + xat = c(0, 1.1, 1.5, 1, 6, 4), + yat = c(0.34, 0.38, 0.7), + type = c('g', 'l'), + resolution = 100 + ); + + manhattanplot <- create.manhattanplot( + formula = x ~ y, + data = test.data + ); + + polygonplot <- create.polygonplot( + formula = NA ~ x, + data = test.data, + max = test.data$x, + min = test.data$z, + xlimits = c(0, 10), + ylimits = c(-2, 10) + ); + + qqcomp <- create.qqplot.comparison( + x = list(test.data$x, test.data$z) + ); + + scatter <- create.scatterplot( + formula = x ~ z, + data = test.data + ); + + seg <- create.segplot( + formula = y ~ x + z, + data = test.data + ); + + violin <- create.violinplot( + formula = y ~ z, + data = test.data + ); + + strip <- create.stripplot( + formula = z ~ y, + data = test.data + ); + + boxplot <- create.boxplot( + formula = x ~ y, + data = test.data + ); + + # these do not easily join the multiplot + histogram <- create.histogram( + x = test.data$z + ); + + qqfit <- create.qqplot.fit( + x = test.data$z + ); + + expect_no_error({ + # Plot everything in one plot for quicker running time + create.multiplot( + file = NULL, + plot.objects = list(polygonplot, manhattanplot, hexbin, dotmap, density, qqcomp, scatter, seg, violin, barplot, strip, boxplot) + ); + }); + } + ); + +test_that( + 'Run helper functions', { + expect_no_error({ + ### HELPER FUNCTIONS ############################################################################### + get.line.breaks(1:10); + + scientific.notation(1234, 2, type = 'list'); + scientific.notation(0, 2); + scientific.notation(c(1234, 1234), 1); + + # This returns the same thing as when type = 'expression' - should something be changed? + scientific.notation(c(1234, 1234), 1, type = 'list'); + + display.statistical.result(1234); + + ### COLOUR FUNCTIONS ############################################################################### + default.colours(12, is.greyscale = FALSE); + default.colours(5, palette.type = 'chromosomes', is.greyscale = FALSE); + default.colours(5, palette.type = 'seq'); + default.colours(c(4, 4), palette.type = c('seq', 'div')); + + force.colour.scheme(c('stopgain snv', 'splicing'), 'annovar.annotation'); + }); + } + ); diff --git a/vignettes/PlottingGuide.Rnw b/vignettes/PlottingGuide.Rnw index 7f236d6..b780f9a 100755 --- a/vignettes/PlottingGuide.Rnw +++ b/vignettes/PlottingGuide.Rnw @@ -302,7 +302,7 @@ require("knitr") opts_chunk$set(fig.path='Examples/', dev='png', warning=FALSE, dpi=120, fig.height=4, fig.width=4, fig.align='center', resize.width='70mm', resize.height='70mm') @ -<>= +<>= library(BoutrosLab.plotting.general); scatter.data <- data.frame( @@ -349,38 +349,20 @@ create.scatterplot( \subsubsection{Scatterplot with log-scale axes} The plot can be changed to use a log-scale. -<>= +<>= +log.scatter.data <- data.frame( + x = 1:100, + y = 10 ** rnorm(100, 6, 2) + ); + create.scatterplot( - formula = sample.two ~ sample.one, - data = scatter.data, + formula = y ~ x, + data = log.scatter.data, main = "Log-scale Axis", - xlab.label = "Sample 1", - ylab.label = "Sample 2", - yat = seq(0, 16, 2), - ylimits = c(0, 15), - xaxis.cex = 1, - yaxis.cex = 1, - xlab.cex = 1.5, - ylab.cex = 1.5, main.cex = 1.5, - - # change axes accordingly - xat = 2 ** (0:4), - - # change axes range accordingly - xlimits = c(2 ** 0,16), - - # format labels - xaxis.lab = c( - expression('2'^'0'), - expression('2'^'1'), - expression('2'^'2'), - expression('2'^'3'), - expression('2'^'4') - ), - - # transform x-axis into log-2 space - xaxis.log = 2 + yat = 'auto.log', # Log-scale y-axis, + xlab.label = '', + ylab.label = '' ); @ @@ -615,8 +597,8 @@ The boxplot is used to display distributions of data. It is also known as the bo The boxplot also requires a data frame and a corresponding formula. <>= boxplot.data <- data.frame( - x <- as.vector(t(microarray[1:10,1:58])), - y <- as.factor(rep(rownames(microarray[1:10,1:58]), each = 58)) + x = as.vector(t(microarray[1:10,1:58])), + y = as.factor(rep(rownames(microarray[1:10,1:58]), each = 58)) ); create.boxplot( @@ -717,6 +699,24 @@ create.boxplot( ); @ +\subsubsection{Boxplot with log-scale axes} +The plot can be changed to use a log-scale. +<>= +log.boxplot.data <- data.frame( + x = 10 ** rnorm(100, 5, 2), + y = 1 + ); + +create.boxplot( + formula = y ~ x, + data = log.boxplot.data, + xat = 'auto.log', # Scale x-axis, + ylab.label = '', + xlab.label = '' + ); + +@ + \subsection{Heatmap} \begin{verbatim} create.heatmap @@ -1032,6 +1032,25 @@ create.barplot( ); @ +\subsubsection{Barplot plot with log-scale axes} +The plot can be changed to use a log-scale. +<>= +log.barplot.data <- data.frame( + x = 1:10, + y = 10 ** rnorm(10, mean = 5, sd = 3) + ); + +create.barplot( + y ~ x, + log.barplot.data, + # Log scale y-axis + yat = 'auto.log', + ylab.label = '', + xlab.label = '' + ); +@ + + \subsubsection{Stacked barplot with legend} Stacked barplots compare overall quantities across items, and also indicate the contribution of sub-categories to the total count \cite{streit}. @@ -1354,6 +1373,21 @@ create.hexbinplot( ); @ +\subsubsection{Hexbin plot with log-scale axes} +The plot can be changed to use a log-scale. +<>= +log.hexbin.data <- hexbin.data; +log.hexbin.data$x <- 10 ** (log.hexbin.data$x); + +create.hexbinplot( + data = hexbin.data, + formula = x ~ y, + xat = 'auto.log', # Scale x-axis, + xlab.label = '', + ylab.label = '' + ); +@ + \subsection{Histogram} \begin{verbatim} create.histogram @@ -2254,6 +2288,105 @@ The spiral.sunrise, spiral.dusk, etc colour schemes were created by ``spiralling \item \texttt{get.line.breaks} is used in the specific case of placing indices in heatmaps \end{itemize} + +\subsection{Axis Scaling} +The scale at which data is viewed is extremely impactful to the insight that can be drawn from a visualization. While linear scaling is perfectly fine, some datasets are more easily interpreted after log transforming the data, for example. +\subsubsection{Automatic Axis Scaling} +BPG supports automatic axis scaling for several plot types with the \verb|auto.axis| function. This functionality can be accessed through the \verb|xat| and \verb|yat| parameters when calling a plotting function. There are 3 settings to choose from: +\begin{itemize} +\item {\verb|auto.log|: $log_{10}$ scaled, with automatically spaced and formatted tick locations.} +\item{\verb|auto.linear|: Linear scaled, with automatic tick placement based on analysis of the data.} +\item{\verb|auto|: Automatically chooses the most appropriate setting (\verb|auto.log| or \verb|auto.linear|).} +\end{itemize} +If nothing is passed, the plot will use lattice's default axis creation method, which is often less "pretty" than BPG's automatic axes. + +<>= +create.auto.axis.examples <- function() { + auto.axis.data <- data.frame( + x = 1:10, + y = 10 ** (1:10 + rnorm(10, sd = 0.4)) + ); + + create.axis.example <- function(auto.axis.setting = NULL) { + if (is.null(auto.axis.setting)) { + main <- 'default'; + auto.axis.setting = FALSE; + } else { + main <- auto.axis.setting; + } + + create.scatterplot( + y ~ x, + auto.axis.data, + yat = auto.axis.setting, + main = main, + main.cex = 2, + main.x = 0.55, + main.y = -0.75, + yaxis.cex = 1.5, + cex = 1, + xat = c(), + xlab.label = '', + ylab.label = '' + ); + } + + auto.log.scatter <- create.axis.example('auto.log'); + auto.linear.scatter <- create.axis.example('auto.linear'); + auto.scatter <- create.axis.example('auto'); + default.scatter <- create.axis.example(); + + create.multipanelplot( + plot.objects = list( + auto.log.scatter, + auto.linear.scatter, + auto.scatter, + default.scatter + ), + layout.width = 2, + layout.height = 2, + x.spacing = 2, + xlab.label = '' + ); + } +create.auto.axis.examples(); +@ + +\subsubsection{Manual Axis Scaling} +It's possible to manually scale the axes for use cases not supported by \verb|auto.axis|. This is a three step process: +\begin{itemize} +\item{Transform the data.} +\item{Transform the axis tick locations in \verb|xat| and \verb|yat| to match the data transformation.} +\item{Override the axis tick labels in \verb|xaxis.lab| and \verb|yaxis.lab| to correspond to the pre-transformation values from the data.} +\end{itemize} + +<>= +# Scaled y-axis data +manual.axis.scaling.data <- data.frame(x = 1:20, y = (1:20) ** 3); +manual.axis.scaling.data$log3.y <- log(manual.axis.scaling.data$y, base = 3); + +num.y.ticks <- 10; + +# Scaled y-axis tick locations +log3.range <- range(manual.axis.scaling.data$log3.y); +log3.yat <- seq(log3.range[1], log3.range[2], length.out = num.y.ticks); + +# Unscaled y-axis tick labels +linear.range <- range(manual.axis.scaling.data$y); +y.tick.labels <- seq(linear.range[1], linear.range[2], length.out = num.y.ticks); + +create.scatterplot( + log3.y ~ x, + manual.axis.scaling.data, + yat = log3.yat, + yaxis.lab = round(y.tick.labels), + xlab.label = '', + ylab.label = '', + yaxis.cex = 1, + xat = c() + ); +@ + \subsection{Private} Other functions are called by plotting functions but are not intended for use by end-users: \begin{verbatim} diff --git a/vignettes/PlottingGuide.pdf b/vignettes/PlottingGuide.pdf index 1e3639a..035a9bb 100755 Binary files a/vignettes/PlottingGuide.pdf and b/vignettes/PlottingGuide.pdf differ