Using RGraphSpace with Spatial Feature Data
Sysbiolab Team
2026-08-01
Source:vignettes/articles/spatial-data.Rmd
spatial-data.RmdPackage: RGraphSpace 1.5.1
Overview
This vignette demonstrates how RGraphSpace renders spatial
transcriptomics data. Using spatial data from the SeuratData
package, we show how a graph is overlaid on a reference tissue image and
high-dimensional features are mapped to ggplot2 aesthetics
through the GraphSpace interface.
Before you start
This vignette assumes familiarity with Seurat (Hao et al. 2024), particularly for handling spatial transcriptomics data.
Note: If you are new to Seurat, we recommend reviewing its spatial analysis tutorials before proceeding.
Computational requirement:
Hardware: RAM >= 16 GB
Software: R (>=4.5) and RStudio
Required packages
Before proceeding, ensure that all packages described in the Installation Instructions are installed.
# Check versions
if (packageVersion("RGraphSpace") < "1.5.1"){
message("Need to update 'RGraphSpace' for this vignette")
remotes::install_github("sysbiolab/RGraphSpace")
}
if (packageVersion("Seurat") < "5.5.0"){
message("Need to update 'Seurat' for this vignette")
remotes::install_github("satijalab/Seurat")
}Setting input data
# Load packages
library("RGraphSpace")
library("Seurat")
library("SeuratObject")
library("SeuratData")Loading the dataset
We will use the stxBrain dataset from the
SeuratData package, consisting of spatial transcriptomics data
from sagittal mouse brain sections generated with Visium v1 technology.
This dataset is commonly used to demonstrate Seurat spatial
workflows (Hao et al. 2024). We apply
as.GraphSpace() to coerce the Seurat object
into a GraphSpace and show how its spatial features can be
mapped to ggplot2 aesthetics, anchored to the tissue image from
which the data were sampled.
# Install a Seurat dataset (required only once)
SeuratData::InstallData("stxBrain")
# Check manifest of installed datasets
# SeuratData::InstalledData()
# Load the 'stxBrain' dataset
# Note: LoadData() may print conversion warnings when loading pbmc3k.
# These are expected and come from SeuratData's internal v4-to-v5
# object migration — they can be safely ignored.
seurat_obj <- LoadData("stxBrain", type = "anterior1")Preprocessing
The stxBrain dataset is normalized as suggested in
Seurat’s spatial_vignette, either using the
SCTransform() and NormalizeData()
functions.
# NOTE: Seurat recommends using SCTransform() for processing this
# spatial dataset, which may require more computation time. Here,
# we use log-normalization for demonstration purposes.
seurat_obj <- NormalizeData(seurat_obj)Creating a GraphSpace object
Next, we create a GraphSpace from the
Seurat object; as.GraphSpace() brings its
spatial coordinates and feature data into the GraphSpace,
making them available for aesthetic mapping. We then attach the tissue
image and normalize node coordinates to the image space.
# Create a GraphSpace from 'seurat_obj'
gs <- as.GraphSpace(seurat_obj, space = "spatial", scale = "lowres")
#> Seurat object converted to GraphSpace:
#> ℹ space=spatial, layer=default, features=31053, samples=2696, scale="lowres"
#> Node spatial boundaries:
#> ℹ x: [76, 493] (cols)
#> ℹ y: [138, 541] (rows)
# If available, add tissue image
gs_image(gs) <- SeuratObject::GetImage(seurat_obj, mode = "raster")
#> Image spatial boundaries:
#> ℹ x: [1, 600] (cols)
#> ℹ y: [1, 599] (rows)
# Normalize node coordinates to the image space
gs <- normalizeGraphSpace(gs)
#> Normalizing node coordinates to image space...
#> Flipping y-coordinates...
gs
#> A GraphSpace-class object for:
#> IGRAPH 6fd732c UN-- 2696 0 --
#> + attr: x (v/n), y (v/n), name (v/c), nodeLabel (v/c), nodeSize (v/n), cell (v/c),
#> | orig.ident (v/x), nCount_Spatial (v/n), nFeature_Spatial (v/n), slice (v/n), region
#> | (v/c), arrowType (e/n)
#> + features: 31053 (Xkr4, Gm1992, Gm37381, Rp1, ...)
#> + samples: 2696 (AAACAAGTATCTCCCA-1, AAACACCAATAACTGC-1, ...)
#> + node spatial boundaries: normalized to image space
#> | x: [76, 493] -> [0, 1] (cols)
#> | y: [138, 541] -> [0, 1] (rows)
#> + image spatial boundaries: cropped to graph space
#> | x: [1, 600] -> [1, 522] (cols)
#> | y: [1, 599] -> [1, 522] (rows)Spatial feature visualization
With the GraphSpace object ready, we can reproduce a
spatial feature plot of the kind familiar from Seurat, using
standard ggplot2 mappings. Here we map expression of the
Ttr gene to the colour aesthetic and display
the tissue image as a background reference.
cpal <- hcl.colors(100, palette = "Spectral", rev = TRUE)
# Reproduce a typical Seurat's spatial feature visualization
ggplot(gs) +
annotation_gspace_image(gs) +
geom_nodespace(mapping = aes(colour = Ttr), size = 1, pch = 19) +
scale_colour_continuous(palette = cpal) +
theme_gspace_coords(theme = "th3", is_norm = TRUE,
xlab = "Tissue coordinates 1", ylab = "Tissue coordinates 2")
Note on image alignment: Proper spatial alignment
between nodes and the background image requires consistent coordinate
conventions. Spatial misalignment may occur if the input image and node
coordinates differ in axis orientation (e.g., top-left versus
bottom-left origins). To accommodate these differences,
normalizeGraphSpace() provides orientation controls through
the swap.xy, flip.x, and flip.y
arguments. If the nodes appear misaligned with the input image, try
combinations of these parameters to correct the alignment.
Alternatively, try flip.v and flip.h arguments
to apply flipping directly to the background image.
Spatial cluster visualization
This section requires additional preprocessing of the
stxBrain dataset, including normalization with
SCTransform() and Seurat’s clustering workflow. We
recommend installing the glmGamPoi package beforehand, as it
substantially speeds up the SCTransform() estimation
step.
Preprocessing
if (!require("glmGamPoi", quietly = TRUE)){
BiocManager::install("glmGamPoi")
}
# Run vst normalization on counts
seurat_obj <- SCTransform(seurat_obj, assay = "Spatial", verbose = FALSE)
seurat_obj <- RunPCA(seurat_obj, assay = "SCT", verbose = FALSE)
seurat_obj <- FindNeighbors(seurat_obj, reduction = "pca", dims = 1:30)
seurat_obj <- FindClusters(seurat_obj, verbose = FALSE)Spatial cluster visualization
With clusters assigned, we rebuild the GraphSpace object
from the updated seurat_obj and reproduce a spatial cluster
plot, mapping cluster identity to the fill aesthetic and
overlaying the tissue image as a dimmed background.
# Re-create a GraphSpace from the updated 'seurat_obj'
gs <- as.GraphSpace(seurat_obj, space = "spatial", scale = "lowres")
gs_image(gs) <- SeuratObject::GetImage(seurat_obj, mode = "raster")
gs <- normalizeGraphSpace(gs)
# Reproduce a typical Seurat cluster visualization
cpal <- DiscretePalette(nlevels(gs$seurat_clusters), palette = "polychrome")
ggplot(gs) +
annotation_gspace_image(gs, opacity = 0.5) +
geom_nodespace(mapping = aes(fill = seurat_clusters),
size = 1.3, color = "grey90", stroke = 0.3) +
scale_fill_manual(values = cpal) +
theme_gspace_coords(theme = "th2", is_norm = TRUE,
xlab = "Tissue coordinates 1", ylab = "Tissue coordinates 2") +
theme_gspace_legend(discrete_fill = TRUE)
Coercing spatial data
Below, we show how to access the relevant components of a
Seurat object and use them to construct a
GraphSpace manually, without relying on
as.GraphSpace(). For another coercion example, see the high-dimensional
data tutorial.
# Extract tissue coordinates
coords <- SeuratObject::GetTissueCoordinates(object = seurat_obj, scale = "lowres")
coords <- as.data.frame(coords)
all(c("x", "y") %in% colnames(coords))
# [1] TRUE
# Extract cell metadata
metadata <- seurat_obj[[]]
# Merge coordinates and metadata using common cell identifiers
ids <- intersect(rownames(coords), rownames(metadata))
coords <- cbind(coords[ids, ], metadata[ids, ])
# Construct a GraphSpace object
# Metadata become node attributes
gs <- GraphSpace(coords)
# Add high-dimensional feature data
# Stored separately for lazy aesthetic mapping
gs_fdata(gs) <- SeuratObject::LayerData(seurat_obj, layer = "data")
# If available, add tissue image
gs_image(gs) <- SeuratObject::GetImage(seurat_obj, mode = "raster")
# Normalize node coordinates to the image space
gs <- normalizeGraphSpace(gs)Session information
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: America/Sao_Paulo
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] stxBrain.SeuratData_0.1.2 ssHippo.SeuratData_3.1.4
#> [3] pbmc3k.SeuratData_3.1.4 SeuratData_0.2.2.9002
#> [5] Seurat_5.5.1 SeuratObject_5.4.0
#> [7] sp_2.2-1 RGraphSpace_1.5.1
#> [9] ggplot2_4.0.3
#>
#> loaded via a namespace (and not attached):
#> [1] RColorBrewer_1.1-3 rstudioapi_0.19.0 jsonlite_2.0.0
#> [4] magrittr_2.0.5 spatstat.utils_3.2-3 ggbeeswarm_0.7.3
#> [7] farver_2.1.2 rmarkdown_2.31 fs_2.1.0
#> [10] ragg_1.5.2 vctrs_0.7.3 ROCR_1.0-12
#> [13] spatstat.explore_3.8-1 htmltools_0.5.9 sass_0.4.10
#> [16] sctransform_0.4.3 parallelly_1.47.0 KernSmooth_2.23-26
#> [19] bslib_0.11.0 htmlwidgets_1.6.4 desc_1.4.3
#> [22] ica_1.0-3 fontawesome_0.5.3 plyr_1.8.9
#> [25] plotly_4.12.0 zoo_1.8-15 cachem_1.1.0
#> [28] igraph_2.3.3 mime_0.13 lifecycle_1.0.5
#> [31] pkgconfig_2.0.3 Matrix_1.7-6 R6_2.6.1
#> [34] fastmap_1.2.0 fitdistrplus_1.2-6 future_1.70.0
#> [37] shiny_1.14.0 digest_0.6.39 patchwork_1.3.2
#> [40] tensor_1.5.1 RSpectra_0.16-2 irlba_2.3.7
#> [43] textshaping_1.0.5 progressr_0.19.0 spatstat.sparse_3.2-0
#> [46] httr_1.4.8 polyclip_1.10-7 abind_1.4-8
#> [49] compiler_4.6.1 withr_3.0.3 S7_0.2.2
#> [52] fastDummies_1.7.6 MASS_7.3-66 rappdirs_0.3.4
#> [55] tools_4.6.1 vipor_0.4.7 lmtest_0.9-40
#> [58] otel_0.2.0 beeswarm_0.4.0 httpuv_1.6.17
#> [61] future.apply_1.20.2 goftest_1.2-3 glue_1.8.1
#> [64] nlme_3.1-170 promises_1.5.0 grid_4.6.1
#> [67] Rtsne_0.17 cluster_2.1.8.2 reshape2_1.4.5
#> [70] generics_0.1.4 gtable_0.3.6 spatstat.data_3.1-9
#> [73] tidyr_1.3.2 data.table_1.18.4 tidygraph_1.3.1
#> [76] spatstat.geom_3.8-1 RcppAnnoy_0.0.23 ggrepel_0.9.8
#> [79] RANN_2.6.2 pillar_1.11.1 stringr_1.6.0
#> [82] spam_2.11-4 RcppHNSW_0.7.0 later_1.4.8
#> [85] splines_4.6.1 dplyr_1.2.1 lattice_0.22-9
#> [88] survival_3.8-9 deldir_2.0-4 tidyselect_1.2.1
#> [91] miniUI_0.1.2 pbapply_1.7-4 knitr_1.51
#> [94] gridExtra_2.3.1 scattermore_1.2 xfun_0.59
#> [97] matrixStats_1.5.0 stringi_1.8.7 lazyeval_0.2.3
#> [100] yaml_2.3.12 evaluate_1.0.5 codetools_0.2-20
#> [103] tibble_3.3.1 cli_3.6.6 uwot_0.2.4
#> [106] xtable_1.8-8 reticulate_1.46.0 systemfonts_1.3.2
#> [109] jquerylib_0.1.4 Rcpp_1.1.1-1.1 globals_0.19.1
#> [112] spatstat.random_3.5-0 png_0.1-9 ggrastr_1.0.2
#> [115] spatstat.univar_3.2-0 parallel_4.6.1 pkgdown_2.2.0
#> [118] dotCall64_1.2 listenv_1.0.0 viridisLite_0.4.3
#> [121] scales_1.4.0 ggridges_0.5.7 crayon_1.5.3
#> [124] purrr_1.2.2 rlang_1.2.0 cowplot_1.2.0