This vignette demonstrates how to reproduce the figures presented in our manuscript ‘CEV: A Tool for Standardized Cancer Evolution Visualization’ using the CEV package. We will walk through the generation of each figure using example datasets provided with the package. The code snippets below show how to create customized phylogenetic trees, CCF heatmaps, and other visualizations that highlight different aspects of tumor evolution. Users can easily adapt these examples to their own datasets by following the same workflow and customization options.
ID | SNV.id | CCF | clone.id | chr | pos |
---|---|---|---|---|---|
LN1 | 10_102747966_G_A | 1.08 | MRCA | 10 | 102747966 |
LN2 | 10_102747966_G_A | 1.25 | MRCA | 10 | 102747966 |
LN3 | 10_102747966_G_A | 1.35 | MRCA | 10 | 102747966 |
R1 | 10_102747966_G_A | 1.16 | MRCA | 10 | 102747966 |
R2 | 10_102747966_G_A | 0.87 | MRCA | 10 | 102747966 |
R3 | 10_102747966_G_A | 1.19 | MRCA | 10 | 102747966 |
parent | label | length.1 | length.2 | spatial | CP | node.id | node.col | |
---|---|---|---|---|---|---|---|---|
MRCA | NA | MRCA | 145 | 2 | Shared | 1.00 | MRCA | #a90000 |
4 | MRCA | 4 | 49 | NA | Metastasis | 0.96 | 4 | #f48004 |
13 | 4 | 13 | 7 | NA | Metastasis | 0.77 | 13 | #05f8f2 |
24 | 13 | 24 | 6 | NA | Metastasis | 0.23 | 24 | #f43ded |
# Calculate median CCF per sample
median.ccf <- aggregate(CCF ~ ID + clone.id, data = snv, FUN = median);
names(median.ccf)[names(median.ccf) == 'CCF'] <- 'median.ccf.per.sample';
# Calculate total unique SNV.id per sample
total.nsnv <- aggregate(
SNV.id ~ ID + clone.id,
data = snv,
FUN = function(x) length(unique(x))
);
names(total.nsnv)[names(total.nsnv) == 'SNV.id'] <- 'total.nsnv';
# Merge the results back to the original data frame
snv <- merge(snv, median.ccf, by = c('ID', 'clone.id'));
snv <- merge(snv, total.nsnv, by = c('ID', 'clone.id'));
create.ccf.summary.heatmap(
filename = 'figures/1C_CCF-summary-heatmap.png',
DF = snv,
ccf.limits = c(0, 1),
clone.colours = clone.col,
clone.order = levels(snv$clone.id),
sample.order = levels(snv$ID),
y.spacing = c(-0.5, -2.5),
xlab.axis.padding = c(0, 0, -2),
plot.objects.heights = c(0.3, 1, 0.25),
height = 7,
width = 11
);
selected.clone <- c('1', subset(multi.phylogeny, spatial %in% c('Shared', 'Metastasis'))$label)
sub.dt <- droplevels(snv[snv$clone.id %in% selected.clone, ]);
create.clone.genome.distribution.plot(
filename = 'figures/1D_clone-genome-distribution.png',
snv.df = sub.dt,
clone.order = levels(sub.dt$clone.id),
clone.colours = clone.col,
legend.y = 0.47,
legend.x = 0.1,
y.spacing = -0.5,
alpha = 0.6,
ylab.axis.padding = c(-1),
width = 26,
);
## [1] "Plotting clone distribution across the genome for sample: all"
## list()
sample.list <- c('LN1', 'LN2', 'R2', 'R7');
cp.dt <- aggregate(CCF ~ clone.id + ID, data = snv, FUN = function(x) median(x, na.rm = TRUE))
names(cp.dt) <- c('label', 'ID', 'node.size');
for (s in sample.list) {
temp.dt <- merge(
x = phy.dt,
y = cp.dt[cp.dt$ID == s, ],
by = 'label',
all.x = TRUE
);
temp.dt$node.id <- temp.dt$label;
idx <- !temp.dt$node.size > 0;
temp.dt$edge.type.1[idx] <- 'dotted';
temp.dt$edge.width.1[idx] <- 1;
temp.dt$node.size[idx] <- 0.8;
temp.dt$node.col[idx] <- 'white';
temp.dt$node.label.col[idx] <- 'grey';
temp.dt$border.type[idx] <- 'dotted';
temp.dt$label[temp.dt$node.size <= 0.5] <- '';
create.phylogenetic.tree(
filename = paste0('figures/2A_', s, '-phylogeny.png'),
temp.dt,
add.normal = TRUE,
scale1 = 2,
height = 7,
width = 7
);
}
text.data <- data.frame(
node = c('MRCA', 'MRCA', 4, 13, 'MRCA'),
name = c('chr1q gain', 'chr12q loss', 'chr2p gain', 'MYC SNV', 'TP53 SNV'),
col = c('blue', 'red', 'blue', 'black', 'black'),
fontface = c(rep('plain', 3), 'bold', 'bold')
);
create.phylogenetic.tree(
filename = 'figures/2B_annot-single-sample-phylogeny.png',
single.dt,
add.normal = TRUE,
polygon.colour.scheme = c(NA, single.dt$node.col),
node.text = text.data,
node.text.line.dist = 0.6,
polygon.scale = 2.5,
height = 5,
width = 3
);
## SRCGrob[GRID.SRCGrob.328]
create.phylogenetic.tree(
filename = 'figures/2C_mutation-timeline-phylogeny.png',
single.dt,
scale1 = 3,
yaxis1.label = 'Number of SNVs',
horizontal.padding = -0.3,
node.text.line.dist = 0.2,
node.text.cex = 1.2,
add.normal = TRUE,
node.text = text.data,
polygon.scale = 2.5,
height = 10,
width = 6
);
parent | label | length.1 | length.2 | node.col | edge.col.1 | edge.col.2 | edge.type.2 | |
---|---|---|---|---|---|---|---|---|
MRCA | NA | MRCA | 145 | 2 | #a90000 | steelblue | black | dotted |
2 | MRCA | 2 | 28 | NA | #f42404 | steelblue | black | dotted |
11 | 2 | 11 | 24 | NA | #16a149 | steelblue | black | dotted |
4 | MRCA | 4 | 49 | NA | #f48004 | steelblue | black | dotted |
5 | 3 | 5 | 11 | NA | #f2a123 | steelblue | black | dotted |
6 | 5 | 6 | 6 | 1 | #d7c454 | steelblue | black | dotted |
7 | 3 | 7 | 10 | NA | #90de3a | steelblue | black | dotted |
8 | 3 | 8 | 5 | NA | #5ecf1c | steelblue | black | dotted |
9 | 7 | 9 | 5 | NA | #37a400 | steelblue | black | dotted |
10 | 3 | 10 | 6 | 2 | #158800 | steelblue | black | dotted |
3 | 2 | 3 | 22 | 2 | #df6b0b | steelblue | black | dotted |
12 | 4 | 12 | 9 | NA | #1ecfa9 | steelblue | black | dotted |
13 | 4 | 13 | 7 | NA | #05f8f2 | steelblue | black | dotted |
14 | 8 | 14 | 7 | NA | #00daff | steelblue | black | dotted |
15 | 4 | 15 | 5 | NA | #00abff | steelblue | black | dotted |
16 | 11 | 16 | 21 | 1 | #007dff | steelblue | black | dotted |
17 | 3 | 17 | 36 | NA | #004aff | steelblue | black | dotted |
18 | 7 | 18 | 17 | 1 | #1922ff | steelblue | black | dotted |
19 | 16 | 19 | 7 | NA | #5703ff | steelblue | black | dotted |
20 | 10 | 20 | 13 | NA | #8600ff | steelblue | black | dotted |
21 | 6 | 21 | 8 | NA | #ab19ff | steelblue | black | dotted |
22 | 9 | 22 | 17 | NA | #bd4cff | steelblue | black | dotted |
23 | 17 | 23 | 8 | NA | #de56fc | steelblue | black | dotted |
24 | 13 | 24 | 6 | NA | #f43ded | steelblue | black | dotted |
25 | 18 | 25 | 14 | NA | #ff00d1 | steelblue | black | dotted |
create.phylogenetic.tree(
filename = 'figures/2D_radial-phylogeny.png',
phy.dt,
add.normal = TRUE,
scale1 = 1.5,
scale2 = 1.5,
yaxis1.label = 'Number of SNVs',
yaxis2.label = 'Number of CNAs',
scale.bar = TRUE,
scale.bar.coords = c(0.25, 0.85),
ylab.cex = 1.4,
yaxis.cex = 1.3,
scale.size.1 = 20,
height = 10,
width = 8
);
dend.dt <- phy.dt;
dend.dt$mode <- 'dendrogram';
dend.dt$spread <- 1.3;
create.phylogenetic.tree(
filename = 'figures/2E_dendrogram-phylogeny.png',
dend.dt,
add.normal = TRUE,
scale1 = 1.5,
scale2 = 1.5,
yaxis1.label = 'Number of SNVs',
yaxis2.label = 'Number of CNAs',
scale.bar = TRUE,
scale.bar.coords = c(0.2, 0.85),
ylab.cex = 1.4,
yaxis.cex = 1.3,
scale.size.1 = 20,
height = 11,
width = 8
);
## SRCGrob[GRID.SRCGrob.360]
mixed.dt <- update.descendant.property(
phy.dt,
parent.id = c('3', '4'),
property = 'mode',
value = 'dendrogram'
);
mixed.dt <- update.descendant.property(
mixed.dt,
parent.id = '7',
property = 'mode',
value = 'radial'
);
mixed.dt$spread[mixed.dt$label == '4'] <- 1.7;
create.phylogenetic.tree(
filename = 'figures/2F_mixed-phylogeny.png',
mixed.dt,
add.normal = TRUE,
scale1 = 1.5,
scale2 = 1.5,
yaxis1.label = 'Number of SNVs',
yaxis2.label = 'Number of CNAs',
scale.bar = TRUE,
scale.bar.coords = c(0.25, 0.85),
ylab.cex = 1.4,
yaxis.cex = 1.3,
scale.size.1 = 20,
height = 11,
width = 8
);
## SRCGrob[GRID.SRCGrob.373]
phy.500 <- phylogeny.500;
phy.500$node.id <- phy.500$label;
phy.500$length.1 <- 1;
phy.500$length.2 <- 0;
phy.500$mode <- 'dendrogram';
node.list <- setNames(
c('navy', 'steelblue', 'orchid', 'maroon', 'seagreen', 'tomato'),
c('1', '7', '26', '53', '125', '204')
);
for (i in seq_along(node.list)) {
idx <- which(phy.500$label == names(node.list)[i]);
phy.500[idx, 'node.col'] <- node.list[i];
phy.500[idx, 'connector.col'] <- node.list[i];
phy.500 <- update.descendant.property(
phy.500,
parent.id = names(node.list)[i],
property = 'edge.col.1',
value = node.list[i]
);
}
phy.500[which(phy.500$edge.col.1 == 'tomato'), 'edge.type.1'] <- 'dotted';
phy.500[which(phy.500$edge.col.1 == 'seagreen'), 'length.2'] <- 1;
phy.500[which(phy.500$edge.col.1 == 'seagreen'), 'edge.col.2'] <- 'palegreen3';
phy.500[!(phy.500$label %in% names(node.list)), 'draw.node'] <- FALSE;
phy.500$label <- '';
create.phylogenetic.tree(
filename = 'figures/2G_phylogeny-500.png',
phy.500,
scale2 = 0.1,
horizontal.padding = 10,
add.normal = TRUE,
width = 23,
height = 9
);
## SRCGrob[GRID.SRCGrob.374]