--- title: "scOverlay: layered visualization of single-cell embeddings" author: - name: Bernat Gel affiliation: Translational Cancer Genomics and Bioinformatics, IGTP package: scOverlay output: BiocStyle::html_document: toc: true toc_float: true vignette: > %\VignetteIndexEntry{scOverlay: layered visualization of single-cell embeddings} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, message=FALSE, warning=FALSE, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 5, fig.height = 4, fig.align="center", dpi = 96, fig.retina = 1, dev = "png" ) ``` # Introduction Dimensionality reduction plots such as t-SNE and UMAP are commonly used to explore single-cell datasets. Point positions represent the global structure of the data, while colour commonly represents one variable such as gene expression, cluster, cell type or a QC metric. This traditional approach is limited to a single layer representing a single variable and makes it difficult to show a variable of interest while retaining the distribution of samples, cell types or clusters as a reference. `scOverlay` addresses this by creating multi-layered plots for single-cell data. It colours a background layer according to one variable (for example, cell type), then draws a second foreground layer colored with a different variable on top using an independent colour scale (for example, gene expression). Optional background dimming partially mutes the background colors so that the foreground remains easy to interpret. The background layer therefore provides context, while the foreground highlights a variable of interest such as gene expression, a score or 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` can also represent multiple foreground features using bivariate blend plots and expression-combination plots. `scOverlay` works with `SingleCellExperiment` objects and requires at least one reduced dimension representation, such as `"TSNE"` or `"UMAP"`, plus the variables to plot in an assay or in `colData`. The package focuses on visualizing already processed single-cell data and is not intended to perform preprocessing, normalization or dimensionality reduction. It complements general single-cell visualization packages such as `scater` and `dittoSeq`, and density-based expression views such as `Nebulosa`, by focusing on independently colored background and foreground layers. # Installation `scOverlay` can be installed with `BiocManager` following Bioconductor's standard procedure which will install `scOverlay` and all its dependencies: ```{r install-bioconductor, eval=FALSE} if (!requireNamespace("BiocManager", quietly = TRUE)) { install.packages("BiocManager") } BiocManager::install("scOverlay") ``` After installation, the package can be loaded with: ```{r load-package-example, eval=FALSE} library(scOverlay) ``` Alternatively, the development version can be installed from github: ```{r install-github, eval=FALSE} if (!requireNamespace("BiocManager", quietly = TRUE)) { install.packages("BiocManager") } BiocManager::install("bernatgel/scOverlay") ``` # Quick start We will start by loading the package and the example data included (more information in the next section). ```{r load-package-and-example-data, eval=TRUE, results="hide", message=FALSE, warning=FALSE} library(scOverlay) library(SingleCellExperiment) data("sce_mpnst_example") ``` 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 below). ```{r view-example-data, eval=TRUE} sce_mpnst_example ``` 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`. ```{r quick-start-1} 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 also select different backgrounds: 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`. Visual parameters such as palettes, point sizes and titles can be adjusted independently. ```{r quick-start-background} 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 visible in the background as context. Here we plot `CDH19` expression only in the cells from one sample. The background still contains all cells. ```{r quick-start-sample-subset} plotOverlay( sce = sce_mpnst_example, foreground = "CDH19", fg_point_size = 0.8, background = "cluster", reduced_dim = "TSNE", fg_subset_cells = quote(sample == "ANF1"), title = "CDH19 in ANF1", bg_dimming = 0.9, bg_legend = FALSE ) ``` # Loading the package and example data `scOverlay` includes an example dataset that we'll use throughout this vignette, including in the previous section. ```{r load-data} data("sce_mpnst_example") sce_mpnst_example ``` The object `sce_mpnst_example` is a reduced `SingleCellExperiment` included with the package to demonstrate its plotting functionality. The underlying data were generated by the package author's research group as part of the single-cell MPNST progression study deposited at the European Genome-phenome Archive under accession `EGAS50000001747`. This example contains selected genes and cells from several sample types, with generic sample identifiers. It retains selected log-expression values, cell metadata and reduced dimensions, and includes no direct patient identifiers. The most important elements for `scOverlay` are the reduced dimensions, assays and cell metadata stored in `colData`. ```{r inspect-object} reducedDimNames(sce_mpnst_example) assayNames(sce_mpnst_example) colnames(colData(sce_mpnst_example)) ``` The reduced dimensions define the coordinates used for plotting. The object contains `"UMAP"`, `"TSNE"` and `"PCA"` coordinates. This vignette uses t-SNE and UMAP to demonstrate the plotting API. 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: ```{r inspect-genes} c("SOX10", "S100B", "CDH19", "MKI67") %in% rownames(sce_mpnst_example) ``` # The basic overlay model 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 is to plot gene expression over the sample type annotation, which indicates the biological sample class associated with each cell. ```{r overlay-model-gene, eval=FALSE} 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)`, such as a cluster, sample, cell type or doublet score. 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. # Choosing the foreground and background 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`; when the background is a gene, `bg_assay` is used. Both default to `"logcounts"`. Selection can also be explicit, for example with `fg_assay = "logcounts"` and `bg_assay = "logcounts"`, which is useful for objects containing several assays. Assay arguments do not affect variables read from `colData`. The data layers can also show cell annotations in `colData(sce)`, including clusters, samples, sample types and other values such as doublet scores or other QC metrics. Value type is detected automatically, but it can be specified explicitly with `fg_type = "categorical"` or `fg_type = "continuous"` (and the corresponding `bg_type` values for the background). This determines the colour mapping used for each layer. The foreground can be a vector of feature or column names only when using `fg_type = "bivariate"` or `fg_type = "combination"`. *Important:* type detection treats numeric vectors as continuous and other vectors as categorical. When categories such as cluster labels are stored as integers, convert the column in `colData` to a factor or specify `fg_type = "categorical"` explicitly. ```{r foreground-numeric-metadata, eval=FALSE} plotOverlay( sce = sce_mpnst_example, foreground = "scDblFinder.score", fg_type = "continuous", background = "cluster", bg_type = "categorical", reduced_dim = "TSNE" ) ``` In addition to representing a variable, both foreground and background can be set to `"solid"` or `"none"`. The former plots all points in that layer with one colour, while the latter omits the layer. Solid layers use grey by default; `fg_palette` and `bg_palette` can change this colour. We can alter foreground plotting order with `fg_order`. This can reduce visual artefacts caused by the input cell order or improve the visibility of high values in dense plots. `fg_order = "random"` randomizes the order, while `fg_order = "ascending"` draws low values first so that high values remain visible on top. Finally, we can subset the foreground layer (and only the foreground! background is not subsettable) with `fg_subset_cells`; the background remains complete. The most direct selection criterion is an expression evaluated in `colData(sce)`, such as `fg_subset_cells = quote(sample_type == "MPNST")`. A logical vector, a vector of cell names or a function that receives the `SingleCellExperiment` object and returns a logical vector are also valid. ```{r foreground-example} 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 ) p4 ``` # Plotting multiple genes on the foreground Some questions involve more than one foreground feature. `scOverlay` supports this through two explicit foreground modes: bivariate blends for two numeric variables, and expression-combination plots for small sets of numeric variables. ## Bivariate foregrounds Bivariate foregrounds blend two numeric variables into a two-dimensional colour scale. The first foreground variable defines the x direction and maps to the `high_x` colour corner; the second foreground variable defines the y direction and maps to the `high_y` colour corner. Because this is not a regular one-dimensional ggplot colour scale, bivariate plots do not use a standard ggplot legend. Use `plotBivariateLegend()` to create a separate two-dimensional legend and combine it manually with the plot. ```{r bivariate-foreground, fig.width=7, fig.height=4, fig.wide=TRUE} p_biv <- plotOverlay( sce = sce_mpnst_example, foreground = c("SOX10", "MKI67"), fg_type = "bivariate", fg_palette = "bivariateBright", background = "cluster", bg_dimming = 0.9, bg_legend = FALSE, reduced_dim = "TSNE" ) leg_biv <- plotBivariateLegend( features = c("SOX10", "MKI67"), palette = "bivariateBright" ) patchwork::wrap_plots(p_biv, leg_biv, widths = c(1, 0.25)) ``` Here, the two-dimensional colour mapping distinguishes SOX10-high cells from MKI67-high proliferating cells and highlights cells with high values for both genes. Custom bivariate palettes use four named corner colours. ```{r custom-bivariate-palette, eval=FALSE} custom_biv <- c( low_xy = "#e6e6e6", high_x = "#ff4346", high_y = "#55b2ff", high_xy = "#7a1fa2" ) plotBivariateLegend( features = c("SOX10", "MKI67"), palette = custom_biv ) ``` ## Expression-combination foregrounds Combination foregrounds convert between two and five numeric foreground features into expressed/not-expressed calls and plot the resulting combinations as categorical values. By default, `fg_threshold = 0`, and expression is called with the strict rule `value > fg_threshold`, so exact zero is not expressed. Labels such as `SOX10-/MKI67+` are categorical values, and the `combinationBright` palette is used by default. ```{r combination-foreground, fig.width=5, fig.height=4} plotOverlay( sce = sce_mpnst_example, foreground = c("SOX10", "MKI67"), fg_type = "combination", fg_threshold = 0, background = "cluster", bg_dimming = 0.9, bg_legend = FALSE, reduced_dim = "TSNE" ) ``` For larger combinations, a data-driven threshold can be useful. ```{r combination-foreground-median, eval=FALSE} plotOverlay( sce = sce_mpnst_example, foreground = c("SOX10", "MKI67", "CDH19"), fg_type = "combination", fg_threshold = "median", background = "solid", reduced_dim = "TSNE" ) ``` # Plotting several genes When 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. ```{r gene-overlay-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) ``` Each element of the list is a regular `ggplot2` object and can be displayed independently. ```{r gene-overlay-single, eval=FALSE} gene_plots$CDH19 ``` 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`. ```{r gene-overlay-background, fig.width=12, fig.height=8, fig.wide=TRUE} 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. ```{r gene-overlay-missing, warning=TRUE} gene_plots_subset <- plotGeneOverlay( sce = sce_mpnst_example, genes = c("SOX10", "NOT_A_GENE"), background = "solid", reduced_dim = "TSNE", fg_order = "ascending" ) names(gene_plots_subset) ``` # Splitting plots by groups 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`. ```{r per-group-sample-type-order, fig.width=12, fig.height=3, fig.wide=TRUE} 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 ) p2 ``` An 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. ```{r per-group-extra-subset, fig.width=12, fig.height=3, fig.wide=TRUE} 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 ) p4 ``` ## Shared scales and legends When plotting the same gene or other value in multiple panels, shared scale limits make colours comparable. For univariate continuous layers, `fg_limits` and `bg_limits` can specify exact limits, such as `fg_limits = c(0, 6)`. Alternatively, `fg_limits = "shared"` computes one foreground range for all panels, while `bg_limits = "shared"` does the same for a continuous background. Shared limits are computed after applying any global foreground subset and before splitting into groups. For bivariate foregrounds, numeric limits are not supported, but `fg_limits = "shared"` provides comparable x and y scaling across grouped or nested panels. Compatible legends can be collected with `shared_legend = TRUE` and moved with regular patchwork syntax. ```{r per-group-shared-scales, fig.width=12, fig.height=4.5, fig.wide=TRUE} p_shared <- plotOverlayPerGroup( sce = sce_mpnst_example, group_col = "sample_type", group_order = c("Nerve", "PNF", "ANF", "MPNST"), foreground = "CDH19", background = "cluster", bg_dimming = 0.9, reduced_dim = "TSNE", fg_order = "ascending", fg_legend = TRUE, bg_legend = FALSE, fg_limits = "shared", shared_legend = TRUE ) p_shared & ggplot2::theme(legend.position = "bottom") ``` For categorical variables, grouped and nested plots use global categorical values to keep colour assignments consistent across panels. Named categorical palettes are recommended when comparing panels, especially when some panels lack some categories. # Nested group plots `plotOverlayPerNestedGroup()` 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. Nested layouts can represent either true hierarchies or repeated inner labels. Every real panel shows the full dataset in the background; the nested grouping restricts only the foreground cells. Nested panels are titled by default as `inner - outer`, which can be changed with `panel_titles`; use `panel_titles = "none"` to suppress these titles. When inner labels are unique to one outer group, a compact layout is often easier to read, and shorter rows are padded on the right with blank spacers so panel widths remain consistent. When inner labels repeat across outer groups, a grid layout preserves a consistent column meaning and uses blank spacers for missing outer/inner combinations. The default `layout = "auto"` uses the grid layout when any inner label appears in more than one outer group and otherwise uses the compact layout. In the sample type/sample example, samples belong to a single sample type, so `layout = "auto"` resolves to the compact layout. The row and panel order can be controlled with `outer_order` and `group_order`. Here we explicitly order the sample types. ```{r nested-groups-1, fig.width=12, fig.height=12, fig.wide=TRUE} 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, layout = "auto" ) p1 ``` We can also use nested plots when the inner labels repeat across outer groups. Here we create a simple high/low detected-gene group from an existing QC metric. Because both labels can appear in several sample types, `layout = "auto"` chooses the grid layout. ```{r nested-groups-grid, fig.width=8, fig.height=7, fig.wide=TRUE} sce_mpnst_example$detected_group <- ifelse( sce_mpnst_example$n_detected > median(sce_mpnst_example$n_detected), "High detected genes", "Low detected genes" ) p_grid <- plotOverlayPerNestedGroup( sce = sce_mpnst_example, outer_col = "sample_type", group_col = "detected_group", foreground = "SOX10", background = "celltype.main", layout = "auto", fg_limits = "shared", shared_legend = TRUE, bg_legend = FALSE, reduced_dim = "TSNE" ) p_grid ``` Nested plots can also be combined with an additional foreground subset. In this example, one plot is created per sample, but only cells whose main cell type is annotated as Mesenchymal are shown in the foreground. ```{r nested-groups-2, fig.width=12, fig.height=12, fig.wide=TRUE} 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"), layout = "auto" ) p1 ``` # Palettes `scOverlay` includes categorical, continuous and bivariate palettes. Palette definitions and metadata can be listed with `listPalettes()`. ```{r list-palettes} listPalettes() ``` ```{r list-bivariate-palettes} listPalettes(type = "bivariate") ``` A graphical preview can be generated with `plotPalettes()`. ```{r list-palettes-plot, fig.width=9, fig.height=7, fig.wide=TRUE} plotPalettes(n = 9) ``` 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. User-defined continuous palettes can therefore be supplied as a small vector of colours that is interpolated automatically. Supported palettes from the `viridisLite` and `RColorBrewer` packages are also available by name. Here are a few plots with different palettes. ```{r palette-patchwork, fig.width=8, fig.height=7, fig.wide=TRUE} my_palettes <- list( "gray_red", "blue_gold", "viridis", c("cornsilk", "khaki1", "yellow2", "darkgoldenrod") ) 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 = 2) ``` ## Categorical and named categorical palettes 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. ```{r palette-named-categorical} 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"`, `"3"` and so on) because these are useful for cluster labels. Use `unname()` to force positional matching, or `setNames()` to define a custom mapping. ```{r palette-named-categorical-rules} 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") ) pal <- getPalette("scOverlay", type = "categorical", n = 4) setNames( unname(pal), c("Nerve", "PNF", "ANF", "MPNST") ) ``` Using named palettes is recommended to avoid misleading plots when positional matching is used and some plots might be missing some groups. # Rasterizing dense point layers 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`. ```{r raster-background} p1 <- plotOverlay( sce = sce_mpnst_example, foreground = "SOX10", background = "sample_type", reduced_dim = "TSNE", fg_order = "ascending", bg_raster = TRUE, bg_dimming = 0.7 ) p1 ``` A 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. ```{r raster-background-only} 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 ) p2 ``` When both layers are dense, both can be rasterized. ```{r raster-both, eval=FALSE} 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 ) p3 ``` The resolution of the rasterized layers is controlled with `raster_dpi`. Higher values, such as 600 or 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. # Saving plots The objects returned by `plotOverlay()` are regular `ggplot2` objects, so they can be saved with the standard `ggplot2::ggsave()` function. ```{r save-create-plot} 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 its intended use, including PNG, PDF and SVG. ```{r save-plot-files, eval=FALSE} 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 of panels to define the image size. ```{r save-grouped-plot, eval=FALSE} 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. # Session information ```{r session-info} sessionInfo() ```