diff --git a/NAMESPACE b/NAMESPACE index 121a915..820e916 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -24,6 +24,7 @@ export(create.ccf.heatmap) export(create.cluster.heatmap) export(create.ccf.summary.heatmap) export(create.clone.genome.distribution.plot) +export(create.ccf.densityplot) export(data.frame.to.array) export(update.descendant.property) diff --git a/NEWS.md b/NEWS.md index 858d82d..bcdf38d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,7 @@ * Add option to use scale bars instead of y-axes. * Wrapper function for `SRCgrob` to automatically save plots to file * Add option to annotate the CCF summary heatmap with the cell values. +* Function to generate single-sample density plot * Add support for 1xn and 1x1 heatmaps. * Add `get.colours.in.order` function to get a list of colours and corresponding clone ID order. * Add `sample.order` and `clone.order` as input parameters to `create.cluster.heatmap` @@ -36,6 +37,7 @@ * Resolved issue where the spread parameter was not applied in dendrogram mode. * Resolved issue for simple dendrogram trees ( < 6 nodes or binary tree), where node angles were not calculated correctly. + # CancerEvolutionVisualization 2.0.1 (2023-11-17) ## Added diff --git a/R/calculate.density.R b/R/calculate.density.R new file mode 100644 index 0000000..bf7a12c --- /dev/null +++ b/R/calculate.density.R @@ -0,0 +1,19 @@ +calculate.density <- function( + x, + value = 'genome.pos', + group = 'clone.id', + scale = TRUE, + ... + ) { + + if (nrow(x) <= 1) { + return(NULL); + } + density <- density(x = x[[value]], bw = 'nrd', na.rm = TRUE, ...); + density.df <- as.data.frame(density[c('x', 'y')]); + density.df$clone.id <- unique(x[[group]]); + if (scale) { + density.df$y <- nrow(x) / sum(density.df$y) * density.df$y; + } + return(density.df); + } diff --git a/R/create.ccf.densityplot.R b/R/create.ccf.densityplot.R new file mode 100644 index 0000000..c804fc1 --- /dev/null +++ b/R/create.ccf.densityplot.R @@ -0,0 +1,111 @@ +create.ccf.densityplot <- function( + x, + filename = NULL, + clone.colours = NULL, + breaks = 100, + xlab.label = 'CCF', + ylab.label = 'SNV Density', + xlimits = c(0, 1.5), + xat = seq(0, 1.5, 0.25), + legend.size = 3, + legend.title.cex = 1.2, + legend.label.cex = 1, + legend.x = 0.8, + legend.y = 0.9, + height = 6, + width = 10, + size.units = 'in', + resolution = 1000, + ... + ) { + + if (is.null(clone.colours)) { + clone.colours <- get.colours(x$clone.id, return.names = TRUE); + } + + mean.ccf <- aggregate(CCF ~ clone.id, data = x, FUN = mean); + nsnv <- aggregate(SNV.id ~ clone.id, data = x, FUN = length); + + density.list <- list(); + for (k in unique(x$clone.id)) { + density.list[[k]] <- calculate.density( + x = x[x$clone.id == k, ], + value = 'CCF', + adjust = 1, + scale = FALSE + ); + } + density.df <- do.call(rbind, density.list); + density.df$y <- density.df$y * (nsnv$SNV.id[match(density.df$clone.id, nsnv$clone.id)] / nrow(x)); + + legend.label <- sapply(names(clone.colours), function(k) { + nsnv <- nsnv[nsnv$clone.id == k, ]$SNV.id; + return(paste0(k, ' (', nsnv, ')')); + }); + clone.legend <- BoutrosLab.plotting.general::legend.grob( + list( + legend = list( + title = 'Clone (SNVs)', + labels = legend.label[names(clone.colours)], + colours = c(clone.colours), + border = 'black' + ) + ), + size = legend.size, + title.just = 'left', + title.cex = legend.title.cex, + label.cex = legend.label.cex + ); + + ymax <- ceiling(max(density.df$y, na.rm = TRUE)); + + hist <- BoutrosLab.plotting.general::create.histogram( + x = x$CCF, + type = 'density', + col = 'gray90', + border.col = 'gray30', + lwd = 0.1, + xlab.label = xlab.label, + ylab.label = ylab.label, + xlimits = xlimits, + xat = xat, + ylimits = c(-0.05, 1.05) * ymax, + legend = list(inside = list( + fun = clone.legend, + x = legend.x, + y = legend.y + )), + ... + ); + + scatter <- BoutrosLab.plotting.general::create.scatterplot( + formula = y ~ x, + data = density.df, + groups = density.df$clone.id, + type = 'l', + lwd = 3, + col = clone.colours, + xlimits = xlimits, + ylimits = c(-0.05, 1.05) * ymax, + abline.v = mean.ccf$CCF, + abline.lwd = 0.5, + abline.lty = 'longdash', + abline.col = 'gray50', + add.text = TRUE, + text.labels = lapply(mean.ccf$CCF, round, 2), + text.x = mean.ccf$CCF, + text.y = ymax, + text.fontface = 'bold', + text.cex = legend.title.cex + ); + + combn.plt <- hist + scatter; + return(BoutrosLab.plotting.general::write.plot( + trellis.object = combn.plt, + filename = filename, + height = height, + width = width, + size.units = size.units, + resolution = resolution + )); + } diff --git a/R/create.clone.genome.distribution.densityplot.R b/R/create.clone.genome.distribution.densityplot.R index 05f8bcd..3bf4c3f 100644 --- a/R/create.clone.genome.distribution.densityplot.R +++ b/R/create.clone.genome.distribution.densityplot.R @@ -8,7 +8,7 @@ create.clone.genome.distribution.densityplot <- function( return(BoutrosLab.plotting.general::create.scatterplot( filename = save.plt, - formula = count ~ x, + formula = y ~ x, data = density.df, groups = density.df$clone.id, xlab.label = 'Chromosome', @@ -24,17 +24,3 @@ create.clone.genome.distribution.densityplot <- function( ... )); } - -calculate.density.and.scale <- function(cluster.df) { - # density should be generated using unque SNV count - density <- density( - x = cluster.df$genome.pos, - bw = 'nrd', - adjust = 0.05, # set to 1E9/3E9 to get density per megabase - na.rm = TRUE); - density.df <- as.data.frame(density[c('x','y')]); - density.df$clone.id <- unique(cluster.df$clone.id); - density.df$count <- nrow(cluster.df) / sum(density.df$y) * density.df$y; - - return(density.df) - } diff --git a/R/create.clone.genome.distribution.plot.R b/R/create.clone.genome.distribution.plot.R index 9892003..dbc52fe 100644 --- a/R/create.clone.genome.distribution.plot.R +++ b/R/create.clone.genome.distribution.plot.R @@ -104,8 +104,9 @@ create.clone.genome.distribution.plot.per.sample <- function( warning(paste('Skipping clone', k, 'in sample', unique(sample.df$ID), 'since there is only one SNV')); next; } - density.list[[k]] <- calculate.density.and.scale( - cluster.df = sample.df[sample.df$clone.id == k, ] + density.list[[k]] <- calculate.density( + x = sample.df[sample.df$clone.id == k, ], + adjust = 0.05 ); } density.df <- do.call(rbind, density.list); diff --git a/R/create.cluster.heatmap.R b/R/create.cluster.heatmap.R index 672bd39..c6f87b9 100644 --- a/R/create.cluster.heatmap.R +++ b/R/create.cluster.heatmap.R @@ -17,6 +17,7 @@ create.cluster.heatmap <- function( xaxis.fontface = 'bold', y.spacing = 1, colour.scheme = c('white', 'blue'), + plot.objects.heights = c(1, 0.2), ... ) { @@ -112,7 +113,7 @@ create.cluster.heatmap <- function( plot.objects = list(hm, cov), layout.width = 1, layout.height = 2, - plot.objects.heights = c(1, 0.2), + plot.objects.heights = plot.objects.heights, legend = list(right = list( fun = legend.clone )), diff --git a/R/create.phylogenetic.tree.R b/R/create.phylogenetic.tree.R index 848e71a..e4b6154 100644 --- a/R/create.phylogenetic.tree.R +++ b/R/create.phylogenetic.tree.R @@ -12,10 +12,10 @@ create.phylogenetic.tree <- function( tree <- as.data.frame(tree); if ('node.id' %in% colnames(tree)) { rownames(tree) <- tree$node.id; - if (!'label' %in% colnames(tree)) { + if (!'label' %in% colnames(tree)) { tree$label <- tree$node.id; } - } + } plt <- SRCGrob( tree, diff --git a/man/create.ccf.densityplot.Rd b/man/create.ccf.densityplot.Rd new file mode 100644 index 0000000..30ff75d --- /dev/null +++ b/man/create.ccf.densityplot.Rd @@ -0,0 +1,51 @@ +\name{create.ccf.densityplot} +\alias{create.ccf.densityplot} +\title{CCF Density Plot} +\description{ +Creates a density plot of cancer cell fraction (CCF) distribution across a sample. +} +\usage{ +create.ccf.densityplot( + x, + filename = NULL, + clone.colours = NULL, + breaks = 100, + xlab.label = 'CCF', + ylab.label = 'SNV Density', + xlimits = c(0, 1.5), + xat = seq(0, 1.5, 0.25), + legend.size = 3, + legend.title.cex = 1.2, + legend.label.cex = 1, + legend.x = 0.8, + legend.y = 0.9, + height = 6, + width = 10, + size.units = 'in', + resolution = 1000, + ... + ); +} +\arguments{ + \item{x}{A data-frame with the following column names: 'SNV.id', 'clone.id', 'CCF'.} + \item{filename}{Filename for tiff output, or if NULL returns the trellis object itself. Defaults to \code{NULL}.} + \item{clone.colours}{Named list to provide a colour scheme for the clone ID covariate bar. If NULL, colours will be randomly generated. Defaults to \code{NULL}.} + \item{breaks}{Number of breaks for the histogram. Defaults to 100.} + \item{xlab.label}{Defaults to \dQuote{CCF}.} + \item{ylab.label}{Defaults to \dQuote{SNV Density}.} + \item{xlimits}{Limits for the x-axis. Defaults to \code{c(0, 1.5)}.} + \item{xat}{Positions for the x-axis labels. Defaults to \code{seq(0, 1.5, 0.25)}.} + \item{legend.size}{Width of the legend boxes in 'character' units. Defaults to 3} + \item{legend.title.cex}{Size of titles in the legends. Defaults to 1.2} + \item{legend.label.cex}{Size of text labels in the legends. Defaults to 1} + \item{legend.x}{x position of the legend. Defaults to 0.8} + \item{legend.y}{y position of the legend. Defaults to 0.9} + \item{height}{Height of the plot. Defaults to 6} + \item{width}{Width of the plot. Defaults to 10} + \item{size.units}{Units for the height and width. Defaults to \dQuote{in}.} + \item{resolution}{Resolution of the plot. Defaults to 1000} + \item{...}{Pass through argument. See BoutrosLab.plotting.general::create.histogram() for further details.} +} +\value{A `grob` object of the heatmap.} +\author{Helena Winata} +\seealso{\code{\link[BoutrosLab.plotting.general]{create.histogram}}, \code{\link[BoutrosLab.plotting.general]{create.scatterplot}}} diff --git a/man/create.cluster.heatmap.Rd b/man/create.cluster.heatmap.Rd index d9d7a74..097eec9 100644 --- a/man/create.cluster.heatmap.Rd +++ b/man/create.cluster.heatmap.Rd @@ -24,6 +24,7 @@ create.cluster.heatmap( xaxis.fontface = 'bold', y.spacing = 1, colour.scheme = c('white', 'blue'), + plot.objects.heights = c(1, 0.2), ... ); } @@ -46,6 +47,7 @@ create.cluster.heatmap( \item{xaxis.fontface}{Defaults to \dQuote{bold}.} \item{y.spacing}{Spacing between heatmap and clone covariate bar. Defaults to 1} \item{colour.scheme}{Colour scheme for the heatmap. Defaults to \code{c('white', 'blue')}.} + \item{plot.objects.heights}{Object heights. Defaults to \code{c(1, 0.2)}.} \item{...}{Pass through argument. See BoutrosLab.plotting.general::create.heatmap() for further details.} } \value{A `grob` object of the heatmap.}