Dimensionality reduction plots such as TSNE and UMAP are commonly used to explore single-cell datasets. These plots are useful to represent the global structure of the data (represented by the positions of the points) usually accompanied by a variable such as gene expression, clustering, cell-type or QC values which are represented in the color of the points. However, the traditional approach to to these plots is limited to a single variable and we cannot respresent at the same time the expression of a gene while keeping the distribution of samples, cell types or clusters visible as a reference.
scOverlay is designed to solve this by creating
multi-layered plots for single-cell data. Using a simple layered
representation, it creates a background layer colored according to a
given variable (e.g. cell-type) and then draws the foreground variable
on top using an independent colour scale (e.g. gene-expression). An
optional dimming value partially mutes the background layer so it does
not interfere with the correct interpretation of the foreground one.
Therefore, the background layer provides the context and the foreground layer is used to highlight the variable of interest, such as the expression of a gene, a score, a cell annotation…
A key additional feature of scOverlay is that subsetting
can be applied to the foreground layer. This means that we can highlight
the cells from a specific sample, sample type or condition while still
showing the complete embedding in the background. This is useful when
comparing where different groups of cells are located in the same
reduced dimension space and is really helpful in identifying composition
differences in related samples or sample types.
scOverlay works with SingleCellExperiment
objects and only need the object to contain at least one reduced
dimension representation, such as "TSNE" or
"UMAP", and the variables to be plotted either in the
assays or in colData.
The package is focused on visualizing already processed single-cell data and not intended to perform single-cell preprocessing, normalization or dimensionality reduction, which can be done with of the the many Bioconductor packages for single-cell data processing.
scOverlay can be installed with BiocManager
following the standard procedure which will install
scOverlay and all its dependencies:
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("scOverlay")After installation, the package can be loaded with:
We will start by loading the package and the example data included (more information in the next section).
and we can view the content of the example data, a
SingleCellExperiment object created from a series of 10X
scRNAseq experiments, with 22K cells and 76 genes (more details in a
later section).
sce_mpnst_example
#> class: SingleCellExperiment
#> dim: 76 22285
#> metadata(0):
#> assays(1): logcounts
#> rownames(76): NF1 CDKN2A ... EEF1A1 TUBB
#> rowData names(2): ID Symbol
#> colnames(22285): nerve1_TGGCTATAGACAACGA-1 nerve1_AGGCGGATCGTACCGA-1
#> ... 51MPNST2E_GGCCTAATCCTAAATG-1 51MPNST2E_GTACTTCGTTTAAAGC-1
#> colData names(11): lib_size n_detected ... celltype celltype.main
#> reducedDimNames(3): UMAP TSNE PCA
#> mainExpName: NULL
#> altExpNames(0):The simplest scOverlay plot shows all cells in the
background and overlays a foreground variable on top. In this first
example, the background shows the sample type annotation (tumor type in
this example) and the foreground shows the expression of
CDH19. In this case we also adjust the size of the
background points so they are clearly
plotOverlay(
sce = sce_mpnst_example,
foreground = "CDH19",
background = "sample_type",
bg_dimming = 0.7,
reduced_dim = "TSNE"
)By default, the examples in this vignette use the "TSNE"
reduced dimension. Other reduced dimensions can be selected with the
reduced_dim argument. For example, the same plot could be
drawn on the UMAP coordinates by setting
reduced_dim = "UMAP".
We can select different backgrounds too: a solid colour,
"none" for no visible background points, a gene or a cell
annotation. For example, cells can be coloured in the background by
cluster, while the foreground shows the doublet score
stored in colData(sce_mpnst_example)$scDblFinder.score. We
can also ajust multiple visual parameters, such as palettes, point
sizes, titles, etc…
plotOverlay(
sce = sce_mpnst_example,
reduced_dim = "UMAP",
foreground = "scDblFinder.score",
fg_palette = "viridis",
fg_point_size = 0.5,
background = "cluster",
bg_palette = "pastel",
bg_point_size = 4,
bg_dimming = 0.6,
bg_legend = FALSE,
title = "Doublet score over clusters"
)One of the main uses of scOverlay is to highlight only a
subset of cells while keeping the complete dataset visibke in the
background as context. Here we plot CDH19 expression only
in the cells from one sample. The background still contains all
cells.
plotOverlay(
sce = sce_mpnst_example,
foreground = "CDH19",
fg_point_size = 0.8,
background = "cluster",
reduced_dim = "TSNE",
fg_subset_cells = quote(sample == "38ANF1"),
title = "CDH19 in 50PNF",
bg_dimming = 0.9,
bg_legend = FALSE
)scOverlay includes an example dataset that we’ll use
throughout this vignette, including in the previous section.
data("sce_mpnst_example")
sce_mpnst_example
#> class: SingleCellExperiment
#> dim: 76 22285
#> metadata(0):
#> assays(1): logcounts
#> rownames(76): NF1 CDKN2A ... EEF1A1 TUBB
#> rowData names(2): ID Symbol
#> colnames(22285): nerve1_TGGCTATAGACAACGA-1 nerve1_AGGCGGATCGTACCGA-1
#> ... 51MPNST2E_GGCCTAATCCTAAATG-1 51MPNST2E_GTACTTCGTTTAAAGC-1
#> colData names(11): lib_size n_detected ... celltype celltype.main
#> reducedDimNames(3): UMAP TSNE PCA
#> mainExpName: NULL
#> altExpNames(0):The object sce_mpnst_example is a
SingleCellExperiment and is included with the package to
demonstrate the plotting functionality. It is a subset of a full
scRNAseq experiment from the single-cell MPNST progression dataset
deposited at the European Genome-phenome Archive under accession
EGAS50000001747. The included dataset only has a small
selection of genes and stripped-down cell and sample annotations.
The most important elements for scOverlay are the
reduced dimensions, the assays and the cell metadata, called
colData.
reducedDimNames(sce_mpnst_example)
#> [1] "UMAP" "TSNE" "PCA"
assayNames(sce_mpnst_example)
#> [1] "logcounts"
colnames(colData(sce_mpnst_example))
#> [1] "lib_size" "n_detected" "mito_percent"
#> [4] "sample" "sample_type" "sizeFactor"
#> [7] "scDblFinder.score" "scDblFinder.class" "cluster"
#> [10] "celltype" "celltype.main"The reduced dimensions define the coordinates used for plotting. In
this vignette we will use mainly use "TSNE" as the default
reduced dimension, but "UMAP" is also included and can be
plotted in the exact same way.
The assay names indicate where gene expression values are stored. The
examples below assume that the object contains a
"logcounts" assay, which is the default assay used by
plotOverlay() when the foreground is a gene.
The columns in colData(sce_mpnst_example) contain
cell-level annotations that can be used as background variables,
foreground variables or to select subsets of cells.
Finally, we can check that some of the genes used in the examples are present in the object:
The main idea behind scOverlay is to separate the
information used as context from the information we want to highlight.
The background layer shows the complete embedding. The foreground layer
is drawn on top and can represent a gene, a cell metadata column, a
score, etc in the whole dataset or a subset of cells.
As seen in the “Quick Start” section, a first example could be to plot the expression of a gene over the sample type annotation, in this case telling us from which tumor type come each cell.
plotOverlay(
sce = sce_mpnst_example,
foreground = "CDH19",
background = "sample_type",
reduced_dim = "TSNE"
)However, the foreground does not have to be a gene. It can also be a
column in colData(sce). for example the clusters, samples,
cell types, doublet scores, etc… The background can use the same data
sources, or a solid colour.
The foreground can also be restricted to a specific subset of cells. This makes it possible to ask questions such as where the cells from a particular sample type are located in the dimensionality reduction embedding, while still seeing the complete structure of the data in the background.
The following sections describe these components in more detail.
The foreground layer contains the variable we want to highlight while
the background provides the context. They can be a gene or feature in
rownames(sce) or a column in colData(sce), but
also "solid" for a fixed-colour selection or
"none" for no visible points.
When the foreground is a gene, expression values are taken from the
assay specified by fg_assay. By default,
plotOverlay() uses the "logcounts" assay. The
same happens for background with bg_assay.
The data layers (foreground and background) can also show cell
annotations in colData(sce). Typically, it can be the
cluster, the samples, sample types… but can also be any other column,
including continuos (non-categorical) values such as doublet-scores and
other QC-related values. The type of value used (continuous or
categorical) is autodetected, but we can specify it explicitly with
fg_type = "categorical" or
fg_type = "continuous" (and
bg_type = "categorical" or
bg_type = "continuous" for the background). This will
affect the coloring schemes used to plot them.
Important: autodetection of value types is simple: if it’s a
numeric vector, assume it’s continuous, if it’s anything else, treat it
as categorical. This works largely fine, but for some cases (such as
clusters) where categories (clusters) are specified as integers, we
might need to either convert the column in colData to a factor or
explicitly specify fg_type = "categorical".
plotOverlay(
sce = sce_mpnst_example,
foreground = "scDblFinder.score",
fg_type = "continuous",
background = "cluster",
bg_type = "categorical",
reduced_dim = "TSNE"
)In addition to represening a variable, both foreground and background
can be set to solid or none. These are special
values that will plot all points of the same color with
solid or plot no points at all in that layer if set to
none. If a layer is set to solid, by default
it will be plotted as gray dots. We can use fg_palette and
bg_palette to change their color.
We can also alter the ordering of the cells when plotting with
fg_order and bg_order. This might be useful to
avoid visual artifacts when plotting the cells as ordered in the
SingleCellExperiment object (usually sorted by sample) or
to improve the visibility of positive cells in very dense plots. To
avoid plotting the cell sorted per sample we could use
fg_order = "random" which would randomize the plotting
order. To ensure that positive cells are visible over the negative ones
we can plot them with fg_order = "ascending".
Finally, we can subset the foreground layer (and only the foreground!
background is not subsettable) with fg_subset_cells,
showing only a subset of the cells. We can use anything as a filter. The
most straightforward selection criteria is an expression evaluated in
colData(sce), such as
fg_subset_cells = quote(sample_type == "MPNST"). However, a
logical vector, a vector of cell names or even a function receiving the
SingleCellExperiment object and returning a logical vector
are also valid criteria.
p4 <- plotOverlay(
sce = sce_mpnst_example,
foreground = "scDblFinder.score",
fg_type = "continuous",
fg_order = "ascending",
fg_subset_cells = quote(sample_type != "MPNST"),
reduced_dim = "TSNE",
background = "cluster",
bg_point_size = 3,
title = "scDblFinder.score in non-MPNST samples",
bg_legend = FALSE
)
p4When we want to inspect several genes, we can call
plotOverlay() repeatedly, but
plotGeneOverlay() provides a compact convenience function.
It creates one overlay plot for each requested gene and returns the
plots as a named list.
gene_plots <- plotGeneOverlay(
sce = sce_mpnst_example,
genes = c("S100B", "CDH19", "PRRX1", "EGFR"),
background = "solid",
reduced_dim = "TSNE",
fg_order = "ascending"
)
names(gene_plots)
#> [1] "S100B" "CDH19" "PRRX1" "EGFR"Each element of the list is a regular ggplot2 object and
can be displayed independently.
All additional arguments are passed to plotOverlay().
For example, we can use a metadata background and keep the same
foreground ordering.
And the plots can also be combined for example with
patchwork.
library(patchwork)
gene_plots_bg <- plotGeneOverlay(
sce = sce_mpnst_example,
genes = c("SOX10", "CDH19", "PRRX1", "EGFR"),
background = "sample_type",
reduced_dim = "TSNE",
fg_order = "ascending",
bg_dimming = 0.7
)
(gene_plots_bg$SOX10 + gene_plots_bg$CDH19) /
(gene_plots_bg$PRRX1 + gene_plots_bg$EGFR)If some of the requested genes are not present in the object, they are skipped with a warning. This makes it possible to use the same gene list with different objects, as long as at least one of the requested genes is present.
gene_plots_subset <- plotGeneOverlay(
sce = sce_mpnst_example,
genes = c("SOX10", "NOT_A_GENE"),
background = "solid",
reduced_dim = "TSNE",
fg_order = "ascending"
)
#> Warning: `genes` contains features not present in rownames(sce) and they will
#> be skipped: NOT_A_GENE.
names(gene_plots_subset)
#> [1] "SOX10"The function plotOverlayPerGroup() creates one overlay
plot for each value of a metadata column. This is useful when we want to
compare the same foreground variable across sample types, clusters,
conditions or other cell annotations.
In each panel, the background still shows the complete embedding but the foreground layer is restricted to the cells belonging to the corresponding group.
By default, the group order follows the factor levels if the grouping
column is a factor, or the order of appearance in the object otherwise.
An explicit order can be supplied with group_order.
p2 <- plotOverlayPerGroup(
sce = sce_mpnst_example,
group_col = "sample_type",
group_order = c("Nerve", "PNF", "ANF", "MPNST"),
foreground = "CDH19",
background = "cluster",
reduced_dim = "TSNE",
fg_order = "ascending",
fg_legend = FALSE,
bg_legend = FALSE,
bg_dimming = 0.9
)
p2An additional foreground subset can be combined with the group-wise split. This allows us to show, for example, one plot per sample type while restricting the foreground to cells with a particular annotation.
p4 <- plotOverlayPerGroup(
sce = sce_mpnst_example,
group_col = "sample_type",
group_order = c("Nerve", "PNF", "ANF", "MPNST"),
foreground = "S100B",
background = "cluster",
reduced_dim = "TSNE",
fg_subset_cells = quote(celltype.main == "Schwann cell"),
fg_order = "ascending",
fg_legend = FALSE,
bg_legend = FALSE
)
p4plotOverlayPerNestedGroup() is useful when there are two
related grouping variables. A common situation is to have cells grouped
by sample, with samples belonging to broader sample types or
experimental conditions.
In this example, group_col = "sample" defines the
individual panels and outer_col = "sample_type" arranges
the samples according to their sample type.
As in plotOverlayPerGroup(), each panel keeps the
complete embedding as background. The foreground layer is restricted to
the cells from the corresponding sample. This makes it possible to
compare where individual samples are located in the same reduced
dimension space.
The row and panel order can be controlled with
outer_order and group_order. Here we
explicitly order the sample types.
p1 <- plotOverlayPerNestedGroup(
sce = sce_mpnst_example,
group_col = "sample",
outer_col = "sample_type",
outer_order = c("Nerve", "PNF", "ANF", "MPNST"),
foreground = "CDH19",
background = "cluster",
reduced_dim = "TSNE",
bg_dimming = 0.9,
fg_order = "ascending",
fg_limits = "shared",
shared_legend = TRUE,
bg_legend = FALSE
)
p1Nested plots can also be combined with an additional foreground subset. In this example, one plot is created per sample, but only cells annotated as Mesenchymal as their main cell type are shown in the foreground.
p1 <- plotOverlayPerNestedGroup(
sce = sce_mpnst_example,
group_col = "sample",
outer_col = "sample_type",
outer_order = c("Nerve", "PNF", "ANF", "MPNST"),
foreground = "PRRX1",
background = "cluster",
reduced_dim = "TSNE",
bg_dimming = 0.9,
fg_order = "ascending",
fg_limits = "shared",
shared_legend = TRUE,
bg_legend = FALSE,
fg_subset_cells = quote(celltype.main == "Mesenchymal")
)
p1scOverlay includes several categorical and continuous
palettes. They can be listed with listPalettes().
listPalettes()
#> name type n_colours is_default
#> 1 scOverlay categorical 23 TRUE
#> 2 base categorical 24 FALSE
#> 3 dark categorical 24 FALSE
#> 4 soft categorical 24 FALSE
#> 5 bright categorical 24 FALSE
#> 6 pastel categorical 24 FALSE
#> 7 magma continuous 6 FALSE
#> 8 gray_blue continuous 5 FALSE
#> 9 gray_red continuous 5 TRUE
#> 10 gray_purple continuous 5 FALSE
#> 11 blue_gold continuous 5 FALSE
#> 12 purple_orange continuous 5 FALSE
#> 13 black_red_yellow continuous 6 FALSE
#> 14 diverging_blue_white_red continuous 7 FALSE
#> 15 diverging_green_white_purple continuous 7 FALSE
#> 16 diverging_teal_gray_magenta continuous 7 FALSEA graphical preview can be generated by setting
plot = TRUE.
Palettes are selected by name with bg_palette or
fg_palette. Continuous palettes are stored internally as
anchor colours and interpolated to the number of colours needed for the
plot. This also means that user-defined continuous palettes can be
provided as a small vector of colours that will be automatically
interpolated. All palettes from CRANpkg(“ViridisLite”) and
CRANpkg(“RColorBrewer”) are also available.
Here are a few plots with different palettes.
my_palettes <- list("gray_red", "blue_gold", "gray_blue", #from scOverlay
"magma", "viridis", "cividis", #from viridisLite
c("goldenrod", "dodgerblue"), c("cornsilk", "khaki1", "yellow2", "darkgoldenrod"), c("#AAAAEE", "#AAEEEE", "#22DDDD"))
plots <- lapply(my_palettes, function(pal) {
plotOverlay(
sce = sce_mpnst_example,
foreground = "CDH19",
background = "solid",
reduced_dim = "TSNE",
fg_palette = pal,
fg_order = "ascending",
fg_legend = FALSE,
title = pal
)
})
patchwork::wrap_plots(plots, ncol = 3)The package also includes categorical palettes that will be used for
categorical data. Categorical palettes may have names. If all plotted
values match those names, colours are matched by name. If none of the
plotted values match the names, colours are assigned by position. If
only some values match, scOverlay raises an error to avoid
accidentally remapping colours.
sample_type_palette <- c(
Nerve = "#4E79A7",
PNF = "#59A14F",
ANF = "#F28E2B",
MPNST = "#E15759"
)
plotOverlay(
sce = sce_mpnst_example,
foreground = "sample_type",
background = "solid",
reduced_dim = "TSNE",
fg_type = "categorical",
fg_palette = sample_type_palette
)The built-in categorical palettes keep integer-like names
("1", "2" and "3" because these
are useful for cluster labels). Use unname() to force
positional matching, or setNames() to define a custom
mapping.
sample_type_palette <- c(
Nerve = "#4E79A7",
PNF = "#59A14F",
ANF = "#F28E2B",
MPNST = "#E15759"
)
getPalette(
palette = sample_type_palette,
type = "categorical",
values = c("Nerve", "PNF", "ANF", "MPNST")
)
#> Nerve PNF ANF MPNST
#> "#4E79A7" "#59A14F" "#F28E2B" "#E15759"
pal <- getPalette("scOverlay", type = "categorical", n = 4)
setNames(
unname(pal),
c("Nerve", "PNF", "ANF", "MPNST")
)
#> Nerve PNF ANF MPNST
#> "#05d165" "#e6194b" "#ebad00" "#be6af3"Using named palettes is recommended to avoid misleading plots when positional matching is used and some plots might be missing some groups.
Single-cell embeddings can contain thousands or millions of points.
When these plots are saved as vector graphics, the resulting PDF or SVG
files can become large and slow to open or edit. scOverlay
can rasterize only the point layers while keeping the rest of the plot
as vector graphics.
This means that axes, titles, labels, legends and other plot elements remain editable, while the dense background and/or foreground points are stored as raster images.
Rasterization is controlled independently for the background and
foreground layers with bg_raster and
fg_raster.
p1 <- plotOverlay(
sce = sce_mpnst_example,
foreground = "SOX10",
background = "sample_type",
reduced_dim = "TSNE",
fg_order = "ascending",
bg_raster = TRUE,
bg_dimming = 0.7
)
p1A common use case is to rasterize the background, because it usually contains all cells, and keep the foreground as vector points. This can be useful when the foreground contains a smaller number of highlighted cells.
p2 <- plotOverlay(
sce = sce_mpnst_example,
foreground = "CDH19",
background = "sample_type",
reduced_dim = "TSNE",
fg_subset_cells = quote(sample_type == "MPNST"),
fg_order = "ascending",
bg_raster = TRUE,
fg_raster = FALSE
)
p2When both layers are dense, both can be rasterized.
p3 <- plotOverlay(
sce = sce_mpnst_example,
foreground = "SOX10",
background = "sample_type",
reduced_dim = "TSNE",
fg_order = "ascending",
bg_raster = TRUE,
fg_raster = TRUE,
raster_dpi = 300
)
p3The resolution of the rasterized layers is controlled with
raster_dpi. Higher values (600, 1200…) may produce sharper
point layers, but also larger output files.
Rasterization is particularly useful when saving plots to PDF or SVG for publication figures, because it reduces the number of vector elements while keeping the text and layout editable.
The objects returned by plotOverlay() are regular
ggplot2 objects, so they can be saved with the standard
ggplot2::ggsave() function.
p <- plotOverlay(
sce = sce_mpnst_example,
foreground = "SOX10",
background = "sample_type",
reduced_dim = "TSNE",
fg_order = "ascending",
bg_raster = TRUE
)The same plot can be saved in different formats depending on the intended use, PNG, PDF, SVG…
ggplot2::ggsave(
filename = "SOX10_overlay.png",
plot = p,
width = 5,
height = 5,
dpi = 300
)
ggplot2::ggsave(
filename = "SOX10_overlay.pdf",
plot = p,
width = 5,
height = 5
)
ggplot2::ggsave(
filename = "SOX10_overlay.svg",
plot = p,
width = 5,
height = 5
)Grouped plots are saved in the same way: create the plot object
first, then pass it to ggplot2::ggsave(). For grouped
plots, take into account the number pf panels to define the image
size.
p_grouped <- plotOverlayPerGroup(
sce = sce_mpnst_example,
group_col = "sample_type",
group_order = c("Nerve", "PNF", "ANF", "MPNST"),
foreground = "SOX10",
background = "solid",
reduced_dim = "TSNE",
fg_order = "ascending"
)
panel_width <- 4
panel_height <- 4
n_panels <- length(unique(sce_mpnst_example$sample_type))
ggplot2::ggsave(
filename = "SOX10_by_sample_type.pdf",
plot = p_grouped,
width = panel_width * n_panels,
height = panel_height
)For very dense plots, it is often a good idea to combine saving to
PDF or SVG with point-layer rasterization using bg_raster,
fg_raster or both.
sessionInfo()
#> R version 4.6.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 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.26.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] patchwork_1.3.2 SingleCellExperiment_1.35.1
#> [3] SummarizedExperiment_1.43.0 Biobase_2.73.1
#> [5] GenomicRanges_1.65.0 Seqinfo_1.3.0
#> [7] IRanges_2.47.2 S4Vectors_0.51.3
#> [9] BiocGenerics_0.59.7 generics_0.1.4
#> [11] MatrixGenerics_1.25.0 matrixStats_1.5.0
#> [13] scOverlay_0.99.0 BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] sass_0.4.10 SparseArray_1.13.2 lattice_0.22-9
#> [4] digest_0.6.39 evaluate_1.0.5 grid_4.6.0
#> [7] RColorBrewer_1.1-3 fastmap_1.2.0 jsonlite_2.0.0
#> [10] Matrix_1.7-5 ggnewscale_0.5.2 ggrastr_1.0.2
#> [13] BiocManager_1.30.27 viridisLite_0.4.3 scales_1.4.0
#> [16] jquerylib_0.1.4 abind_1.4-8 cli_3.6.6
#> [19] rlang_1.2.0 XVector_0.53.0 withr_3.0.3
#> [22] cachem_1.1.0 DelayedArray_0.39.3 yaml_2.3.12
#> [25] otel_0.2.0 ggbeeswarm_0.7.3 S4Arrays_1.13.0
#> [28] tools_4.6.0 ggplot2_4.0.3 buildtools_1.0.0
#> [31] vctrs_0.7.3 R6_2.6.1 lifecycle_1.0.5
#> [34] vipor_0.4.7 beeswarm_0.4.0 bslib_0.11.0
#> [37] gtable_0.3.6 glue_1.8.1 xfun_0.59
#> [40] sys_3.4.3 knitr_1.51 farver_2.1.2
#> [43] htmltools_0.5.9 labeling_0.4.3 rmarkdown_2.31
#> [46] maketools_1.3.2 Cairo_1.7-0 compiler_4.6.0
#> [49] S7_0.2.2