Skip to contents

Package: RGraphSpace 1.4.1

Overview

This vignette demonstrates how RGraphSpace extends the ggplot2 grammar to spatial transcriptomics data. We use spatial data from the SeuratData package to illustrate direct mapping of spatial variables to ggplot2 aesthetics via the ggplot-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.4.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

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 spatial high-dimensional variables can be mapped directly 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; the as.GraphSpace() converts the Seurat object into a GraphSpace, exposing its spatial coordinates and feature data to the ggplot2 grammar. 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 c4b5fdf 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, ...)

Spatial feature visualization

With the GraphSpace object ready, we can reproduce a typical Seurat spatial feature plot using standard ggplot2 syntax. 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 rotate.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 typical Seurat 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.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: 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.0              SeuratObject_5.4.0       
#> [7] sp_2.2-1                  RGraphSpace_1.4.1        
#> [9] ggplot2_4.0.3            
#> 
#> loaded via a namespace (and not attached):
#>   [1] RColorBrewer_1.1-3     rstudioapi_0.18.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.2           mime_0.13              lifecycle_1.0.5       
#>  [31] pkgconfig_2.0.3        Matrix_1.7-5           R6_2.6.1              
#>  [34] fastmap_1.2.0          fitdistrplus_1.2-6     future_1.70.0         
#>  [37] shiny_1.13.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.0         withr_3.0.2            S7_0.2.2              
#>  [52] fastDummies_1.7.6      MASS_7.3-65            rappdirs_0.3.4        
#>  [55] tools_4.6.0            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-169           promises_1.5.0         grid_4.6.0            
#>  [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.0          dplyr_1.2.1            lattice_0.22-9        
#>  [88] survival_3.8-6         deldir_2.0-4           tidyselect_1.2.1      
#>  [91] miniUI_0.1.2           pbapply_1.7-4          knitr_1.51            
#>  [94] gridExtra_2.3          scattermore_1.2        xfun_0.58             
#>  [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.0         pkgdown_2.2.0         
#> [118] dotCall64_1.2          listenv_0.10.1         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
Hao, Yuhan, Tim Stuart, Madeline H Kowalski, et al. 2024. “Dictionary Learning for Integrative, Multimodal and Scalable Single-Cell Analysis.” Nature Biotechnology 42 (2): 293–304. https://doi.org/10.1038/s41587-023-01767-y.