Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add R scripts for clone density plot and histogram #143

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -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)
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
* 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

## Update
* Fixed angle calculation bug where child angles do not follow
their parent angle, instead moving "downward" at 0 degrees.
Expand Down
19 changes: 19 additions & 0 deletions R/calculate.density.R
Original file line number Diff line number Diff line change
@@ -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, ...);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the reusable wrapper function around the built-in here.

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)
Copy link
Collaborator

@dan-knight dan-knight Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semicolon after the return statement.

}
115 changes: 115 additions & 0 deletions R/create.ccf.densityplot.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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);
}

# calculate mean CCF and nsnv per cluster -----------------------------------------------------
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't need these comments.

mean.ccf <- aggregate(CCF ~ clone.id, data = x, FUN = mean);
nsnv <- aggregate(SNV.id ~ clone.id, data = x, FUN = length);

# calculate densities for each cluster --------------------------------------------------------
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));


# get plot legend -----------------------------------------------------------------------------
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(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double-checking. Are we returning the results of write.plot in the rest of the package, or just returning the BPG object (so here, returning scatter).

trellis.object = combn.plt,
filename = filename,
height = height,
width = width,
size.units = size.units,
resolution = resolution
));
}
16 changes: 1 addition & 15 deletions R/create.clone.genome.distribution.densityplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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)
}
5 changes: 3 additions & 2 deletions R/create.clone.genome.distribution.plot.R
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion R/create.cluster.heatmap.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ create.cluster.heatmap <- function(
xaxis.fontface = 'bold',
y.spacing = 1,
colour.scheme = c('white', 'blue'),
plot.objects.heights = c(1, 0.2),
...
) {

Expand Down Expand Up @@ -94,7 +95,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
)),
Expand Down
4 changes: 2 additions & 2 deletions R/create.phylogenetic.tree.R
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
51 changes: 51 additions & 0 deletions man/create.ccf.densityplot.Rd
Original file line number Diff line number Diff line change
@@ -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}}}
2 changes: 2 additions & 0 deletions man/create.cluster.heatmap.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ create.cluster.heatmap(
xaxis.fontface = 'bold',
y.spacing = 1,
colour.scheme = c('white', 'blue'),
plot.objects.heights = c(1, 0.2),
...
);
}
Expand All @@ -42,6 +43,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.}
Expand Down