CanvasXpressBio connects the CanvasXpress interactive
visualization library to core Bioconductor data structures. It does not
replace the CRAN canvasXpress package — it adds methods
that take a Bioconductor object directly and render it as a reproducible
CanvasXpress widget, carrying the object’s annotations along for
interactive grouping, coloring and sorting.
This vignette builds one
SummarizedExperiment and shows it four ways — heatmap,
volcano plot, Kaplan-Meier curve and paired boxplot — so the same
samples and genes run through every figure.
Bioconductor already has excellent visualization packages. Static
heatmaps come from ComplexHeatmap and
pheatmap; EnhancedVolcano draws volcano plots;
survminer draws Kaplan-Meier curves; and
InteractiveComplexHeatmap adds interactivity to a
ComplexHeatmap — but through a Shiny application, which
needs a running R server to explore the figure.
CanvasXpressBio is complementary rather than a
replacement, and is useful when:
InteractiveComplexHeatmap is the better choice inside a
Shiny app; CanvasXpressBio is the better choice for a
standalone, shareable HTML artefact.SummarizedExperiment drives the heatmap, volcano,
survival curve and boxplot below through four small functions, so the
colData / rowData annotations are reused
consistently instead of being reshaped for a different package per
plot.colData becomes sample overlays and
rowData becomes feature overlays, so cohort, time point and
gene direction are available for interactive colouring and sorting
without extra bookkeeping.The rest of this vignette demonstrates these four views on a single object.
The study follows 24 patients in two cohorts, each measured at
baseline and at week 12, over a panel of twelve marker genes. The
colData records the cohort, the time point, the subject, a
follow-up time with an event indicator, and whether that subject’s
biomarker went up or down; the rowData records whether each
gene is an up- or down-regulated marker.
To keep the figures below uncluttered, the data construction lives in
a single helper, makeDemoSE(), so the vignette builds the
whole object with one call. The derived pieces the other figures need —
a differential-expression table for the volcano and the per-cohort
paired t-test p-values for the boxplot — travel with the object in its
metadata(). (See ?makeDemoSE for exactly how
the object is assembled.)
se <- makeDemoSE()
se
#> class: SummarizedExperiment
#> dim: 12 48
#> metadata(2): deResults albPaired
#> assays(1): expr
#> rownames(12): IL6 CXCL8 ... ADIPOQ LEP
#> rowData names(1): Direction
#> colnames(48): PA01_BL PA01_W12 ... PB12_BL PB12_W12
#> colData names(6): Patient Condition ... Status updowncxplot() turns the assay matrix into an interactive
heatmap. colData becomes sample (column) annotations and
rowData becomes feature (row) annotations, so the cohort,
time point and gene direction travel with the plot.
overlays and var_overlays draw chosen
annotations as coloured tracks beside the map, and
cluster = TRUE clusters the rows and columns with
dendrograms. These are the same twelve genes highlighted in the volcano
plot below.
cxplot(se, title = "Marker-gene expression",
overlays = c("Cohort", "Condition"), var_overlays = "Direction",
cluster = TRUE)Enriching the hover metadata. The annotations are
not only drawn as the coloured tracks beside the map — they also travel
into every cell’s tooltip. Hovering a cell shows its expression value
together with the gene and sample it belongs to and the
colData / rowData fields listed in
overlays / var_overlays (here the cohort, time
point and gene direction). To surface more metadata on hover, a user
simply adds more columns to the object’s colData or
rowData and names them in overlays /
var_overlays — no extra wiring.
Resetting the view. After zooming (mouse wheel) or a two-finger pinch, the toolbar’s reset button — and a double-click anywhere on the plot — restores the original view, the same role Plotly’s home control plays.
Because SingleCellExperiment and
DESeqDataSet both extend SummarizedExperiment,
the same method renders them without any extra code.
cxvolcano() accepts any result table with a
log-fold-change and a p-value column — a DESeq2::results()
object, a limma::topTable() data frame, or a plain data
frame. Here week 12 is contrasted against baseline; the twelve marker
genes from the heatmap are the labelled hits. The result table (the
twelve markers among a genome-wide background) was computed in
makeDemoSE() and rides along in the object’s
metadata().
cxsurvival() reads the follow-up time and event status
straight from colData. Using the baseline rows gives one
record per patient, stratified by cohort.
The curve is interactive, and the interactivity carries the
per-subject information: tooltip names extra
colData columns to attach to each subject, so hovering a
step or a censored observation shows that patient’s metadata.
Censored observations are drawn as crosshair marks on the curve and,
with kmCensoredRug = TRUE, as a rug along the time axis;
the risk table (“Subjects at Risk”) and the log-rank p-value are shown
as well.
cxboxplot() plots one marker across the two time points,
draws each patient’s points, and joins the repeated measures with a line
coloured by the direction of change — faceted by cohort.
The per-cohort paired t-test p-values were computed in
makeDemoSE() and read back from metadata() to
label each panel.
paired <- S4Vectors::metadata(se)$albPaired
ptext <- function(p) if (p < 1e-4) "p<0.0001" else sprintf("p=%.3g", p)
ytop <- max(assay(se)["ALB", ]) + 0.3
cxboxplot(se, feature = "ALB", group = "Condition", segregate = "Cohort",
connect = "Patient", color = "updown",
title = "ALB over time, per patient",
colorKey = list(updown = list(
up = "rgb(224,130,20)",
down = "rgb(58,102,160)")),
decorations = list(text = list(
list(value = ytop, label = ptext(paired[["Cohort A"]]), panel = 1,
smpCenter = TRUE, preTransformed = TRUE),
list(value = ytop, label = ptext(paired[["Cohort B"]]), panel = 2,
smpCenter = TRUE, preTransformed = TRUE))))The paired boxplot above draws every observation because the design
is small. When a group holds many points, the points
argument switches the overlay to a sina / beeswarm layout that packs the
points inside the distribution envelope rather than overplotting.
makeBeeswarmDemo() returns a single-feature object with 500
observations in each of five groups (2,500 points);
points = "sina" keeps it legible.
sessionInfo()
#> R version 4.6.2 (2026-10-29)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] SummarizedExperiment_1.43.0 Biobase_2.73.2
#> [3] GenomicRanges_1.65.4 Seqinfo_1.3.2
#> [5] IRanges_2.47.5 S4Vectors_0.51.10
#> [7] BiocGenerics_0.59.13 generics_0.1.4
#> [9] MatrixGenerics_1.25.0 matrixStats_1.5.0
#> [11] CanvasXpressBio_0.99.6 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] Matrix_1.7-6 jsonlite_2.0.0 compiler_4.6.2
#> [4] BiocManager_1.30.27 jquerylib_0.1.4 yaml_2.3.12
#> [7] fastmap_1.2.0 lattice_0.23-1 XVector_0.53.0
#> [10] R6_2.6.1 S4Arrays_1.13.2 knitr_1.52
#> [13] htmlwidgets_1.6.4 DelayedArray_0.39.8 maketools_1.3.2
#> [16] bslib_0.12.0 rlang_1.3.0 cachem_1.1.0
#> [19] xfun_0.62 canvasXpress_1.70.3 sass_0.4.10
#> [22] sys_3.4.3 otel_0.3.0 SparseArray_1.13.4
#> [25] cli_3.6.6 withr_3.0.3 digest_0.6.39
#> [28] grid_4.6.2 lifecycle_1.0.5 evaluate_1.0.5
#> [31] buildtools_1.0.0 abind_1.4-8 rmarkdown_2.32
#> [34] tools_4.6.2 htmltools_0.5.9