#' Plot multiple signal tracks in line with each other
#'
#' @usage plotMultiSignal(
#' data,
#' binSize = NA,
#' binCap = TRUE,
#' negData = FALSE,
#' chrom,
#' chromstart = NULL,
#' chromend = NULL,
#' assembly = "hg38",
#' linecolor= "#37a7db",
#' fill = NA,
#' ymax = 1,
#' range = NULL,
#' scale = FALSE,
#' label = NULL,
#' bg = NA,
#' baseline = TRUE,
#' baseline.color = "grey",
#' baseline.lwd = 1,
#' orientation = "h",
#' x = NULL,
#' y = NULL,
#' width = NULL,
#' height = NULL,
#' just = c("left", "top"),
#' gapdistance = .2,
#' default.units = "inches",
#' draw = TRUE,
#' params = NULL, ...
#' )
#'
#' @param data List of data to be plotted as character values specifying
#' multiple bigwig file paths, dataframes in BED format, or
#' \link[GenomicRanges]{GRanges} objects with metadata column \code{score}.
#' @param binSize A numeric specifying the length of each data
#' bin in basepairs. Default value is \code{binSize = NA}.
#' @param binCap A logical value indicating whether the function will
#' limit the number of data bins to 8,000.
#' Default value is \code{binCap = TRUE}.
#' @param negData A logical value indicating whether any of the data has both
#' positive and negative scores and the y-axis of each signal track
#' should be split. Default value is \code{negData = FALSE}.
#' @param chrom Chromosome of region to be plotted, as a string.
#' @param chromstart Integer start position on chromosome to be plotted.
#' @param chromend Integer end position on chromosome to be plotted.
#' @param assembly Default genome assembly as a string or a
#' \link[plotgardener]{assembly} object.
#' Default value is \code{assembly = "hg38"}.
#' @param linecolor A character value or vector of character values specifying
#' the line color(s) outlining the signal tracks.
#' Default value is \code{linecolor = "#37a7db"}.
#' @param fill A character value or vector specifying
#' the fill color(s) of the signal tracks. Default value is \code{fill = NA}.
#' @param ymax A numeric specifying the fraction of the max y-value
#' to set as the height of each plot. Default value is \code{ymax = 1}.
#' @param range A numeric vector of length 2 specifying the y-range
#' of data to plot (c(min, max)) in each signal track. If \code{range = NULL},
#' an optimal range for all signal tracks will be calculated.
#' @param scale A logical value indicating whether to include a data
#' scale label in the top left corner of each plot.
#' Default value is \code{scale = FALSE}.
#' @param label An optional character vector to conveniently add text labels
#' to signal tracks. If \code{scale = TRUE}, the labels will be drawn in the
#' top right of the signal tracks. Otherwise, the label will be drawn in the
#' top left of the plot. For more customizable labels,
#' use \link[plotgardener]{plotText}.
#' @param bg Character value indicating background color.
#' Default value is \code{bg = NA}.
#' @param baseline Logical value indicating whether to include a
#' baseline along the x-axis. Default value is \code{baseline = TRUE}.
#' @param baseline.color Baseline color.
#' Default value is \code{baseline.color = "grey"}.
#' @param baseline.lwd Baseline line width.
#' Default value is \code{baseline.lwd = 1}.
#' @param orientation A string specifying signal track orientations.
#' Default value is \code{orientation = "h"}. Options are:
#' \itemize{
#' \item{\code{"v"}: }{Vertical signal track orientations, where signal tracks
#' will be stacked from left to right.}
#' \item{\code{"h"}: }{Horizontal signal track orientations, where signal tracks
#' will be stacked from top to bottom.}
#' }
#' @param x A numeric vector or unit object specifying the overall multisignal
#' x-location.
#' @param y A numeric, unit object, or character containing a "b"
#' combined with a numeric value specifying overall multisignal plot y-location.
#' The character value will
#' place the multisignal plot y relative to the bottom of the most recently
#' plotted plot according to the units of the plotgardener page.
#' @param width A numeric or unit object specifying overall multisignal plot
#' width.
#' @param height A numeric or unit object specifying overall multisignal plot
#' height.
#' @param just Justification of overall multisignal plot relative to
#' its (x, y) location. If there are two values, the first value specifies
#' horizontal justification and the second value specifies vertical
#' justification. Possible string values are: \code{"left"}, \code{"right"},
#' \code{"centre"}, \code{"center"}, \code{"bottom"}, and \code{"top"}.
#' Default value is \code{just = c("left", "top")}.
#' @param gapdistance A numeric or unit object
#' specifying space between plots. Default value is \code{gapdistance = 0.2}.
#' @param default.units A string indicating the default units to use
#' if \code{x} or \code{y} are only given as numerics.
#' Default value is \code{default.units = "inches"}.
#' @param draw A logical value indicating whether graphics output should be
#' produced. Default value \code{draw = TRUE}.
#' @param params An optional \link[plotgardener]{pgParams} object containing
#' relevant function parameters.
#' @param ... Additional grid graphical parameters. See \link[grid]{gpar}.
#'
#' @return Returns a list of \code{signal} objects containing relevant
#' genomic region, placement, and \link[grid]{grob} information for each signal
#' track.
#'
#' @examples
#' library("plotgardenerData")
#' data("GM12878_ChIP_CTCF_signal")
#' data("IMR90_ChIP_CTCF_signal")
#' data("GM12878_ChIP_H3K27ac_signal")
#' data("IMR90_ChIP_H3K27ac_signal")
#' library("RColorBrewer")
#'
#' ## List of multiple signal datasets
#' signalList <- list(GM12878_ChIP_CTCF_signal, GM12878_ChIP_H3K27ac_signal,
#' IMR90_ChIP_CTCF_signal, IMR90_ChIP_H3K27ac_signal)
#'
#' ## Create page
#' pageCreate(width = 6.9, height = 3.5, default.units = "inches")
#'
#' ## Plot multiple signals
#' multisignal <- plotMultiSignal(signalList, chrom = "chr21",
#' chromstart = 28150000, chromend = 29150000,
#' linecolor = c(brewer.pal(n = 9,"YlGnBu")[4],
#' brewer.pal(n = 9,"YlGnBu")[5],
#' brewer.pal(n = 9,"YlGnBu")[6],
#' brewer.pal(n = 9,"YlGnBu")[7]),
#' label = c("GM12878 CTCF", "GM12878 H3K27ac",
#' "IMR90 CTCF", "IMR90 H3K27ac"),
#' assembly = "hg19",
#' x = 0.2, y = 0.2,
#' width = 6.5, height = 3,
#' default.units = "inches",
#' gapdistance = 0.1)
#'
#' ## Plot genome label
#' plotGenomeLabel(
#' chrom = "chr21",
#' chromstart = 28150000, chromend = 29150000,
#' assembly = "hg19",
#' scale = "Kb",
#' x = 0.2, y = 3.25, length = 6.5,
#' default.units = "inches"
#' )
#'
#' ## Hide page guides
#' pageGuideHide()
#' @export
plotMultiSignal<- function(data,
binSize = NA,
binCap = TRUE,
negData = FALSE,
chrom,
chromstart = NULL,
chromend = NULL,
assembly = "hg38",
linecolor = "#37a7db",
fill = NA,
ymax = 1,
range = NULL,
scale = FALSE,
label = NULL,
bg = NA,
baseline = TRUE,
baseline.color = "grey",
baseline.lwd = 1,
orientation = "h",
x = NULL,
y = NULL,
width = NULL,
height = NULL,
just = c("left", "top"),
gapdistance = 0.2,
default.units = "inches",
draw = TRUE,
params = NULL, ...) {
# =========================================================================
# FUNCTIONS
# =========================================================================
## Define a function to fill in colors
setColors <- function(colorList, nTracks){
# exit recursive with list of colors of the correct length
if (length(colorList) == nTracks){
return(colorList)
}
# if there are too few colors for the number of tracks,
# repeat the colors provided
else if (length(colorList) < nTracks){
setColors(append(colorList, colorList), nTracks)
}
# if there are too many colors for the number of tracks,
# truncate the color list
else if(length(colorList) > nTracks){
return(colorList[seq(1, nTracks)])
}
}
## Define a function to calculate x coordinates of each track
getXCoordinates <- function(x, nTracks, width, orientation, gapdistance){
# create a list for the x coordinates
xList <- rep(x, nTracks)
# if the orientation is h, the x coordinates stay the same, so return
# a list of the same x
if (orientation == "h"){
return(xList)
}
else if (orientation == "v"){
# calculating the width of each plot
internalWidth <- (width - (gapdistance * (nTracks-1)))/nTracks
# first plot won't change x, others are shifting to the right
for (i in 2:nTracks){
xList[i]<- (xList[i-1] + internalWidth + gapdistance)
}
return(xList)
}
}
## Define a function to calculate y coordinates of each track
getYCoordinates <- function(y, nTracks, height, orientation, gapdistance){
# create a list for the y coordinates
yList <- rep(y, nTracks)
# if the orientation is v, the y coordinates stay the same, so return
# a list of the same y
if (orientation == "v"){
return(yList)
}
else if (orientation == "h"){
# calculating the height of each plot
internalHeight <- (height - (gapdistance * (nTracks-1)))/nTracks
# first plot won't change y, others are shifting down
for (i in 2:nTracks){
yList[i]<- (yList[i-1] + internalHeight + gapdistance)
}
return(yList)
}
}
## Define a function to get and add a signal gtree to one multisignal gtree
getGrobs <- function(object){
assign("pg_multiSignals",
addGrob(
gTree = get("pg_multiSignals", envir = pgEnv),
child = object$grobs
),
envir = pgEnv
)
}
# =========================================================================
# PARSE PARAMETERS
# =========================================================================
multisigInternal <- parseParams(
params = params,
defaultArgs = formals(eval(match.call()[[1]])),
declaredArgs = lapply(match.call()[-1], eval.parent, n = 2),
class = "multisigInternal"
)
## Set gp
multisigInternal$gp <- setGP(
gpList = gpar(),
params = multisigInternal, ...
)
## Justification
multisigInternal$just <- justConversion(just = multisigInternal$just)
# =========================================================================
# CATCH ERRORS
# =========================================================================
if (is.null(multisigInternal$data)) stop("argument \"orientation\" is",
"missing, with no default.", call. = FALSE)
# =========================================================================
# PARSE ASSEMBLY
# =========================================================================
multisigInternal$assembly <- parseAssembly(assembly =
multisigInternal$assembly)
# =========================================================================
# PARSE UNITS
# =========================================================================
multisigInternal <- defaultUnits(
object = multisigInternal,
default.units = multisigInternal$default.units
)
multisigInternal$gapdistance <-
misc_defaultUnits(value = multisigInternal$gapdistance,
name = "gapdistance",
default.units = multisigInternal$default.units)
# =========================================================================
# READ IN FILES/DATAFRAMES AND SET RANGE
# =========================================================================
signal <- lapply(multisigInternal$data, read_rangeData,
assembly = multisigInternal$assembly,
chrom = multisigInternal$chrom,
start = multisigInternal$chromstart,
end = multisigInternal$chromend)
range <- calcSignalRange(data = signal,
chrom = multisigInternal$chrom,
chromstart = multisigInternal$chromstart,
chromend = multisigInternal$chromend,
assembly = multisigInternal$assembly,
negData = multisigInternal$negData)
# =========================================================================
# SET PLACEMENT COORDINATES, COLORS, AND LABELS
# =========================================================================
nTracks <- length(multisigInternal$data)
xList <- getXCoordinates(x = multisigInternal$x,
nTracks = nTracks,
width = multisigInternal$width,
orientation = multisigInternal$orientation,
gapdistance = multisigInternal$gapdistance)
yList <- getYCoordinates(y = multisigInternal$y,
nTracks = nTracks,
height = multisigInternal$height,
orientation = multisigInternal$orientation,
gapdistance = multisigInternal$gapdistance)
if (multisigInternal$orientation == "h"){
multisigInternal$height <- (multisigInternal$height -
(multisigInternal$gapdistance * (nTracks-1)))/nTracks
} else if (multisigInternal$orientation == "v"){
multisigInternal$width <- (multisigInternal$width -
(multisigInternal$gapdistance * (nTracks-1)))/nTracks
}
linecolorList <- setColors(multisigInternal$linecolor, nTracks)
fillList <- setColors(multisigInternal$fill, nTracks)
if (is.null(multisigInternal$label)){
#multisigInternal$label <- rep(multisigInternal$label, nTracks)
multisigInternal$label <- vector(mode = "list", length = nTracks)
}
# =========================================================================
# CALL PLOTSIGNAL
# =========================================================================
## Save to a group object?
sigObjects <- purrr::pmap(list(multisigInternal$data, xList, yList,
linecolorList, fillList, rep(FALSE, nTracks),
multisigInternal$label),
\(d, x, y, l, f, draw, lab){
suppressMessages(plotSignal(data = d, x = x, y = y,
linecolor = l,
height = multisigInternal$height,
width = multisigInternal$width,
range = range,
orientation = multisigInternal$orientation,
scale = multisigInternal$scale,
label = lab,
binSize = multisigInternal$binSize,
binCap = multisigInternal$binCap,
negData = multisigInternal$negData,
chrom = multisigInternal$chrom,
chromstart = multisigInternal$chromstart,
chromend = multisigInternal$chromend,
assembly = multisigInternal$assembly,
fill = f,
ymax = multisigInternal$ymax,
bg = multisigInternal$bg,
baseline = multisigInternal$baseline,
baseline.color = multisigInternal$baseline.color,
baseline.lwd = multisigInternal$baseline.lwd,
just = multisigInternal$just,
default.units = multisigInternal$default.units,
draw = draw))
})
# =========================================================================
# GET AND DRAW GROBS
# =========================================================================
name <- paste0(
"multisignal",
length(grep(
pattern = "multisignal",
x = grid.ls(
print = FALSE,
recursive = FALSE
)
)) + 1
)
assign("pg_multiSignals", gTree(name = name), envir = pgEnv)
invisible(lapply(sigObjects, getGrobs))
sigGrobs <- get("pg_multiSignals", envir = pgEnv)
# Call grid.draw to draw all these grobs at once
if (multisigInternal$draw == TRUE){
grid.draw(sigGrobs)
}
# =========================================================================
# RETURN OBJECT
# =========================================================================
message("multisignal[", name, "]")
invisible(sigObjects)
}
#' Calculate a score range for multiple signals
#'
#' @usage calcSignalRange(
#' data,
#' chrom = NULL,
#' chromstart = 1,
#' chromend = .Machine$integer.max,
#' assembly = "hg38",
#' negData = FALSE)
#'
#' @param data List of data to be plotted as character values specifying
#' multiple bigwig file paths, dataframes in BED format, or
#' \link[GenomicRanges]{GRanges} objects with metadata column \code{score}.
#' @param chrom Chromosome of data region ragne as a string, if range for a
#' specific chromosome is desired.
#' @param chromstart Integer start position on chromosome to get data range.
#' @param chromend Integer end position on chromosome to get data range.
#' @param assembly Default genome assembly as a string or a
#' \link[plotgardener]{assembly} object.
#' Default value is \code{assembly = "hg38"}.
#' @param negData A logical value indicating whether any of the data has both
#' positive and negative scores and the signal range should be adjusted
#' accordingly. Default value is \code{negData = FALSE}.
#'
#' @return Returns a vector of length 2 with the calculated c(min, max) range.
#'
#' @examples
#' library("plotgardenerData")
#' data("GM12878_ChIP_CTCF_signal")
#' data("IMR90_ChIP_CTCF_signal")
#' data("GM12878_ChIP_H3K27ac_signal")
#' data("IMR90_ChIP_H3K27ac_signal")
#'
#' calcSignalRange(data = list(GM12878_ChIP_CTCF_signal,
#' GM12878_ChIP_H3K27ac_signal,
#' IMR90_ChIP_CTCF_signal,
#' IMR90_ChIP_H3K27ac_signal),
#' chrom = "chr21",
#' chromstart = 28150000, chromend = 29150000,
#' assembly = "hg38", negData = FALSE)
#'
#' @export
calcSignalRange <- function(data, chrom = NULL, chromstart = 1,
chromend = .Machine$integer.max,
assembly = "hg38", negData = FALSE){
# Read and process data with read_rangeData()
data <- lapply(data, read_rangeData,
assembly = assembly,
chrom = chrom,
start = chromstart,
end = chromend)
# calculating the max from the processed data scores
rangeMax <- lapply(data, dplyr::select, "score") %>%
lapply(max) %>%
unlist() %>%
max
# if no negative data, lower bound of range = 0
if (negData == FALSE){
rangeMin <- 0
}
# if there are negative values, lower bound of range is the min value
else {
rangeMin <- lapply(data, dplyr::select, "score") %>%
lapply(min) %>%
unlist() %>%
min
}
return(c(rangeMin, rangeMax))
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.