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 |
plt <- create.cluster.heatmap(
filename = 'figures/1B_CCF-heatmap.png',
DF = snv,
ccf.limits = c(0, 1),
clone.colours = clone.col,
ylab.label = 'Sample',
ylab.cex = 1.2,
yaxis.cex = 1,
y.spacing = -0.2,
height = 7,
width = 11
);# 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,
);## 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]