From 42fcec07e455d3d0046d74c5f898e7221950c368 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 14:24:04 -0400 Subject: [PATCH 01/24] Add datatype param, check for correct input formats --- R/vis_clus.R | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index e10c4e72..0e25825f 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -40,7 +40,16 @@ #' particular, expects a logical colData column `exclude_overlapping` #' specifying which spots to exclude from the plot. Sets `auto_crop = FALSE`. #' @param guide_point_size A `numeric(1)` specifying the size of the points in -#' guide. Defaults to `point_size`. Increase to improve visability. +#' guide. Defaults to `point_size`. Increase to improve visibility. +#' @param datatype A `character(1)` specifying the type of spatial transcriptomics +#' data stored in `spe`. Supported options are: +#' \describe{ +#' \item{`"Visium"`}{(Default) Expects `pxl_col_in_fullres` and +#' `pxl_row_in_fullres` as columns of `spatialCoords(spe)`. Enables +#' image handling via the `spatialData` slot.} +#' \item{`"Xenium"`}{Expects `x_centroid` and `y_centroid` as columns +#' of `spatialCoords(spe)`.} +#' } #' @param ... Passed to [paste0()][base::paste] for making the title of the #' plot following the `sampleid`. #' @@ -145,6 +154,7 @@ vis_clus <- function( na_color = "#CCCCCC40", is_stitched = FALSE, guide_point_size = point_size, + datatype = c("Visium", "Xenium"), ...) { # Verify existence and legitimacy of 'sampleid' if ( @@ -158,11 +168,19 @@ vis_clus <- function( call. = FALSE ) } + + ## Check for valid datatype + datatype <- match.arg(datatype) - # Check validity of spatial coordinates - if (!setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { + # Check validity of spatial coordinates by datatype + if (datatype == "Visium" & !setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { stop( - "Abnormal spatial coordinates: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", + "Abnormal spatial coordinates for Visium datatype: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", + call. = FALSE + ) + } else if (datatype == "Xenium" & !setequal(c("x_centroid", "y_centroid"), colnames(spatialCoords(spe)))) { + stop( + "Abnormal spatial coordinates for Xisium datatype: should have 'x_centroid' and 'y_centroid' columns.", call. = FALSE ) } From b9dcff862b3ae3759d2346a6409c797c04c928dc Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 14:25:05 -0400 Subject: [PATCH 02/24] Add vis_clus_c() function for handling visulization of Xenium/centroid based data --- NAMESPACE | 1 + R/vis_clus_c.R | 116 ++++++++++++++++++++++++++++++++++++++++++++++ man/vis_clus_c.Rd | 108 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 R/vis_clus_c.R create mode 100644 man/vis_clus_c.Rd diff --git a/NAMESPACE b/NAMESPACE index 68a61fe9..1d34a0d6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -40,6 +40,7 @@ export(sig_genes_extract) export(sig_genes_extract_all) export(sort_clusters) export(vis_clus) +export(vis_clus_c) export(vis_clus_p) export(vis_gene) export(vis_gene_p) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R new file mode 100644 index 00000000..4428ae9f --- /dev/null +++ b/R/vis_clus_c.R @@ -0,0 +1,116 @@ +#' Sample spatial cluster visualization workhorse function for centroid data +#' +#' This function visualizes the clusters for one given sample at the spot-level +#' using (by default) the histology information on the background. This is the +#' function that does all the plotting behind [vis_clus()]. To visualize +#' gene-level (or any continuous variable) use [vis_gene_p()]. +#' +#' @inheritParams vis_clus +#' @param d A `data.frame()` with the sample-level information. This is +#' typically obtained using `cbind(colData(spe), spatialCoords(spe))`. +#' @param title The title for the plot. +#' +#' @return A [ggplot2][ggplot2::ggplot] object. +#' @export +#' @importFrom tibble tibble +#' @importFrom SpatialExperiment imgData scaleFactors +#' @importFrom S4Vectors metadata +#' @importFrom grid rasterGrob unit +#' @family Spatial cluster visualization functions +#' +#' @examples +#' +#' if (enough_ram()) { +#' ## Obtain the necessary data +#' if (!exists("spe")) spe <- fetch_data("spe") +#' +#' spe_sub <- qs2::qs_read("../Xenium/XeniumIO_test/spe_Xenium_test_Br1039.qs2") +#' table(spe_sub$sample_id) +#' head(spatialCoords(spe_sub)) +#' +#' summary(spatialCoords(spe_sub)[,"x_centroid"]) +#' summary(spatialCoords(spe_sub)[,"y_centroid"]) +#' +#' ## add catagorical variable +#' spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "west", "east") +#' table(spe_sub$x_half) +#' +#' p <- vis_clus_c( +#' spe = spe_sub, +#' d = as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE), +#' clustervar = "x_half", +#' sampleid = "sample01.1", +#' #colors = libd_layer_colors, +#' colors = c(east = "red", west = "blue"), +#' title = "Xenium test", +#' point_size = 1, +#' alpha = 0.5 +#' ) +#' print(p) +#' +#' ## Clean up +#' rm(spe_sub) +#' } +vis_clus_c <- + function(spe, + d, + clustervar, + sampleid = unique(spe$sample_id)[1], + colors, + title, + alpha = NA, + point_size = 1, + auto_crop = TRUE, + na_color = "#CCCCCC40") { + ## Some variables + x_centroid <- y_centroid <- key <- NULL + # stopifnot(all(c("x_centroid", "y_centroid", "key") %in% colnames(d))) + + + # ## Crop the image if needed + # if (auto_crop) { + # frame_lims <- + # frame_limits(spe, sampleid = sampleid, image_id = image_id) + # img <- + # img[frame_lims$y_min:frame_lims$y_max, frame_lims$x_min:frame_lims$x_max] + # adjust <- + # list(x = frame_lims$x_min, y = frame_lims$y_min) + # } else { + adjust <- list(x = 0, y = 0) + # } + # + p <- ggplot( + d, + aes( + x = x_centroid, + y = y_centroid, + fill = factor(!!sym(clustervar)), + key = key + ) + ) + + p <- p + + geom_point( + shape = 21, + size = point_size, + stroke = 0, + colour = "transparent", + alpha = alpha + ) + + coord_fixed(expand = FALSE) + + scale_fill_manual(values = colors, na.value = na_color) + + xlab("") + ylab("") + + labs(fill = NULL) + + ggtitle(title) + + theme_set(theme_bw(base_size = 20)) + + theme( + panel.grid.major = element_blank(), + panel.grid.minor = element_blank(), + panel.background = element_blank(), + axis.line = element_blank(), + axis.text = element_blank(), + axis.ticks = element_blank(), + legend.box.spacing = unit(0, "µm") + ) + return(p) + } diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd new file mode 100644 index 00000000..595b0912 --- /dev/null +++ b/man/vis_clus_c.Rd @@ -0,0 +1,108 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/vis_clus_c.R +\name{vis_clus_c} +\alias{vis_clus_c} +\title{Sample spatial cluster visualization workhorse function} +\usage{ +vis_clus_c( + spe, + d, + clustervar, + sampleid = unique(spe$sample_id)[1], + colors, + title, + alpha = NA, + point_size = 2, + auto_crop = TRUE, + na_color = "#CCCCCC40" +) +} +\arguments{ +\item{spe}{A +\link[SpatialExperiment:SpatialExperiment]{SpatialExperiment-class} +object. See \code{\link[=fetch_data]{fetch_data()}} for how to download some example objects or +\code{\link[=read10xVisiumWrapper]{read10xVisiumWrapper()}} to read in \code{spaceranger --count} output files and +build your own \code{spe} object.} + +\item{d}{A \code{data.frame()} with the sample-level information. This is +typically obtained using \code{cbind(colData(spe), spatialCoords(spe))}.} + +\item{clustervar}{A \code{character(1)} with the name of the \code{colData(spe)} +column that has the cluster values.} + +\item{sampleid}{A \code{character(1)} specifying which sample to plot from +\code{colData(spe)$sample_id} (formerly \code{colData(spe)$sample_name}).} + +\item{colors}{A vector of colors to use for visualizing the clusters +from \code{clustervar}. If the vector has names, then those should match the +values of \code{clustervar}.} + +\item{title}{The title for the plot.} + +\item{alpha}{A \code{numeric(1)} in the \verb{[0, 1]} range that specifies the +transparency level of the data on the spots.} + +\item{point_size}{A \code{numeric(1)} specifying the size of the points. Defaults +to \code{1.25}. Some colors look better if you use \code{2} for instance.} + +\item{auto_crop}{A \code{logical(1)} indicating whether to automatically crop +the image / plotting area, which is useful if the Visium capture area is +not centered on the image and if the image is not a square.} + +\item{na_color}{A \code{character(1)} specifying a color for the NA values. +If you set \code{alpha = NA} then it's best to set \code{na_color} to a color that has +alpha blending already, which will make non-NA values pop up more and the NA +values will show with a lighter color. This behavior is lost when \code{alpha} is +set to a non-\code{NA} value.} +} +\value{ +A \link[ggplot2:ggplot]{ggplot2} object. +} +\description{ +This function visualizes the clusters for one given sample at the spot-level +using (by default) the histology information on the background. This is the +function that does all the plotting behind \code{\link[=vis_clus]{vis_clus()}}. To visualize +gene-level (or any continuous variable) use \code{\link[=vis_gene_p]{vis_gene_p()}}. +} +\examples{ + +if (enough_ram()) { + ## Obtain the necessary data + if (!exists("spe")) spe <- fetch_data("spe") + + spe_sub <- qs2::qs_read("../Xenium/XeniumIO_test/spe_Xenium_test_Br1039.qs2") + table(spe_sub$sample_id) + head(spatialCoords(spe_sub)) + + summary(spatialCoords(spe_sub)[,"x_centroid"]) + summary(spatialCoords(spe_sub)[,"y_centroid"]) + + ## add catagorical variable + spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "west", "east") + table(spe_sub$x_half) + + p <- vis_clus_c( + spe = spe_sub, + d = as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE), + clustervar = "x_half", + sampleid = "sample01.1", + #colors = libd_layer_colors, + colors = c(east = "red", west = "blue"), + title = "Xenium test", + point_size = 1 + ) + print(p) + + ## Clean up + rm(spe_sub) +} +} +\seealso{ +Other Spatial cluster visualization functions: +\code{\link{frame_limits}()}, +\code{\link{vis_clus}()}, +\code{\link{vis_clus_p}()}, +\code{\link{vis_grid_clus}()}, +\code{\link{vis_image}()} +} +\concept{Spatial cluster visualization functions} From e534d9f2c02a4e0715826809a5deb52c46b9f44e Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 14:30:02 -0400 Subject: [PATCH 03/24] Update desciptions for vis_clus_*() functions --- R/vis_clus_c.R | 11 ++++++----- R/vis_clus_p.R | 12 +++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index 4428ae9f..83c6ad8f 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -1,9 +1,10 @@ -#' Sample spatial cluster visualization workhorse function for centroid data +#' Sample spatial cluster visualization workhorse function for Xenium data with +#' centroid based spatailCoords #' -#' This function visualizes the clusters for one given sample at the spot-level -#' using (by default) the histology information on the background. This is the -#' function that does all the plotting behind [vis_clus()]. To visualize -#' gene-level (or any continuous variable) use [vis_gene_p()]. +#' This function visualizes clusters or categorical variables for one given +#' sample at the cell-level. This is the function that does all the plotting +#' behind [vis_clus(datatype = "Xenium")]. To visualize gene-level +#' (or any continuous variable) use [vis_gene_c()]. #' #' @inheritParams vis_clus #' @param d A `data.frame()` with the sample-level information. This is diff --git a/R/vis_clus_p.R b/R/vis_clus_p.R index b7a381d6..bd88149e 100644 --- a/R/vis_clus_p.R +++ b/R/vis_clus_p.R @@ -1,9 +1,11 @@ -#' Sample spatial cluster visualization workhorse function +#' Sample spatial cluster visualization workhorse function for Visium data with +#' pixel based spatailCoords aligned to images. #' -#' This function visualizes the clusters for one given sample at the spot-level -#' using (by default) the histology information on the background. This is the -#' function that does all the plotting behind [vis_clus()]. To visualize -#' gene-level (or any continuous variable) use [vis_gene_p()]. +#' This function visualizes the clusters or categorical variables for one given +#' sample at the spot-level using (by default) the histology information on the +#' background. This is the function that does all the plotting behind +#' [vis_clus()]. To visualize gene-level (or any continuous variable) use +#' [vis_gene_p()]. #' #' @inheritParams vis_clus #' @param d A `data.frame()` with the sample-level information. This is From 4ac9bc9d74b94464d7aea434ee55115b1cab0385 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 14:30:27 -0400 Subject: [PATCH 04/24] Update docs --- man/frame_limits.Rd | 1 + man/vis_clus.Rd | 14 +++++++++++++- man/vis_clus_c.Rd | 16 +++++++++------- man/vis_clus_p.Rd | 13 ++++++++----- man/vis_grid_clus.Rd | 3 ++- man/vis_image.Rd | 1 + 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/man/frame_limits.Rd b/man/frame_limits.Rd index b24b2af4..d224682f 100644 --- a/man/frame_limits.Rd +++ b/man/frame_limits.Rd @@ -58,6 +58,7 @@ if (enough_ram()) { \seealso{ Other Spatial cluster visualization functions: \code{\link{vis_clus}()}, +\code{\link{vis_clus_c}()}, \code{\link{vis_clus_p}()}, \code{\link{vis_grid_clus}()}, \code{\link{vis_image}()} diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index c01c8028..61014f7c 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -18,6 +18,7 @@ vis_clus( na_color = "#CCCCCC40", is_stitched = FALSE, guide_point_size = point_size, + datatype = c("Visium", "Xenium"), ... ) } @@ -69,7 +70,17 @@ particular, expects a logical colData column \code{exclude_overlapping} specifying which spots to exclude from the plot. Sets \code{auto_crop = FALSE}.} \item{guide_point_size}{A \code{numeric(1)} specifying the size of the points in -guide. Defaults to \code{point_size}. Increase to improve visability.} +guide. Defaults to \code{point_size}. Increase to improve visibility.} + +\item{datatype}{A \code{character(1)} specifying the type of spatial transcriptomics +data stored in \code{spe}. Supported options are: +\describe{ +\item{\code{"Visium"}}{(Default) Expects \code{pxl_col_in_fullres} and +\code{pxl_row_in_fullres} as columns of \code{spatialCoords(spe)}. Enables +image handling via the \code{spatialData} slot.} +\item{\code{"Xenium"}}{Expects \code{x_centroid} and \code{y_centroid} as columns +of \code{spatialCoords(spe)}.} +}} \item{...}{Passed to \link[base:paste]{paste0()} for making the title of the plot following the \code{sampleid}.} @@ -158,6 +169,7 @@ if (enough_ram()) { \seealso{ Other Spatial cluster visualization functions: \code{\link{frame_limits}()}, +\code{\link{vis_clus_c}()}, \code{\link{vis_clus_p}()}, \code{\link{vis_grid_clus}()}, \code{\link{vis_image}()} diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index 595b0912..d0cb1dcb 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -2,7 +2,8 @@ % Please edit documentation in R/vis_clus_c.R \name{vis_clus_c} \alias{vis_clus_c} -\title{Sample spatial cluster visualization workhorse function} +\title{Sample spatial cluster visualization workhorse function for Xenium data with +centroid based spatailCoords} \usage{ vis_clus_c( spe, @@ -12,7 +13,7 @@ vis_clus_c( colors, title, alpha = NA, - point_size = 2, + point_size = 1, auto_crop = TRUE, na_color = "#CCCCCC40" ) @@ -59,10 +60,10 @@ set to a non-\code{NA} value.} A \link[ggplot2:ggplot]{ggplot2} object. } \description{ -This function visualizes the clusters for one given sample at the spot-level -using (by default) the histology information on the background. This is the -function that does all the plotting behind \code{\link[=vis_clus]{vis_clus()}}. To visualize -gene-level (or any continuous variable) use \code{\link[=vis_gene_p]{vis_gene_p()}}. +This function visualizes clusters or categorical variables for one given +sample at the cell-level. This is the function that does all the plotting +behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level +(or any continuous variable) use \code{\link[=vis_gene_c]{vis_gene_c()}}. } \examples{ @@ -89,7 +90,8 @@ if (enough_ram()) { #colors = libd_layer_colors, colors = c(east = "red", west = "blue"), title = "Xenium test", - point_size = 1 + point_size = 1, + alpha = 0.5 ) print(p) diff --git a/man/vis_clus_p.Rd b/man/vis_clus_p.Rd index ced22bc7..0a7b1dc0 100644 --- a/man/vis_clus_p.Rd +++ b/man/vis_clus_p.Rd @@ -2,7 +2,8 @@ % Please edit documentation in R/vis_clus_p.R \name{vis_clus_p} \alias{vis_clus_p} -\title{Sample spatial cluster visualization workhorse function} +\title{Sample spatial cluster visualization workhorse function for Visium data with +pixel based spatailCoords aligned to images.} \usage{ vis_clus_p( spe, @@ -68,10 +69,11 @@ set to a non-\code{NA} value.} A \link[ggplot2:ggplot]{ggplot2} object. } \description{ -This function visualizes the clusters for one given sample at the spot-level -using (by default) the histology information on the background. This is the -function that does all the plotting behind \code{\link[=vis_clus]{vis_clus()}}. To visualize -gene-level (or any continuous variable) use \code{\link[=vis_gene_p]{vis_gene_p()}}. +This function visualizes the clusters or categorical variables for one given +sample at the spot-level using (by default) the histology information on the +background. This is the function that does all the plotting behind +\code{\link[=vis_clus]{vis_clus()}}. To visualize gene-level (or any continuous variable) use +\code{\link[=vis_gene_p]{vis_gene_p()}}. } \examples{ @@ -101,6 +103,7 @@ if (enough_ram()) { Other Spatial cluster visualization functions: \code{\link{frame_limits}()}, \code{\link{vis_clus}()}, +\code{\link{vis_clus_c}()}, \code{\link{vis_grid_clus}()}, \code{\link{vis_image}()} } diff --git a/man/vis_grid_clus.Rd b/man/vis_grid_clus.Rd index ba69f862..30d292db 100644 --- a/man/vis_grid_clus.Rd +++ b/man/vis_grid_clus.Rd @@ -86,7 +86,7 @@ particular, expects a logical colData column \code{exclude_overlapping} specifying which spots to exclude from the plot. Sets \code{auto_crop = FALSE}.} \item{guide_point_size}{A \code{numeric(1)} specifying the size of the points in -guide. Defaults to \code{point_size}. Increase to improve visability.} +guide. Defaults to \code{point_size}. Increase to improve visibility.} \item{...}{Passed to \link[base:paste]{paste0()} for making the title of the plot following the \code{sampleid}.} @@ -130,6 +130,7 @@ if (enough_ram()) { Other Spatial cluster visualization functions: \code{\link{frame_limits}()}, \code{\link{vis_clus}()}, +\code{\link{vis_clus_c}()}, \code{\link{vis_clus_p}()}, \code{\link{vis_image}()} } diff --git a/man/vis_image.Rd b/man/vis_image.Rd index 943c3a6b..d0626aaa 100644 --- a/man/vis_image.Rd +++ b/man/vis_image.Rd @@ -77,6 +77,7 @@ if (enough_ram()) { Other Spatial cluster visualization functions: \code{\link{frame_limits}()}, \code{\link{vis_clus}()}, +\code{\link{vis_clus_c}()}, \code{\link{vis_clus_p}()}, \code{\link{vis_grid_clus}()} } From 90d348810d252563da55252defcf611680373666 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:28:51 -0400 Subject: [PATCH 05/24] Add xenium example to experimenthub metadata for LFF ERC --- inst/extdata/metadata_LFF_spatial_ERC.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/inst/extdata/metadata_LFF_spatial_ERC.csv b/inst/extdata/metadata_LFF_spatial_ERC.csv index 91bcc02a..fac6d51c 100644 --- a/inst/extdata/metadata_LFF_spatial_ERC.csv +++ b/inst/extdata/metadata_LFF_spatial_ERC.csv @@ -7,3 +7,4 @@ "LFF_spatial_ERC_snRNAseq_pseudobulk_subcluster","Pseudo-bulked SingleCellExperiment object LFF_spatial_ERC human brain (ERC) snRNA-seq data (n = 31) at 38 subcluster resolution, from the Chromium platform from 10x Genomics generated by the Lieber Institute for Brain Development (LIBD) and available through the spatialLIBD Bioconductor package.","3.21","GRCh38","GTF","https://bioconductor.org/packages/spatialLIBD","Nov 20 2025","Homo sapiens",9606,TRUE,"LIBD","Leonardo Collado-Torres ","SingleCellExperiment","Rds","spatialLIBD/spatialLIBD_files/sce_subcluster_pseudobulk-cell_type_anno.rds","Visium_snRNAseq_AD_Alzheimer_Disease_APOE_ERC_spatialLIBD" "LFF_spatial_ERC_snRNAseq_modeling_results_broad","List of modeling results at the 9 broad cell type resolution for the LFF_spatial_ERC human brain (ERC) for snRNA-seq (n = 31) from the Chromium platform from 10x Genomics generated by the Lieber Institute for Brain Development (LIBD) and available through the spatialLIBD Bioconductor package.","3.21","GRCh38","GTF","https://bioconductor.org/packages/spatialLIBD","Nov 20 2025","Homo sapiens",9606,TRUE,"LIBD","Leonardo Collado-Torres ","list","Rds","spatialLIBD/spatialLIBD_files/sce_subcluster_pseudobulk-cell_type_broad.rds","Visium_snRNAseq_AD_Alzheimer_Disease_APOE_ERC_spatialLIBD" "LFF_spatial_ERC_snRNAseq_modeling_results_subcluster","List of modeling results at the 38 subcluster resolution for the LFF_spatial_ERC human brain (ERC) for snRNA-seq (n = 31) from the Chromium platform from 10x Genomics generated by the Lieber Institute for Brain Development (LIBD) and available through the spatialLIBD Bioconductor package.","3.21","GRCh38","GTF","https://bioconductor.org/packages/spatialLIBD","Nov 20 2025","Homo sapiens",9606,TRUE,"LIBD","Leonardo Collado-Torres ","list","Rds","spatialLIBD/spatialLIBD_files/sce_subcluster_modeling_results-cell_type_anno.rds","Visium_snRNAseq_AD_Alzheimer_Disease_APOE_ERC_spatialLIBD" +"spe_xenium_example","SpatialExperiment object of Xenium data for the LFF_spatial_ERC human brain (ERC) spatial transcriptomics data (n = 2) from the Xenium platform from 10x Genomics generated by the Lieber Institute for Brain Development (LIBD) and available through the spatialLIBD Bioconductor package.","3.24","GRCh38","GTF","https://bioconductor.org/packages/spatialLIBD","May 5 2026","Homo sapiens",9606,TRUE,"LIBD","Leonardo Collado-Torres ","SpatialExperiment","FilePath","spatialLIBD/spatialLIBD_files/spe_Xenium_test.rds","Visium_snRNAseq_AD_Alzheimer_Disease_APOE_ERC_spatialLIBD" \ No newline at end of file From 2fed7895e6d0ce4ab924871fedea9fbfd20c32ca Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:44:46 -0400 Subject: [PATCH 06/24] Add spe_xenium_example to fetch_data (need to update github when ready) --- R/fetch_data.R | 13 ++++++++++++- man/fetch_data.Rd | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/R/fetch_data.R b/R/fetch_data.R index 76bfa185..83383b53 100644 --- a/R/fetch_data.R +++ b/R/fetch_data.R @@ -157,7 +157,8 @@ fetch_data <- "LFF_spatial_ERC_snRNAseq_pseudobulk_broad", "LFF_spatial_ERC_snRNAseq_pseudobulk_subcluster", "LFF_spatial_ERC_snRNAseq_modeling_results_broad", - "LFF_spatial_ERC_snRNAseq_modeling_results_subcluster" + "LFF_spatial_ERC_snRNAseq_modeling_results_subcluster", + "spe_xenium_example" ), destdir = tempdir(), eh = ExperimentHub::ExperimentHub(), @@ -409,6 +410,16 @@ fetch_data <- file_name <- "sce_subcluster_pseudobulk-cell_type_anno.rds" url <- "https://www.dropbox.com/scl/fi/y42pv7k02luvznwqii2rm/sce_subcluster_modeling_results-cell_type_anno.rds?rlkey=17c0ybowjpejdxc71tuxlcfna&dl=1" + } else if ( + type == "spe_xenium_example" + ) { + tag <- "LFF_spatial_ERC" + hub_title <- type + + ## While EH is not set-up + file_name <- "spe_Xenium_test.rds" + url <- + "https://www.dropbox.com/scl/fi/y42pv7k02luvznwqii2rm/sce_subcluster_modeling_results-cell_type_anno.rds?rlkey=17c0ybowjpejdxc71tuxlcfna&dl=1" } file_path <- file.path(destdir, file_name) diff --git a/man/fetch_data.Rd b/man/fetch_data.Rd index 1c90603f..73eed5fa 100644 --- a/man/fetch_data.Rd +++ b/man/fetch_data.Rd @@ -19,7 +19,7 @@ fetch_data( "LFF_spatial_ERC_snRNAseq_pseudobulk_broad", "LFF_spatial_ERC_snRNAseq_pseudobulk_subcluster", "LFF_spatial_ERC_snRNAseq_modeling_results_broad", - "LFF_spatial_ERC_snRNAseq_modeling_results_subcluster"), + "LFF_spatial_ERC_snRNAseq_modeling_results_subcluster", "spe_xenium_example"), destdir = tempdir(), eh = ExperimentHub::ExperimentHub(), bfc = BiocFileCache::BiocFileCache() From 22392ae0b811e21f7fe139bac5464c0d633db1df Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:45:20 -0400 Subject: [PATCH 07/24] clean up vis_clus_c example --- R/vis_clus_c.R | 17 +++++++++-------- man/vis_clus_c.Rd | 17 +++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index 83c6ad8f..3dce112f 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -23,17 +23,18 @@ #' #' if (enough_ram()) { #' ## Obtain the necessary data -#' if (!exists("spe")) spe <- fetch_data("spe") +#' if (!exists("spe")) spe <- fetch_data("spe_Xenium_test") #' -#' spe_sub <- qs2::qs_read("../Xenium/XeniumIO_test/spe_Xenium_test_Br1039.qs2") -#' table(spe_sub$sample_id) -#' head(spatialCoords(spe_sub)) +#' # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") +#' +#' ## Prepare the data for the plotting function +#' spe_sub <- spe[, spe$sample_id == "sample1"] #' -#' summary(spatialCoords(spe_sub)[,"x_centroid"]) -#' summary(spatialCoords(spe_sub)[,"y_centroid"]) +#' # summary(spatialCoords(spe_sub)[,"x_centroid"]) +#' # summary(spatialCoords(spe_sub)[,"y_centroid"]) #' #' ## add catagorical variable -#' spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "west", "east") +#' spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "left", "right") #' table(spe_sub$x_half) #' #' p <- vis_clus_c( @@ -42,7 +43,7 @@ #' clustervar = "x_half", #' sampleid = "sample01.1", #' #colors = libd_layer_colors, -#' colors = c(east = "red", west = "blue"), +#' colors = c(left = "red", right = "blue"), #' title = "Xenium test", #' point_size = 1, #' alpha = 0.5 diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index d0cb1dcb..c991baf4 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -69,17 +69,18 @@ behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level if (enough_ram()) { ## Obtain the necessary data - if (!exists("spe")) spe <- fetch_data("spe") + if (!exists("spe")) spe <- fetch_data("spe_Xenium_test") - spe_sub <- qs2::qs_read("../Xenium/XeniumIO_test/spe_Xenium_test_Br1039.qs2") - table(spe_sub$sample_id) - head(spatialCoords(spe_sub)) + # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") + + ## Prepare the data for the plotting function + spe_sub <- spe[, spe$sample_id == "sample1"] - summary(spatialCoords(spe_sub)[,"x_centroid"]) - summary(spatialCoords(spe_sub)[,"y_centroid"]) + # summary(spatialCoords(spe_sub)[,"x_centroid"]) + # summary(spatialCoords(spe_sub)[,"y_centroid"]) ## add catagorical variable - spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "west", "east") + spe_sub$x_half <- ifelse(spatialCoords(spe_sub)[,"x_centroid"] < 3088, "left", "right") table(spe_sub$x_half) p <- vis_clus_c( @@ -88,7 +89,7 @@ if (enough_ram()) { clustervar = "x_half", sampleid = "sample01.1", #colors = libd_layer_colors, - colors = c(east = "red", west = "blue"), + colors = c(left = "red", right = "blue"), title = "Xenium test", point_size = 1, alpha = 0.5 From c981336f000e5aae202494ba7dfc9b3b594168e5 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:45:37 -0400 Subject: [PATCH 08/24] Add xenium example --- R/vis_clus.R | 21 ++++++++++++++++++++- man/vis_clus.Rd | 21 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index 0e25825f..cce03144 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -63,7 +63,7 @@ #' @examples #' #' if (enough_ram()) { -#' ## Obtain the necessary data +#' ## Obtain the necessary data: Visium example #' if (!exists("spe")) spe <- fetch_data("spe") #' #' ## Check the colors defined by Lukas M Weber @@ -126,6 +126,25 @@ #' ... = " LIBD Layers" #' ) #' print(p5) +#' +#' ## Obtain the necessary data: Xenium example +#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_Xenium_test") +#' +#' spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") +#' +#' p6 <- vis_clus( +#' spe = spe_xenium, +#' clustervar = "layer_guess_reordered", +#' sampleid = "sample1", +#' colors = c(left = "red", right = "blue"), +#' na_color = "white", +#' point_size = 1, +#' guide_point_size = 3, +#' datatype = "Xenium" +#' ) +#' print(p6) +#' +#' #' #' } vis_clus <- function( diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index 61014f7c..a66dd7b7 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -100,7 +100,7 @@ data and title for \code{\link[=vis_clus_p]{vis_clus_p()}}. \examples{ if (enough_ram()) { - ## Obtain the necessary data + ## Obtain the necessary data: Visium example if (!exists("spe")) spe <- fetch_data("spe") ## Check the colors defined by Lukas M Weber @@ -163,6 +163,25 @@ if (enough_ram()) { ... = " LIBD Layers" ) print(p5) + + ## Obtain the necessary data: Xenium example + if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_Xenium_test") + + spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") + + p6 <- vis_clus( + spe = spe_xenium, + clustervar = "layer_guess_reordered", + sampleid = "sample1", + colors = c(left = "red", right = "blue"), + na_color = "white", + point_size = 1, + guide_point_size = 3, + datatype = "Xenium" + ) + print(p6) + + } } From 1b734e2f2fbb8eb5116babfa0b091bb547da3852 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:52:23 -0400 Subject: [PATCH 09/24] use vis_clus_c for xenium datatype --- R/vis_clus.R | 102 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index cce03144..127fe07c 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -188,49 +188,79 @@ vis_clus <- function( ) } + ## subset spe to selected sample + spe_sub <- spe[, spe$sample_id == sampleid] + ## Check for valid datatype datatype <- match.arg(datatype) - - # Check validity of spatial coordinates by datatype - if (datatype == "Visium" & !setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { - stop( - "Abnormal spatial coordinates for Visium datatype: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", - call. = FALSE - ) - } else if (datatype == "Xenium" & !setequal(c("x_centroid", "y_centroid"), colnames(spatialCoords(spe)))) { - stop( - "Abnormal spatial coordinates for Xisium datatype: should have 'x_centroid' and 'y_centroid' columns.", - call. = FALSE - ) + + if(datatype == "Visium"){ + + # Check validity of spatial coordinates by datatype + if (!setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { + stop( + "Abnormal spatial coordinates for Visium datatype: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", + call. = FALSE + ) } - + + d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) + spe_sub <- spe[, spe$sample_id == sampleid] - + if (is_stitched) { - # Drop excluded spots and calculate an appropriate point size - temp <- prep_stitched_data(spe_sub, point_size, image_id) - spe_sub <- temp$spe - point_size <- temp$point_size - - # Frame limits are poorly defined for stitched data - auto_crop <- FALSE + # Drop excluded spots and calculate an appropriate point size + temp <- prep_stitched_data(spe_sub, point_size, image_id) + spe_sub <- temp$spe + point_size <- temp$point_size + + # Frame limits are poorly defined for stitched data + auto_crop <- FALSE } - - d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) - + vis_clus_p( - spe = spe_sub, - d = d, - clustervar = clustervar, - sampleid = sampleid, - spatial = spatial, - title = paste0(sampleid, ...), - colors = get_colors(colors, d[, clustervar]), - image_id = image_id, - alpha = alpha, - point_size = point_size, - auto_crop = auto_crop, - na_color = na_color + spe = spe_sub, + d = d, + clustervar = clustervar, + sampleid = sampleid, + spatial = spatial, + title = paste0(sampleid, ...), + colors = get_colors(colors, d[, clustervar]), + image_id = image_id, + alpha = alpha, + point_size = point_size, + auto_crop = auto_crop, + na_color = na_color ) + guides(fill = guide_legend(override.aes = list(size = guide_point_size))) + + } else if(datatype == "Xenium"){ + + if (datatype == "Xenium" & !setequal(c("x_centroid", "y_centroid"), colnames(spatialCoords(spe)))) { + stop( + "Abnormal spatial coordinates for Xisium datatype: should have 'x_centroid' and 'y_centroid' columns.", + call. = FALSE + ) + } + + vis_clus_c( + spe = spe_sub, + d = d, + clustervar = clustervar, + sampleid = sampleid, + title = paste0(sampleid, ...), + colors = get_colors(colors, d[, clustervar]), + alpha = alpha, + point_size = point_size, + na_color = na_color + ) + + guides(fill = guide_legend(override.aes = list(size = guide_point_size))) + + + } + + + + + } From 3c8795329e34162e43d6808c0c0fd0430d81aea4 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 16:57:33 -0400 Subject: [PATCH 10/24] fix unit --- R/vis_clus_c.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index 3dce112f..baaec20c 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -112,7 +112,7 @@ vis_clus_c <- axis.line = element_blank(), axis.text = element_blank(), axis.ticks = element_blank(), - legend.box.spacing = unit(0, "µm") + legend.box.spacing = unit(0, "pt") ) return(p) } From 539787b855b7c67903bd24cb19413183f224e4f7 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 17:03:04 -0400 Subject: [PATCH 11/24] fix example, redefine d --- R/vis_clus.R | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index 127fe07c..8036f2e4 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -134,11 +134,12 @@ #' #' p6 <- vis_clus( #' spe = spe_xenium, -#' clustervar = "layer_guess_reordered", +#' clustervar = "x_half", #' sampleid = "sample1", #' colors = c(left = "red", right = "blue"), #' na_color = "white", #' point_size = 1, +#' alpha = 0.5, #' guide_point_size = 3, #' datatype = "Xenium" #' ) @@ -205,9 +206,7 @@ vis_clus <- function( } d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) - - spe_sub <- spe[, spe$sample_id == sampleid] - + if (is_stitched) { # Drop excluded spots and calculate an appropriate point size temp <- prep_stitched_data(spe_sub, point_size, image_id) @@ -243,6 +242,8 @@ vis_clus <- function( ) } + d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) + vis_clus_c( spe = spe_sub, d = d, From 3587f09463841edef4fb091706e675bb6e136619 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Tue, 5 May 2026 17:03:19 -0400 Subject: [PATCH 12/24] Update doc with fixed example --- man/vis_clus.Rd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index a66dd7b7..e8aa2ac1 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -171,11 +171,12 @@ if (enough_ram()) { p6 <- vis_clus( spe = spe_xenium, - clustervar = "layer_guess_reordered", + clustervar = "x_half", sampleid = "sample1", colors = c(left = "red", right = "blue"), na_color = "white", point_size = 1, + alpha = 0.5, guide_point_size = 3, datatype = "Xenium" ) From 92b7ae59f558d94a11cddefd0f4dcfa2b4d9939a Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 14:39:13 -0400 Subject: [PATCH 13/24] Fix cross link in docs (vis_gene_c not defined yet) --- R/vis_clus.R | 4 ++-- R/vis_clus_c.R | 3 ++- man/vis_clus.Rd | 4 ++-- man/vis_clus_c.Rd | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index 8036f2e4..6b5c658a 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -58,7 +58,7 @@ #' @export #' @importFrom SpatialExperiment spatialCoords #' @details This function subsets `spe` to the given sample and prepares the -#' data and title for [vis_clus_p()]. +#' data and title for [vis_clus_p()] or [vis_clus_c()]. #' #' @examples #' @@ -135,7 +135,7 @@ #' p6 <- vis_clus( #' spe = spe_xenium, #' clustervar = "x_half", -#' sampleid = "sample1", +#' sampleid = "sample2", #' colors = c(left = "red", right = "blue"), #' na_color = "white", #' point_size = 1, diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index baaec20c..002284b0 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -4,7 +4,8 @@ #' This function visualizes clusters or categorical variables for one given #' sample at the cell-level. This is the function that does all the plotting #' behind [vis_clus(datatype = "Xenium")]. To visualize gene-level -#' (or any continuous variable) use [vis_gene_c()]. +#' (or any continuous variable) use [vis_gene_p()] +#' TODO fix when vis_gene_c is defined. #' #' @inheritParams vis_clus #' @param d A `data.frame()` with the sample-level information. This is diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index e8aa2ac1..3ac00d7a 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -95,7 +95,7 @@ gene-level (or any continuous variable) use \code{\link[=vis_gene]{vis_gene()}}. } \details{ This function subsets \code{spe} to the given sample and prepares the -data and title for \code{\link[=vis_clus_p]{vis_clus_p()}}. +data and title for \code{\link[=vis_clus_p]{vis_clus_p()}} or \code{\link[=vis_clus_c]{vis_clus_c()}}. } \examples{ @@ -172,7 +172,7 @@ if (enough_ram()) { p6 <- vis_clus( spe = spe_xenium, clustervar = "x_half", - sampleid = "sample1", + sampleid = "sample2", colors = c(left = "red", right = "blue"), na_color = "white", point_size = 1, diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index c991baf4..f7ea4e30 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -63,7 +63,8 @@ A \link[ggplot2:ggplot]{ggplot2} object. This function visualizes clusters or categorical variables for one given sample at the cell-level. This is the function that does all the plotting behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level -(or any continuous variable) use \code{\link[=vis_gene_c]{vis_gene_c()}}. +(or any continuous variable) use \code{\link[=vis_gene_p]{vis_gene_p()}} +TODO fix when vis_gene_c is defined. } \examples{ From b84be4c35daaf8c5514de6735d374d0efa0d2943 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 14:40:57 -0400 Subject: [PATCH 14/24] Fix typo in example --- R/vis_clus.R | 2 +- man/vis_clus.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index 6b5c658a..b0687378 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -128,7 +128,7 @@ #' print(p5) #' #' ## Obtain the necessary data: Xenium example -#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_Xenium_test") +#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") #' #' spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") #' diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index 3ac00d7a..1561b23d 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -165,7 +165,7 @@ if (enough_ram()) { print(p5) ## Obtain the necessary data: Xenium example - if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_Xenium_test") + if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") From 1a0206d2dc676704a656da1432c9d77e789a08a0 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 14:44:43 -0400 Subject: [PATCH 15/24] Bump date and version --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6f46909e..1885d49e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: spatialLIBD Title: spatialLIBD: an R/Bioconductor package to visualize spatially-resolved transcriptomics data -Version: 1.23.2 -Date: 2026-01-09 +Version: 1.23.3 +Date: 2026-06-05 Authors@R: c( person("Leonardo", "Collado-Torres", role = c("aut", "cre"), From a4f21a32757422cb708e96ad7129e8184840db6b Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 15:28:14 -0400 Subject: [PATCH 16/24] Add vis_gene_c --- R/vis_gene_c.R | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 R/vis_gene_c.R diff --git a/R/vis_gene_c.R b/R/vis_gene_c.R new file mode 100644 index 00000000..ee3fe9e5 --- /dev/null +++ b/R/vis_gene_c.R @@ -0,0 +1,128 @@ +#' Sample spatial gene visualization workhorse function for Xenium data with +#' centroid based spatailCoords +#' +#' This function visualizes the gene expression stored in `assays(spe)` or any +#' continuous variable stored in `colData(spe)` for one given sample at the +#' spot-level using (by default) the histology information on the background. +#' This is the function that does all the plotting behind +#' [vis_gene(datatype = "Xenium")] +#' To visualize clusters (or any discrete variable) use [vis_clus_c()]. +#' +#' @param d A `data.frame()` with the sample-level information. This is +#' typically obtained using `cbind(colData(spe), spatialCoords(spe))`. +#' The `data.frame` has to contain +#' a column with the continuous variable data to plot stored under `d$COUNT`. +#' @param legend_title A `character(1)` specifying the legend title. +#' @inheritParams vis_clus_c +#' @inheritParams vis_gene +#' +#' @return A [ggplot2][ggplot2::ggplot] object. +#' @export +#' @importFrom tibble tibble +#' @importFrom SpatialExperiment imgData scaleFactors +#' @importFrom S4Vectors metadata +#' @importFrom grid rasterGrob unit +#' @family Spatial gene visualization functions +#' +#' @examples +#' +#' if (enough_ram()) { +#' ## Obtain the necessary data +#' if (!exists("spe")) spe <- fetch_data("spe_xenium_test") +#' +#' ## Prepare the data for the plotting function +#' spe_sub <- spe[, spe$sample_id == "sample1"] +#' df <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) +#' df$COUNT <- df$detected_gex +#' +#' ## Don't plot the histology information +#' p <- vis_gene_c( +#' spe = spe_sub, +#' d = df, +#' sampleid = "sample1", +#' title = "sample1 detected_gex", +#' point_size = 1 +#' ) +#' print(p) +#' +#' ## Clean up +#' rm(spe_sub) +#' } +vis_gene_c <- + function(spe, + d, + sampleid = unique(spe$sample_id)[1], + title, + viridis = TRUE, + alpha = NA, + cont_colors = if (viridis) viridisLite::viridis(21) else c("aquamarine4", "springgreen", "goldenrod", "red"), + point_size = 2, + na_color = "#CCCCCC40", + legend_title = "") { + ## Some variables + y_centroid <- x_centroid <- key <- COUNT <- NULL + # stopifnot(all(c("x_centroid", "y_centroid", "COUNT", "key") %in% colnames(d))) + + + # ## Crop the image if needed + # if (auto_crop) { + # frame_lims <- + # frame_limits(spe, sampleid = sampleid, image_id = image_id) + # img <- + # img[frame_lims$y_min:frame_lims$y_max, frame_lims$x_min:frame_lims$x_max] + # adjust <- + # list(x = frame_lims$x_min, y = frame_lims$y_min) + # } else { + adjust <- list(x = 0, y = 0) + # } + + p <- + ggplot( + d, + aes( + x = x_centroid, + y = y_centroid, + fill = COUNT, + color = COUNT, + key = key + ) + ) + + p <- p + + geom_point( + shape = 21, + size = point_size, + stroke = 0, + colour = "transparent", + alpha = alpha + ) + + coord_fixed(expand = FALSE) + + p <- p + scale_fill_gradientn( + name = legend_title, + colors = cont_colors, + na.value = na_color + ) + + scale_color_gradientn( + name = legend_title, + colors = cont_colors, + na.value = na_color + ) + + p <- p + + xlab("") + ylab("") + + labs(fill = NULL, color = NULL) + + ggtitle(title) + + theme_set(theme_bw(base_size = 20)) + + theme( + panel.grid.major = element_blank(), + panel.grid.minor = element_blank(), + panel.background = element_blank(), + axis.line = element_blank(), + axis.text = element_blank(), + axis.ticks = element_blank(), + legend.title = element_text(size = 10), + legend.box.spacing = unit(0, "pt") + ) + return(p) + } From 0823d989d3fcfba23bdb96dc1351055b431162a5 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 15:29:05 -0400 Subject: [PATCH 17/24] Update docs --- NAMESPACE | 1 + R/vis_clus_c.R | 5 +-- man/vis_clus_c.Rd | 5 +-- man/vis_gene.Rd | 1 + man/vis_gene_c.Rd | 105 +++++++++++++++++++++++++++++++++++++++++++ man/vis_gene_p.Rd | 1 + man/vis_grid_gene.Rd | 1 + 7 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 man/vis_gene_c.Rd diff --git a/NAMESPACE b/NAMESPACE index 1d34a0d6..bad76dfe 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -43,6 +43,7 @@ export(vis_clus) export(vis_clus_c) export(vis_clus_p) export(vis_gene) +export(vis_gene_c) export(vis_gene_p) export(vis_grid_clus) export(vis_grid_gene) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index 002284b0..0c1981be 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -4,8 +4,7 @@ #' This function visualizes clusters or categorical variables for one given #' sample at the cell-level. This is the function that does all the plotting #' behind [vis_clus(datatype = "Xenium")]. To visualize gene-level -#' (or any continuous variable) use [vis_gene_p()] -#' TODO fix when vis_gene_c is defined. +#' (or any continuous variable) use [vis_gene_c()]. #' #' @inheritParams vis_clus #' @param d A `data.frame()` with the sample-level information. This is @@ -24,7 +23,7 @@ #' #' if (enough_ram()) { #' ## Obtain the necessary data -#' if (!exists("spe")) spe <- fetch_data("spe_Xenium_test") +#' if (!exists("spe")) spe <- fetch_data("spe_xenium_test") #' #' # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") #' diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index f7ea4e30..8149ea63 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -63,14 +63,13 @@ A \link[ggplot2:ggplot]{ggplot2} object. This function visualizes clusters or categorical variables for one given sample at the cell-level. This is the function that does all the plotting behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level -(or any continuous variable) use \code{\link[=vis_gene_p]{vis_gene_p()}} -TODO fix when vis_gene_c is defined. +(or any continuous variable) use \code{\link[=vis_gene_c]{vis_gene_c()}}. } \examples{ if (enough_ram()) { ## Obtain the necessary data - if (!exists("spe")) spe <- fetch_data("spe_Xenium_test") + if (!exists("spe")) spe <- fetch_data("spe_xenium_test") # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") diff --git a/man/vis_gene.Rd b/man/vis_gene.Rd index 49fe828c..a0f4fe0f 100644 --- a/man/vis_gene.Rd +++ b/man/vis_gene.Rd @@ -238,6 +238,7 @@ if (enough_ram()) { } \seealso{ Other Spatial gene visualization functions: +\code{\link{vis_gene_c}()}, \code{\link{vis_gene_p}()}, \code{\link{vis_grid_gene}()} } diff --git a/man/vis_gene_c.Rd b/man/vis_gene_c.Rd new file mode 100644 index 00000000..dfe88b29 --- /dev/null +++ b/man/vis_gene_c.Rd @@ -0,0 +1,105 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/vis_gene_c.R +\name{vis_gene_c} +\alias{vis_gene_c} +\title{Sample spatial gene visualization workhorse function for Xenium data with +centroid based spatailCoords} +\usage{ +vis_gene_c( + spe, + d, + sampleid = unique(spe$sample_id)[1], + title, + viridis = TRUE, + alpha = NA, + cont_colors = if (viridis) viridisLite::viridis(21) else c("aquamarine4", + "springgreen", "goldenrod", "red"), + point_size = 2, + na_color = "#CCCCCC40", + legend_title = "" +) +} +\arguments{ +\item{spe}{A +\link[SpatialExperiment:SpatialExperiment]{SpatialExperiment-class} +object. See \code{\link[=fetch_data]{fetch_data()}} for how to download some example objects or +\code{\link[=read10xVisiumWrapper]{read10xVisiumWrapper()}} to read in \code{spaceranger --count} output files and +build your own \code{spe} object.} + +\item{d}{A \code{data.frame()} with the sample-level information. This is +typically obtained using \code{cbind(colData(spe), spatialCoords(spe))}. +The \code{data.frame} has to contain +a column with the continuous variable data to plot stored under \code{d$COUNT}.} + +\item{sampleid}{A \code{character(1)} specifying which sample to plot from +\code{colData(spe)$sample_id} (formerly \code{colData(spe)$sample_name}).} + +\item{title}{The title for the plot.} + +\item{viridis}{A \code{logical(1)} whether to use the color-blind friendly +palette from \link[viridisLite:viridis]{viridis} or the color palette used +in the paper that was chosen for contrast when visualizing the data on +top of the histology image. One issue is being able to differentiate low +values from NA ones due to the purple-ish histology information that is +dependent on cell density.} + +\item{alpha}{A \code{numeric(1)} in the \verb{[0, 1]} range that specifies the +transparency level of the data on the spots.} + +\item{cont_colors}{A \code{character()} vector of colors that supersedes the +\code{viridis} argument.} + +\item{point_size}{A \code{numeric(1)} specifying the size of the points. Defaults +to \code{1.25}. Some colors look better if you use \code{2} for instance.} + +\item{na_color}{A \code{character(1)} specifying a color for the NA values. +If you set \code{alpha = NA} then it's best to set \code{na_color} to a color that has +alpha blending already, which will make non-NA values pop up more and the NA +values will show with a lighter color. This behavior is lost when \code{alpha} is +set to a non-\code{NA} value.} + +\item{legend_title}{A \code{character(1)} specifying the legend title.} +} +\value{ +A \link[ggplot2:ggplot]{ggplot2} object. +} +\description{ +This function visualizes the gene expression stored in \code{assays(spe)} or any +continuous variable stored in \code{colData(spe)} for one given sample at the +spot-level using (by default) the histology information on the background. +This is the function that does all the plotting behind +\link{vis_gene(datatype = "Xenium")} +To visualize clusters (or any discrete variable) use \code{\link[=vis_clus_c]{vis_clus_c()}}. +} +\examples{ + +if (enough_ram()) { + ## Obtain the necessary data + if (!exists("spe")) spe <- fetch_data("spe_xenium_test") + + ## Prepare the data for the plotting function + spe_sub <- spe[, spe$sample_id == "sample1"] + df <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) + df$COUNT <- df$detected_gex + + ## Don't plot the histology information + p <- vis_gene_c( + spe = spe_sub, + d = df, + sampleid = "sample1", + title = "sample1 detected_gex", + point_size = 1 + ) + print(p) + + ## Clean up + rm(spe_sub) +} +} +\seealso{ +Other Spatial gene visualization functions: +\code{\link{vis_gene}()}, +\code{\link{vis_gene_p}()}, +\code{\link{vis_grid_gene}()} +} +\concept{Spatial gene visualization functions} diff --git a/man/vis_gene_p.Rd b/man/vis_gene_p.Rd index ca9d6a29..294bab48 100644 --- a/man/vis_gene_p.Rd +++ b/man/vis_gene_p.Rd @@ -111,6 +111,7 @@ if (enough_ram()) { \seealso{ Other Spatial gene visualization functions: \code{\link{vis_gene}()}, +\code{\link{vis_gene_c}()}, \code{\link{vis_grid_gene}()} } \concept{Spatial gene visualization functions} diff --git a/man/vis_grid_gene.Rd b/man/vis_grid_gene.Rd index 647cdce9..77018545 100644 --- a/man/vis_grid_gene.Rd +++ b/man/vis_grid_gene.Rd @@ -147,6 +147,7 @@ if (enough_ram()) { \seealso{ Other Spatial gene visualization functions: \code{\link{vis_gene}()}, +\code{\link{vis_gene_c}()}, \code{\link{vis_gene_p}()} } \concept{Spatial gene visualization functions} From 25cb967f24d83cf279561ddc56210923398fd12d Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 15:36:55 -0400 Subject: [PATCH 18/24] Add datatype arg to vis_gene --- R/vis_gene.R | 10 ++++++++++ man/vis_gene.Rd | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/R/vis_gene.R b/R/vis_gene.R index 1e129164..d87cc497 100644 --- a/R/vis_gene.R +++ b/R/vis_gene.R @@ -41,6 +41,15 @@ #' of 0.95 sets the top 5% of expression values to the 95th percentile value. #' This can help make the color scale more dynamic in the presence of high #' outliers. Defaults to `1`, which effectively performs no capping. +#' @param datatype A `character(1)` specifying the type of spatial transcriptomics +#' data stored in `spe`. Supported options are: +#' \describe{ +#' \item{`"Visium"`}{(Default) Expects `pxl_col_in_fullres` and +#' `pxl_row_in_fullres` as columns of `spatialCoords(spe)`. Enables +#' image handling via the `spatialData` slot.} +#' \item{`"Xenium"`}{Expects `x_centroid` and `y_centroid` as columns +#' of `spatialCoords(spe)`.} +#' } #' #' @return A [ggplot2][ggplot2::ggplot] object. #' @export @@ -180,6 +189,7 @@ vis_gene <- multi_gene_method = c("z_score", "pca", "sparsity"), is_stitched = FALSE, cap_percentile = 1, + datatype = c("Visium", "Xenium"), ...) { multi_gene_method <- rlang::arg_match(multi_gene_method) # Verify existence and legitimacy of 'sampleid' diff --git a/man/vis_gene.Rd b/man/vis_gene.Rd index a0f4fe0f..047a9217 100644 --- a/man/vis_gene.Rd +++ b/man/vis_gene.Rd @@ -22,6 +22,7 @@ vis_gene( multi_gene_method = c("z_score", "pca", "sparsity"), is_stitched = FALSE, cap_percentile = 1, + datatype = c("Visium", "Xenium"), ... ) } @@ -107,6 +108,16 @@ of 0.95 sets the top 5\% of expression values to the 95th percentile value. This can help make the color scale more dynamic in the presence of high outliers. Defaults to \code{1}, which effectively performs no capping.} +\item{datatype}{A \code{character(1)} specifying the type of spatial transcriptomics +data stored in \code{spe}. Supported options are: +\describe{ +\item{\code{"Visium"}}{(Default) Expects \code{pxl_col_in_fullres} and +\code{pxl_row_in_fullres} as columns of \code{spatialCoords(spe)}. Enables +image handling via the \code{spatialData} slot.} +\item{\code{"Xenium"}}{Expects \code{x_centroid} and \code{y_centroid} as columns +of \code{spatialCoords(spe)}.} +}} + \item{...}{Passed to \link[base:paste]{paste0()} for making the title of the plot following the \code{sampleid}.} } From ab82404d0debdbc19dc95b25259ec6490d1a43d1 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 15:48:14 -0400 Subject: [PATCH 19/24] Fix typo --- R/vis_clus.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index b0687378..c4505429 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -237,7 +237,7 @@ vis_clus <- function( if (datatype == "Xenium" & !setequal(c("x_centroid", "y_centroid"), colnames(spatialCoords(spe)))) { stop( - "Abnormal spatial coordinates for Xisium datatype: should have 'x_centroid' and 'y_centroid' columns.", + "Abnormal spatial coordinates for Xenium datatype: should have 'x_centroid' and 'y_centroid' columns.", call. = FALSE ) } From 7d0ac11111ecd8fefa27563dc41b6b5269bf949b Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 15:48:47 -0400 Subject: [PATCH 20/24] Adapt vis_gene for xenium data --- R/vis_gene.R | 270 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 194 insertions(+), 76 deletions(-) diff --git a/R/vis_gene.R b/R/vis_gene.R index d87cc497..b86b6e67 100644 --- a/R/vis_gene.R +++ b/R/vis_gene.R @@ -209,114 +209,118 @@ vis_gene <- if (!(assayname %in% names(assays(spe)))) { stop(sprintf("'%s' is not an assay in 'spe'", assayname), call. = FALSE) } - - # Check validity of spatial coordinates - if (!setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { + + ## Check for valid datatype + datatype <- match.arg(datatype) + + if(datatype == "Visium"){ + # Check validity of spatial coordinates + if (!setequal(c("pxl_col_in_fullres", "pxl_row_in_fullres"), colnames(spatialCoords(spe)))) { stop( - "Abnormal spatial coordinates: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", - call. = FALSE + "Abnormal spatial coordinates for Visium datatype: should have 'pxl_row_in_fullres' and 'pxl_col_in_fullres' columns.", + call. = FALSE ) - } - - # Validate 'cap_percentile' - if (cap_percentile <= 0 || cap_percentile > 1) { + } + + # Validate 'cap_percentile' + if (cap_percentile <= 0 || cap_percentile > 1) { stop("'cap_percentile' must be in (0, 1]", call. = FALSE) - } - - spe_sub <- spe[, spe$sample_id == sampleid] - - if (is_stitched) { + } + + spe_sub <- spe[, spe$sample_id == sampleid] + + if (is_stitched) { # Drop excluded spots and calculate an appropriate point size temp <- prep_stitched_data(spe_sub, point_size, image_id) spe_sub <- temp$spe point_size <- temp$point_size - + # Frame limits are poorly defined for stitched data auto_crop <- FALSE - } - - d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) - - # Verify legitimacy of names in geneid - geneid_is_valid <- (geneid %in% rowData(spe_sub)$gene_search) | + } + + d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) + + # Verify legitimacy of names in geneid + geneid_is_valid <- (geneid %in% rowData(spe_sub)$gene_search) | (geneid %in% rownames(spe_sub)) | (geneid %in% colnames(colData(spe_sub))) - if (any(!geneid_is_valid)) { + if (any(!geneid_is_valid)) { stop( - "Could not find the 'geneid'(s) ", - paste(geneid[!geneid_is_valid], collapse = ", "), - call. = FALSE + "Could not find the 'geneid'(s) ", + paste(geneid[!geneid_is_valid], collapse = ", "), + call. = FALSE ) - } - - # Grab any continuous colData columns and verify they're all numeric - cont_cols <- colData(spe_sub)[ + } + + # Grab any continuous colData columns and verify they're all numeric + cont_cols <- colData(spe_sub)[ , geneid[geneid %in% colnames(colData(spe_sub))], drop = FALSE - ] - if (!all(sapply(cont_cols, class) %in% c("numeric", "integer"))) { + ] + if (!all(sapply(cont_cols, class) %in% c("numeric", "integer"))) { stop( - "'geneid' can not contain non-numeric colData columns.", - call. = FALSE + "'geneid' can not contain non-numeric colData columns.", + call. = FALSE ) - } - cont_cols <- as.matrix(cont_cols) - - # Get the integer indices of each gene in the SpatialExperiment, since we - # aren't guaranteed that rownames are gene names - remaining_geneid <- geneid[!(geneid %in% colnames(colData(spe_sub)))] - valid_gene_indices <- unique( + } + cont_cols <- as.matrix(cont_cols) + + # Get the integer indices of each gene in the SpatialExperiment, since we + # aren't guaranteed that rownames are gene names + remaining_geneid <- geneid[!(geneid %in% colnames(colData(spe_sub)))] + valid_gene_indices <- unique( c( - match(remaining_geneid, rowData(spe_sub)$gene_search), - match(remaining_geneid, rownames(spe_sub)) + match(remaining_geneid, rowData(spe_sub)$gene_search), + match(remaining_geneid, rownames(spe_sub)) ) - ) - valid_gene_indices <- valid_gene_indices[!is.na(valid_gene_indices)] - - # Grab any genes - gene_cols <- t( + ) + valid_gene_indices <- valid_gene_indices[!is.na(valid_gene_indices)] + + # Grab any genes + gene_cols <- t( as.matrix(assays(spe_sub[valid_gene_indices, ])[[assayname]]) - ) - - # Combine into one matrix where rows are samples and columns are continuous - # features - cont_matrix <- cbind(cont_cols, gene_cols) - - # Determine plot and legend titles - if (ncol(cont_matrix) == 1) { + ) + + # Combine into one matrix where rows are samples and columns are continuous + # features + cont_matrix <- cbind(cont_cols, gene_cols) + + # Determine plot and legend titles + if (ncol(cont_matrix) == 1) { plot_title <- paste(sampleid, geneid, ...) d$COUNT <- cont_matrix[, 1] if (!(geneid %in% colnames(colData(spe_sub)))) { - legend_title <- sprintf("%s\n min > %s", assayname, minCount) + legend_title <- sprintf("%s\n min > %s", assayname, minCount) } else { - legend_title <- sprintf("min > %s", minCount) + legend_title <- sprintf("min > %s", minCount) } - } else { + } else { plot_title <- paste(sampleid, ...) if (multi_gene_method == "z_score") { - d$COUNT <- multi_gene_z_score(cont_matrix) - legend_title <- paste("Z score\n min > ", minCount) + d$COUNT <- multi_gene_z_score(cont_matrix) + legend_title <- paste("Z score\n min > ", minCount) } else if (multi_gene_method == "sparsity") { - d$COUNT <- multi_gene_sparsity(cont_matrix) - legend_title <- paste("Prop. nonzero\n min > ", minCount) + d$COUNT <- multi_gene_sparsity(cont_matrix) + legend_title <- paste("Prop. nonzero\n min > ", minCount) } else { # must be 'pca' - d$COUNT <- multi_gene_pca(cont_matrix) - legend_title <- paste("PC1\n min > ", minCount) + d$COUNT <- multi_gene_pca(cont_matrix) + legend_title <- paste("PC1\n min > ", minCount) } - } - - # Cap the expression values at the given percentile, if applicable - if (cap_percentile < 1) { + } + + # Cap the expression values at the given percentile, if applicable + if (cap_percentile < 1) { sorted_count <- sort(d$COUNT) cap <- sorted_count[ - as.integer(round(length(sorted_count) * cap_percentile)) + as.integer(round(length(sorted_count) * cap_percentile)) ] d$COUNT[d$COUNT > cap] <- cap - } - - d$COUNT[d$COUNT <= minCount] <- NA - - p <- vis_gene_p( + } + + d$COUNT[d$COUNT <= minCount] <- NA + + p <- vis_gene_p( spe = spe_sub, d = d, sampleid = sampleid, @@ -330,6 +334,120 @@ vis_gene <- auto_crop = auto_crop, na_color = na_color, legend_title = legend_title - ) - return(p) + ) + return(p) + } else if(datatype == "Xenium"){ + # Check validity of spatial coordinates + if (!setequal(c("x_centroid", "y_centroid"), colnames(spatialCoords(spe)))) { + stop( + "Abnormal spatial coordinates for Xenium datatype: should have 'x_centroid' and 'y_centroid' columns.", + call. = FALSE + ) + } + + # Validate 'cap_percentile' + if (cap_percentile <= 0 || cap_percentile > 1) { + stop("'cap_percentile' must be in (0, 1]", call. = FALSE) + } + + spe_sub <- spe[, spe$sample_id == sampleid] + + d <- as.data.frame(cbind(colData(spe_sub), SpatialExperiment::spatialCoords(spe_sub)), optional = TRUE) + + # Verify legitimacy of names in geneid + geneid_is_valid <- (geneid %in% rowData(spe_sub)$gene_search) | + (geneid %in% rownames(spe_sub)) | + (geneid %in% colnames(colData(spe_sub))) + if (any(!geneid_is_valid)) { + stop( + "Could not find the 'geneid'(s) ", + paste(geneid[!geneid_is_valid], collapse = ", "), + call. = FALSE + ) + } + + # Grab any continuous colData columns and verify they're all numeric + cont_cols <- colData(spe_sub)[ + , geneid[geneid %in% colnames(colData(spe_sub))], + drop = FALSE + ] + if (!all(sapply(cont_cols, class) %in% c("numeric", "integer"))) { + stop( + "'geneid' can not contain non-numeric colData columns.", + call. = FALSE + ) + } + cont_cols <- as.matrix(cont_cols) + + # Get the integer indices of each gene in the SpatialExperiment, since we + # aren't guaranteed that rownames are gene names + remaining_geneid <- geneid[!(geneid %in% colnames(colData(spe_sub)))] + valid_gene_indices <- unique( + c( + match(remaining_geneid, rowData(spe_sub)$gene_search), + match(remaining_geneid, rownames(spe_sub)) + ) + ) + valid_gene_indices <- valid_gene_indices[!is.na(valid_gene_indices)] + + # Grab any genes + gene_cols <- t( + as.matrix(assays(spe_sub[valid_gene_indices, ])[[assayname]]) + ) + + # Combine into one matrix where rows are samples and columns are continuous + # features + cont_matrix <- cbind(cont_cols, gene_cols) + + # Determine plot and legend titles + if (ncol(cont_matrix) == 1) { + plot_title <- paste(sampleid, geneid, ...) + d$COUNT <- cont_matrix[, 1] + if (!(geneid %in% colnames(colData(spe_sub)))) { + legend_title <- sprintf("%s\n min > %s", assayname, minCount) + } else { + legend_title <- sprintf("min > %s", minCount) + } + } else { + plot_title <- paste(sampleid, ...) + if (multi_gene_method == "z_score") { + d$COUNT <- multi_gene_z_score(cont_matrix) + legend_title <- paste("Z score\n min > ", minCount) + } else if (multi_gene_method == "sparsity") { + d$COUNT <- multi_gene_sparsity(cont_matrix) + legend_title <- paste("Prop. nonzero\n min > ", minCount) + } else { # must be 'pca' + d$COUNT <- multi_gene_pca(cont_matrix) + legend_title <- paste("PC1\n min > ", minCount) + } + } + + # Cap the expression values at the given percentile, if applicable + if (cap_percentile < 1) { + sorted_count <- sort(d$COUNT) + cap <- sorted_count[ + as.integer(round(length(sorted_count) * cap_percentile)) + ] + d$COUNT[d$COUNT > cap] <- cap + } + + d$COUNT[d$COUNT <= minCount] <- NA + + p <- vis_gene_c( + spe = spe_sub, + d = d, + sampleid = sampleid, + title = plot_title, + viridis = viridis, + alpha = alpha, + cont_colors = cont_colors, + point_size = point_size, + auto_crop = auto_crop, + na_color = na_color, + legend_title = legend_title + ) + return(p) + } + + } From 4134d6a8631fbee403fc37d4ac121720b7e036d0 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Wed, 6 May 2026 16:02:52 -0400 Subject: [PATCH 21/24] Add xenium example, fix auto_crop --- R/vis_gene.R | 15 ++++++++++++++- man/vis_gene.Rd | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/R/vis_gene.R b/R/vis_gene.R index b86b6e67..8c8bced3 100644 --- a/R/vis_gene.R +++ b/R/vis_gene.R @@ -171,6 +171,20 @@ #' multi_gene_method = "pca" #' ) #' print(p8) +#' +#' ## Obtain the necessary data: Xenium example +#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") +#' +#' p9 <- vis_gene( +#' spe = spe_xenium, +#' sampleid = "sample2", +#' geneid = "MBP", +#' assayname = "counts", +#' point_size = 1, +#' datatype = "Xenium" +#' ) +#' print(p9) +#' #' } vis_gene <- function(spe, @@ -442,7 +456,6 @@ vis_gene <- alpha = alpha, cont_colors = cont_colors, point_size = point_size, - auto_crop = auto_crop, na_color = na_color, legend_title = legend_title ) diff --git a/man/vis_gene.Rd b/man/vis_gene.Rd index 047a9217..19236d96 100644 --- a/man/vis_gene.Rd +++ b/man/vis_gene.Rd @@ -245,6 +245,20 @@ if (enough_ram()) { multi_gene_method = "pca" ) print(p8) + + ## Obtain the necessary data: Xenium example + if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") + + p9 <- vis_gene( + spe = spe_xenium, + sampleid = "sample2", + geneid = "MBP", + assayname = "counts", + point_size = 1, + datatype = "Xenium" + ) + print(p9) + } } \seealso{ From 3649ebf28841be6bef2c0656455a872f885a71bf Mon Sep 17 00:00:00 2001 From: lahuuki Date: Thu, 7 May 2026 10:02:49 -0400 Subject: [PATCH 22/24] Update date --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 1885d49e..4817bb91 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: spatialLIBD Title: spatialLIBD: an R/Bioconductor package to visualize spatially-resolved transcriptomics data Version: 1.23.3 -Date: 2026-06-05 +Date: 2026-06-07 Authors@R: c( person("Leonardo", "Collado-Torres", role = c("aut", "cre"), From dfb9a3c2e198f258501acd7b544b9485bf5dd801 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Thu, 7 May 2026 10:47:04 -0400 Subject: [PATCH 23/24] fix spe_xenium_example name --- R/vis_clus.R | 2 +- R/vis_clus_c.R | 2 +- man/vis_clus.Rd | 2 +- man/vis_clus_c.Rd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/vis_clus.R b/R/vis_clus.R index c4505429..02af7983 100644 --- a/R/vis_clus.R +++ b/R/vis_clus.R @@ -128,7 +128,7 @@ #' print(p5) #' #' ## Obtain the necessary data: Xenium example -#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") +#' if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_example") #' #' spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") #' diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index 0c1981be..e1e9ba6e 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -23,7 +23,7 @@ #' #' if (enough_ram()) { #' ## Obtain the necessary data -#' if (!exists("spe")) spe <- fetch_data("spe_xenium_test") +#' if (!exists("spe")) spe <- fetch_data("spe_xenium_example") #' #' # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") #' diff --git a/man/vis_clus.Rd b/man/vis_clus.Rd index 1561b23d..ce8faf8d 100644 --- a/man/vis_clus.Rd +++ b/man/vis_clus.Rd @@ -165,7 +165,7 @@ if (enough_ram()) { print(p5) ## Obtain the necessary data: Xenium example - if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_test") + if (!exists("spe_xenium")) spe_xenium <- fetch_data("spe_xenium_example") spe_xenium$x_half <- ifelse(spatialCoords(spe_xenium)[,"x_centroid"] < 3088, "left", "right") diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index 8149ea63..615c070e 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -69,7 +69,7 @@ behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level if (enough_ram()) { ## Obtain the necessary data - if (!exists("spe")) spe <- fetch_data("spe_xenium_test") + if (!exists("spe")) spe <- fetch_data("spe_xenium_example") # spe <- readRDS("../Xenium/XeniumIO_test/spe_Xenium_test.rds") From 3f56fba4ce535c06eb0b90dd6f1183c2615cfb99 Mon Sep 17 00:00:00 2001 From: lahuuki Date: Thu, 7 May 2026 10:50:42 -0400 Subject: [PATCH 24/24] Fix Cross-refrences in descriptions --- R/vis_clus_c.R | 2 +- R/vis_gene_c.R | 6 +++--- man/vis_clus_c.Rd | 2 +- man/vis_gene_c.Rd | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/R/vis_clus_c.R b/R/vis_clus_c.R index e1e9ba6e..59d2f366 100644 --- a/R/vis_clus_c.R +++ b/R/vis_clus_c.R @@ -3,7 +3,7 @@ #' #' This function visualizes clusters or categorical variables for one given #' sample at the cell-level. This is the function that does all the plotting -#' behind [vis_clus(datatype = "Xenium")]. To visualize gene-level +#' behind [vis_clus()] when `datatype = "Xenium"`. To visualize gene-level #' (or any continuous variable) use [vis_gene_c()]. #' #' @inheritParams vis_clus diff --git a/R/vis_gene_c.R b/R/vis_gene_c.R index ee3fe9e5..201330ae 100644 --- a/R/vis_gene_c.R +++ b/R/vis_gene_c.R @@ -4,9 +4,9 @@ #' This function visualizes the gene expression stored in `assays(spe)` or any #' continuous variable stored in `colData(spe)` for one given sample at the #' spot-level using (by default) the histology information on the background. -#' This is the function that does all the plotting behind -#' [vis_gene(datatype = "Xenium")] -#' To visualize clusters (or any discrete variable) use [vis_clus_c()]. +#' This is the function that does all the plotting behind [vis_clus()] when +#' `datatype = "Xenium"`. To visualize clusters (or any discrete variable) +#' use [vis_clus_c()]. #' #' @param d A `data.frame()` with the sample-level information. This is #' typically obtained using `cbind(colData(spe), spatialCoords(spe))`. diff --git a/man/vis_clus_c.Rd b/man/vis_clus_c.Rd index 615c070e..f049eafd 100644 --- a/man/vis_clus_c.Rd +++ b/man/vis_clus_c.Rd @@ -62,7 +62,7 @@ A \link[ggplot2:ggplot]{ggplot2} object. \description{ This function visualizes clusters or categorical variables for one given sample at the cell-level. This is the function that does all the plotting -behind \link{vis_clus(datatype = "Xenium")}. To visualize gene-level +behind \code{\link[=vis_clus]{vis_clus()}} when \code{datatype = "Xenium"}. To visualize gene-level (or any continuous variable) use \code{\link[=vis_gene_c]{vis_gene_c()}}. } \examples{ diff --git a/man/vis_gene_c.Rd b/man/vis_gene_c.Rd index dfe88b29..0b5f5f63 100644 --- a/man/vis_gene_c.Rd +++ b/man/vis_gene_c.Rd @@ -67,9 +67,9 @@ A \link[ggplot2:ggplot]{ggplot2} object. This function visualizes the gene expression stored in \code{assays(spe)} or any continuous variable stored in \code{colData(spe)} for one given sample at the spot-level using (by default) the histology information on the background. -This is the function that does all the plotting behind -\link{vis_gene(datatype = "Xenium")} -To visualize clusters (or any discrete variable) use \code{\link[=vis_clus_c]{vis_clus_c()}}. +This is the function that does all the plotting behind \code{\link[=vis_clus]{vis_clus()}} when +\code{datatype = "Xenium"}. To visualize clusters (or any discrete variable) +use \code{\link[=vis_clus_c]{vis_clus_c()}}. } \examples{