--- title: "Tutorial 16 — Spatial Transcriptomics with Visium" subtitle: "Load a 10x Visium tissue, find spatially variable genes, integrate two sections, and sketch cell-type deconvolution" author: "Single Cell RNA-seq Workshop" format: html: toc: true toc-depth: 3 code-fold: false code-overflow: wrap highlight-style: github embed-resources: true execute: eval: false echo: true warning: false message: false editor: visual --- ::: {.callout-note title="Running the code in this tutorial"} Every code chunk here is tagged with `#| eval: false`, so the published page shows the code **without running it**. To run it yourself, open the downloaded `.qmd` in **RStudio**: - **Run one chunk:** click the green ▶ (*Run Current Chunk*) at the chunk's top-right, or press *Ctrl/Cmd + Enter*. This runs the chunk **regardless of its `eval` setting** — the easiest way to work through the tutorial interactively. - **Run a chunk on render:** change that chunk's `#| eval: false` to `#| eval: true` (or delete the line) so it executes when the document is rendered. - **Render the whole tutorial:** click the **Render** button in RStudio (or run `quarto render` in a terminal) to execute the chunks top-to-bottom and knit a finished HTML. To run everything on render, set `eval: true` once in the YAML header at the top. ::: ## About this tutorial A laptop-friendly **introduction to spatial transcriptomics** using 10x **Visium** — the most accessible spatial platform today. We use the `stxBrain` dataset from `SeuratData`: matched **anterior** and **posterior** sagittal sections of an adult mouse brain, \~50 MB total, installed with one R command (no manual download, no 10x click-through). ::: callout-note **Companion lecture:** [Lecture 16 — Spatial Transcriptomics: Gene Expression in Tissue Context](../Lecture_Folder/Lecture_16_Spatial.html) · **Companion book chapter:** [Chapter 16 — Spatial Transcriptomics](../Resources_Folder/Chapter_16_Spatial.html) — the long-form prose treatment of this tutorial's material, with cross-references to the prerequisite appendices. ::: You'll learn to: - Load Visium data into Seurat (counts + tissue image + spot coordinates) - Run the spatial-aware QC, normalization (`SCTransform`), and clustering workflow - Plot expression on top of the histology image - Find **spatially variable features** — genes whose expression has spatial structure beyond cluster membership - Integrate two tissue sections (the same anterior + posterior idea as `ifnb`'s CTRL + STIM) ::: callout-note **Why mouse brain for a primer?** It has dramatic, well-known anatomy (cortex, hippocampus, thalamus, hypothalamus, cerebellum) — easy to recognize on the histology image, easy to validate with marker genes (`Hpca` in hippocampus, `Plp1` in white matter, `Pcp4` in cerebellum). A clean teaching dataset; no laptop will struggle with it. ::: ::: {.callout-tip title="How to use this page"} 1. Download the `.qmd` source: Tutorial_16_Spatial_Transcriptomics.qmd. If your browser saves the file as `Tutorial_16_Spatial_Transcriptomics.qmd.txt`, **drop the trailing `.txt`** so the filename ends in `.qmd`, then open it in RStudio. 2. Install the `stxBrain` dataset once (one R command — see [Setup](#setup)). 3. Work through the chunks, flipping `eval: true` as you go. ::: ## Dataset — `stxBrain` (10x Visium adult mouse brain) **What it is.** Two 10x Visium sections from the same adult mouse brain — one **anterior** (\~2,700 spots), one **posterior** (\~3,300 spots). Each spot is \~55 µm in diameter and captures the transcripts from \~1–10 cells underneath. The "spots" are the unit of analysis here, not single cells. - Distribution: `SeuratData::InstallData("stxBrain")` (no manual download) - Source: 10x Genomics public Visium dataset - Used in: this tutorial; the [Seurat spatial vignette](https://satijalab.org/seurat/articles/spatial_vignette.html) ::: callout-warning A Visium "spot" is **not a single cell.** Most analyses borrow the language of single-cell (clusters, marker genes, integration) but interpret each unit as a small *neighbourhood* of cells. For true single-cell resolution use higher-resolution spatial platforms — Xenium, MERFISH, or Visium HD. ::: ## Learning goals - Load a Visium dataset and understand the four objects in a `SpatialAssay` (counts, image, coordinates, scale factors) - Run `SCTransform` on spatial data and explain why it's preferred over `LogNormalize` here - Use `SpatialFeaturePlot` and `SpatialDimPlot` to overlay expression / cluster on tissue - Find spatially variable features with Moran's I - Merge two tissue sections and run a joint clustering ::: {.callout-warning title="Common errors / things that bite"} **`SeuratData::InstallData("stxBrain")` is slow** — \~50 MB download. First run takes a few minutes; subsequent runs are instant. **`SCTransform` warns "iteration limit reached"** — usually safe to ignore. Visium spots have a wider count distribution than single cells, so SCT's NB-regression sometimes hits its iteration limit on outlier spots. The result is still usable. **`SpatialFeaturePlot` shows nothing on the tissue** — the `images` slot wasn't loaded. After `SaveH5Seurat` / `Convert`, the histology image can be lost; `Read10X_Image(dirname(seu@tools$Staffli@imgs[1]))` re-attaches it. **Anterior + posterior integration is slow / OOMs** — the SCT-anchor path is RAM-hungry on Visium. If you have <16 GB RAM, downsample spots first (`subset(brain, downsample = 1500)`) or use Harmony on the SCT residuals instead of `IntegrateData`. ::: ::: {.callout-tip title="Solutions / instructor copy"} The *Think about it* prompts on this page have inline answers (click "Show answer" / "Click for answer" to expand). For the full **runnable instructor copy** with every chunk pre-evaluated and outputs shown: - **For students:** the same `.qmd` you downloaded already contains every solution inline — just expand the collapsed answers - **For instructors:** render with `quarto render --profile solutions` from the `Exercise_Folder/` directory. The fully-evaluated HTML is written to `docs/Exercise_Folder/_solutions/Tutorial_16_Spatial_Transcriptomics.html`. See [Exercise_Folder/_quarto-solutions.yml](https://github.com/wcresko/scRNAseq_tutorial/blob/main/Exercise_Folder/_quarto-solutions.yml) for the build profile ::: ## Setup {#setup} ```{r} #| label: M16-setup # Packages are installed in Tutorial 00 (Setup → bonus modules); the stxBrain # dataset install is on the Datasets page. Here we just load the libraries. library(Seurat) library(SeuratData) library(tidyverse) library(patchwork) # --------------------------------------------------------------------------- # Output directory for this module's figures and tables. # Every figure/table chunk below writes a file named Mod16_C_ # into ../output/Mod16/ so it can be cross-referenced from the rest of the site. # Mod16 = Module 16 (this tutorial); C = the nth code chunk. # --------------------------------------------------------------------------- out_dir <- "../output/Mod16" dir.create(out_dir, recursive = TRUE, showWarnings = FALSE) dir.create("../data", showWarnings = FALSE) # shared .rds hand-off folder (sibling of scripts/, created one level up) message("This module writes its figures & tables to: ", normalizePath(out_dir)) # --- Figure saving -------------------------------------------------------- # save_fig() writes every figure as a .png (for viewing) AND a .svg (vector, # editable in Illustrator / Inkscape for a manuscript). Pass the .png path; the # .svg is written alongside with the same basename. (.svg uses the 'svglite' # package, installed in Tutorial 00.) save_fig <- function(filename, plot, width, height, dpi = 300, ...) { ggplot2::ggsave(filename, plot, width = width, height = height, dpi = dpi, ...) svg_path <- paste0(tools::file_path_sans_ext(filename), ".svg") tryCatch(ggplot2::ggsave(svg_path, plot, width = width, height = height, ...), error = function(e) message(" (could not write ", basename(svg_path), " - install 'svglite'? ", conditionMessage(e), ")")) } set.seed(2026) ``` ## Step 1 — Load both sections ```{r} #| label: M16-load ant <- LoadData("stxBrain", type = "anterior1") post <- LoadData("stxBrain", type = "posterior1") ant post # Check the assays — Visium objects have a "Spatial" assay (not "RNA") DefaultAssay(ant) # --- Table out: per-section dimensions (features x spots) ------------------- tibble::tibble( section = c("anterior1", "posterior1"), features = c(nrow(ant), nrow(post)), spots = c(ncol(ant), ncol(post)) ) |> readr::write_csv(file.path(out_dir, "Mod16_C2_section_dimensions.csv")) ``` ::: {.callout-important title="Think about it"} 1. How many features (genes) are in each section? How many spots? 2. What's stored in `ant@images`?
Show answers 1. About 32,000 genes × \~2,700 spots (anterior) and \~3,300 spots (posterior). Each spot is bigger than a single cell, so the absolute spot count is much smaller than a typical scRNA-seq experiment. 2. An object of class `VisiumV1` containing the **histology image** (low-res JPEG), the **per-spot tissue-coordinate table** (which pixel each spot maps to), and the **scale factors** that translate between Visium's three coordinate systems (full-res, low-res, hi-res). `SpatialFeaturePlot` uses all of these to draw expression on top of the H&E.
::: ## Step 2 — Spatial QC The same per-spot QC metrics as scRNA-seq apply: `nCount_Spatial`, `nFeature_Spatial`, percent mitochondrial. **But also visualize them spatially** — a localized QC outlier (e.g. one corner of the tissue with low UMI counts) is a tissue artefact (bubble, fold, edge cells), not random noise. ```{r} #| label: M16-qc #| fig-cap: "Per-spot QC for the anterior section: violin distributions and the same metrics overlaid on the tissue." ant[["percent.mt"]] <- PercentageFeatureSet(ant, pattern = "^mt-") post[["percent.mt"]] <- PercentageFeatureSet(post, pattern = "^mt-") # Standard violin p1 <- VlnPlot(ant, features = c("nCount_Spatial", "nFeature_Spatial", "percent.mt"), pt.size = 0, ncol = 3) & xlab("Anterior section") & theme(plot.title = element_text(size = 11)) p1 <- p1 + patchwork::plot_annotation( title = "Per-spot QC metrics — stxBrain anterior section", subtitle = "UMIs/spot, genes/spot and mitochondrial % distributions", caption = "Module 16 · Spatial Transcriptomics" ) # Spatial overlay — same metrics, on the tissue p2 <- SpatialFeaturePlot(ant, features = c("nCount_Spatial", "percent.mt"), pt.size.factor = 1.6) + plot_layout(ncol = 2) p2 <- p2 + patchwork::plot_annotation( title = "Spatial QC overlay — stxBrain anterior section", subtitle = "UMIs/spot and mitochondrial % on the histology image", caption = "Module 16 · Spatial Transcriptomics" ) p1 p2 # --- Figures out ----------------------------------------------------------- save_fig(file.path(out_dir, "Mod16_C3_qc_violins.png"), p1, width = 10, height = 4, dpi = 300) save_fig(file.path(out_dir, "Mod16_C3_qc_spatial.png"), p2, width = 10, height = 5, dpi = 300) ``` ::: callout-tip `pattern = "^mt-"` (lowercase) for mouse mitochondrial genes. For human use `"^MT-"`. ::: ::: {.callout-tip title="Reading the output"} The **violin panel** shows the distribution of `nCount_Spatial`, `nFeature_Spatial`, and `percent.mt` across all spots: a wide lower tail on UMI counts is normal for Visium (edge spots, white-matter spots) but a very high upper tail on `percent.mt` would flag tissue damage. The **spatial overlay** (`p2`) is the key diagnostic: each circle is a spot colored by its metric value, placed on the H&E image. Uniform color variation that matches anatomy — lower UMI counts over white matter, higher over the cortex — is expected and not a reason to filter. Isolated patches of very low counts in otherwise high-count tissue regions, or a single corner of the section with elevated `percent.mt`, are more likely artefacts (a bubble, a fold, a damaged area). ::: ::: {.callout-important title="Think about it"} On the spatial `nCount_Spatial` overlay, regions of consistently low UMI counts often correspond to specific tissue features. What might they be?
Show answers White matter is famously low-yield on Visium because the tissue is mostly axons (myelin), with relatively few neuronal cell bodies — fewer cells per spot, lower transcript counts per spot. Tissue **edges** also drop off because spots there are only partially covered. Don't filter these spots reflexively — for the brain dataset they are real biology.
::: ## Step 3 — Normalize with SCTransform `SCTransform` is the recommended normalization for Visium because spot-level counts have a **wider dynamic range** than single-cell counts (each spot pools 1–10 cells), and `SCTransform`'s regularized negative binomial model handles that better than `LogNormalize`. ```{r} #| label: M16-norm ant <- SCTransform(ant, assay = "Spatial", verbose = FALSE) post <- SCTransform(post, assay = "Spatial", verbose = FALSE) DefaultAssay(ant) # Should now be "SCT" ``` ## Step 4 — Visualize known marker genes on the tissue Sanity-check that the data captures known anatomy. For the mouse brain: - `Hpca` — hippocampus - `Pcp4` — cerebellum, parts of cortex - `Plp1` — myelin / white matter - `Sst` — somatostatin interneurons - `Th` — dopaminergic / noradrenergic neurons (substantia nigra) - `Cck` — broadly cortical inhibitory neurons ```{r} #| label: M16-markers p_markers_ant <- SpatialFeaturePlot(ant, features = c("Hpca", "Plp1", "Sst"), ncol = 3, alpha = c(0.1, 1)) + patchwork::plot_annotation( title = "Anatomical markers on tissue — anterior section", subtitle = "Hpca (hippocampus), Plp1 (white matter), Sst (interneurons)", caption = "Module 16 · Spatial Transcriptomics" ) p_markers_post <- SpatialFeaturePlot(post, features = c("Hpca", "Plp1", "Pcp4"), ncol = 3, alpha = c(0.1, 1)) + patchwork::plot_annotation( title = "Anatomical markers on tissue — posterior section", subtitle = "Hpca (hippocampus), Plp1 (white matter), Pcp4 (cerebellum)", caption = "Module 16 · Spatial Transcriptomics" ) p_markers_ant p_markers_post # --- Figures out ----------------------------------------------------------- save_fig(file.path(out_dir, "Mod16_C5_markers_anterior.png"), p_markers_ant, width = 12, height = 4, dpi = 300) save_fig(file.path(out_dir, "Mod16_C5_markers_posterior.png"), p_markers_post, width = 12, height = 4, dpi = 300) ``` ::: {.callout-tip title="Reading the output"} Each panel is a spatial feature plot: every circle is one Visium spot placed on the H&E histology image, and the color gradient from light to dark encodes expression level (low → high). `Hpca` should light up in the medial hippocampus (more prominent on the posterior section), `Plp1` should trace white-matter tracts (corpus callosum, internal capsule), and `Sst`/`Pcp4` should mark specific cortical and cerebellar layers respectively. Spots that match the expected anatomy validate that normalization worked and that the tissue section is oriented correctly; unexpected expression patterns (e.g. `Plp1` smeared uniformly) can signal high ambient RNA or a failed normalization step. ::: ::: {.callout-important title="Think about it"} `Hpca` should be highest in the hippocampus (a banana-shaped structure in the medial part of each section). Can you see the hippocampus light up? If not, you may have the section orientation flipped — SeuratData's `stxBrain` is stored with the cortical surface roughly **at the top** of the image.
Show answers If you don't recognize the hippocampus, open the [Allen Mouse Brain Atlas](https://mouse.brain-map.org/static/atlas) for a side-by-side reference. The anterior section catches the *front* of the hippocampus and not much else of it; the posterior section gets the bulk. `Hpca` will appear stronger on `posterior1` than on `anterior1`.
::: ## Step 5 — Cluster spots Same workflow as scRNA-seq, but on the SCT assay: ```{r} #| label: M16-cluster ant <- ant |> RunPCA(assay = "SCT", verbose = FALSE) |> FindNeighbors(reduction = "pca", dims = 1:30) |> FindClusters(resolution = 0.5, verbose = FALSE) |> RunUMAP(reduction = "pca", dims = 1:30) # UMAP and tissue side-by-side p_umap <- DimPlot(ant, reduction = "umap", label = TRUE) + labs(title = "Anterior — UMAP", x = "UMAP 1", y = "UMAP 2", colour = "Cluster") p_tis <- SpatialDimPlot(ant, label = TRUE, label.size = 3) + labs(title = "Anterior — clusters on tissue", fill = "Cluster") p_cluster <- (p_umap + p_tis) + patchwork::plot_annotation( title = "Spot clustering — stxBrain anterior section", subtitle = "SNN clusters on the SCT assay, in UMAP space and on the histology image", caption = "Module 16 · Spatial Transcriptomics" ) p_cluster # --- Figure out ------------------------------------------------------------ save_fig(file.path(out_dir, "Mod16_C6_anterior_clusters.png"), p_cluster, width = 12, height = 5, dpi = 300) ``` ::: {.callout-tip title="Reading the output"} The left panel is the standard UMAP: each point is a spot, colored by cluster assignment; well-separated round islands indicate distinct transcriptional populations. The right panel is the spatial payoff — the same cluster colors mapped back onto the H&E image. Clusters that form **contiguous anatomical regions** (a continuous band matching the cortex, a distinct patch in the hippocampal area) support biological interpretation; clusters whose colored spots are scattered randomly across the tissue are more likely technical artifacts or low-frequency cell types. If a major brain region appears split across two or three clusters, try increasing `resolution`; if the entire tissue collapses to two or three coarse clusters, lower it. ::: ::: {.callout-important title="Think about it"} A spatial cluster that occupies a single contiguous anatomical region (e.g. one cluster covers the entire hippocampus) is biologically meaningful. A cluster that's scattered randomly across the section in single spots is more likely a technical / mixed-cell artefact. Why?
Show answers Real cell types are spatially organized in tissue — neurons in cortex, oligodendrocytes in white matter, etc. A cluster whose spots all share *spatial* proximity is recovering an anatomical structure. A cluster whose spots are scattered is recovering either (a) very rare cells distributed throughout (immune cells, vasculature — sometimes legitimate) or (b) a technical signature (depth, ambient RNA, batch). The spatial layout is itself a quality check.
::: ## Step 6 — Spatially variable features (Moran's I) Some genes are variable *and* their variation has spatial structure (a clear pattern on the tissue), some are variable but spatially random. The first kind is what you usually want to study in a spatial dataset. ```{r} #| label: M16-svf ant <- FindSpatiallyVariableFeatures( ant, assay = "SCT", features = VariableFeatures(ant)[1:1000], # restrict to top-1000 HVGs for speed selection.method = "moransi" ) # Top spatially variable features top_sv <- SpatiallyVariableFeatures(ant, selection.method = "moransi")[1:6] top_sv p_svf <- SpatialFeaturePlot(ant, features = top_sv, ncol = 3, alpha = c(0.1, 1)) + patchwork::plot_annotation( title = "Top spatially variable features (Moran's I) — anterior section", subtitle = "Genes whose expression has the strongest spatial structure", caption = "Module 16 · Spatial Transcriptomics" ) p_svf # --- Outputs: top spatially variable genes (table) + tissue overlay -------- tibble::tibble(rank = seq_along(top_sv), gene = top_sv) |> readr::write_csv(file.path(out_dir, "Mod16_C7_spatially_variable_features.csv")) save_fig(file.path(out_dir, "Mod16_C7_spatially_variable_features.png"), p_svf, width = 12, height = 7, dpi = 300) ``` ::: {.callout-tip title="Reading the output"} The console first prints `top_sv` — a character vector of the six genes with the highest Moran's I statistic, meaning their expression is most strongly autocorrelated across the tissue (neighboring spots tend to have similar values). The six-panel `SpatialFeaturePlot` then maps each gene onto the H&E: spots with high expression are dark, low expression is light. A good spatially variable gene looks like a **distinct patch or stripe** confined to a recognizable anatomical structure — not a uniform wash across the tissue. In the mouse brain anterior section you typically see white-matter genes (`Plp1`, `Mbp`) and hippocampal or cortical markers dominating the top six. A gene that tiles the tissue with no clear anatomy despite a high Moran's I may reflect a confounding gradient (slide edge effects, sequencing depth gradient) rather than real biology. ::: ::: {.callout-important title="Think about it"} A gene with high variance but **low** Moran's I is variable spot-to-spot but with no spatial pattern — what does that suggest?
Show answers Either (a) genuine cell-cell heterogeneity within an anatomical region (rare cell types scattered across a region), or more often (b) a technical signature like ambient-RNA bleed, mitochondrial percent, or sequencing depth. Spatial variability is a stronger biological signal than overall variability.
::: ## Step 7 — Merge anterior + posterior, joint cluster The same two-sample integration logic as `ifnb`, but on Visium. We use `SCTransform` integration: ```{r} #| label: M16-merge_integrate # Each section is its own "batch" ant$slice <- "anterior" post$slice <- "posterior" brain.list <- list(ant = ant, post = post) features <- SelectIntegrationFeatures(object.list = brain.list, nfeatures = 3000, verbose = FALSE) brain.list <- PrepSCTIntegration(object.list = brain.list, anchor.features = features, verbose = FALSE) anchors <- FindIntegrationAnchors(object.list = brain.list, normalization.method = "SCT", anchor.features = features, verbose = FALSE) brain <- IntegrateData(anchorset = anchors, normalization.method = "SCT", verbose = FALSE) DefaultAssay(brain) <- "integrated" brain <- brain |> RunPCA(verbose = FALSE) |> FindNeighbors(dims = 1:30) |> FindClusters(resolution = 0.5, verbose = FALSE) |> RunUMAP(dims = 1:30) # Joint UMAP p_int_umap <- DimPlot(brain, group.by = c("slice", "seurat_clusters")) + patchwork::plot_annotation( title = "Integrated UMAP — anterior + posterior sections", subtitle = "Coloured by section of origin and by joint cluster", caption = "Module 16 · Spatial Transcriptomics" ) p_int_umap # And per-slice spatial cluster maps p_int_spatial <- SpatialDimPlot(brain, label = TRUE) + patchwork::plot_annotation( title = "Integrated clusters on tissue — both sections", subtitle = "Joint cluster assignments mapped back onto each section", caption = "Module 16 · Spatial Transcriptomics" ) p_int_spatial # --- Figures out ----------------------------------------------------------- save_fig(file.path(out_dir, "Mod16_C8_integrated_umap.png"), p_int_umap, width = 12, height = 5, dpi = 300) save_fig(file.path(out_dir, "Mod16_C8_integrated_spatial_clusters.png"), p_int_spatial, width = 10, height = 5, dpi = 300) ``` ::: {.callout-tip title="Reading the output"} `p_int_umap` shows two side-by-side UMAPs from the integrated object: the left is colored by `slice` (anterior vs. posterior) and the right by `seurat_clusters`. Before integration, coloring by slice would show two blobs; after successful integration, spots from both sections should **interleave** in the UMAP, meaning the same anatomical cell type from both sections lands in the same cluster regardless of which chip it came from. `p_int_spatial` maps those joint cluster assignments back onto each section's H&E — anatomically equivalent structures (e.g. cortex, hippocampus) in both anterior and posterior sections should receive the **same cluster color**, which is the validation that integration succeeded. ::: ::: {.callout-important title="Think about it"} Why integrate sections from the same brain at all? The animal is one animal — what could differ?
Show answers Even matched sections can drift: minor differences in tissue handling (RNA quality, embedding, cryostat thickness), Visium chip-to-chip variation, different lots of capture probes, different sequencing depth. Integration aligns the technical axes so the *anatomical* differences (anterior catches striatum, posterior catches cerebellum) are what dominates the joint embedding rather than chip-to-chip noise.
::: ## Step 8 — (Optional) cell-type deconvolution from a single-cell reference A Visium spot covers \~1–10 cells, so each spot's transcriptome is a *mixture*. Tools like **`spacexr`** (the RCTD method), **`CARD`**, **`cell2location`** (Python), and **`SPOTlight`** decompose each spot into estimated cell-type proportions, using a single-cell reference of the same tissue. This is too heavy to install and run end-to-end here, but the recipe is: *Optional reference recipe — left `#| eval: false`; it's heavy to run and is here as a pattern for your own data, so you don't need to change anything.* ```{r} #| label: M16-deconv_recipe #| eval: false # Reference recipe for an actual deconvolution with spacexr (RCTD). library(spacexr) # 1. Build a reference from a single-cell brain dataset (e.g. Allen Mouse Brain # Atlas or a Tabula Muris brain subset) — a Reference() object with cell-type labels. # 2. Build a SpatialRNA() object from your Visium counts + spot coordinates. # 3. Run RCTD: rctd <- create.RCTD(spatial_rna, reference, max_cores = 4) rctd <- run.RCTD(rctd, doublet_mode = "doublet") # 4. Each spot now has weights for the contributing cell types; visualize as a # per-cell-type SpatialFeaturePlot. ``` For the laptop workflow here, **a fast proxy** is to compute a **module score** for each cell type using its top markers from a single-cell reference. We borrow markers from the `ifnb` PBMC reference for one quick example (mouse brain doesn't have PBMCs, so this is purely illustrative — for a real run, use a brain-tissue scRNA-seq reference): *Optional illustrative sketch — left `#| eval: false`; you don't need to change it.* ```{r} #| label: M16-module_score #| eval: false # Illustrative sketch only (cross-species gene-name conversion is non-trivial): brain <- AddModuleScore(brain, features = list(Microglia = c("Cx3cr1", "P2ry12", "Tmem119")), name = "Microglia") SpatialFeaturePlot(brain, features = "Microglia1") ``` ## Step 9 — Save ```{r} #| label: M16-save dir.create("../data", showWarnings = FALSE) saveRDS(brain, "../data/stxBrain_integrated.rds") ``` ## Wrap-up You've done the canonical Visium workflow on a real two-section dataset: 1. Loaded Visium data including the histology image 2. Visualized QC and marker expression on the tissue 3. Normalized with `SCTransform` and clustered the spots 4. Found spatially variable features (Moran's I) 5. Integrated two sections and re-clustered jointly 6. Sketched the recipe for cell-type deconvolution ::: {.callout-tip title="Going further"} - **Higher-resolution platforms.** Visium HD (subcellular bins), 10x Xenium, MERFISH, NanoString CosMx — same conceptual workflow, much higher resolution. Seurat 5 supports Visium HD natively; Xenium requires Vitessce / Seurat 5 / `xeniumtools`. - **Cell–cell communication in space.** Tools like `LIANA+` (Python) extend the LIANA framework to spatial data — they ask which ligand-receptor pairs are *spatially co-located*, not just co-expressed. - **Spatial domains via graph methods.** `BANKSY` and `STAGATE` produce spatially-aware clusters that explicitly use coordinates as features, often cleaner than the SNN approach above. - **Image-aware normalization.** `STUtility` and `semla` add tissue-image-based normalization that handles section-edge effects better than off-the-shelf SCT. ::: ## See also - [Seurat spatial vignette](https://satijalab.org/seurat/articles/spatial_vignette.html) — the canonical reference workflow this tutorial follows - [scNotebooks Module 10](https://integrativebioinformatics.github.io/scNotebooks/modules/en/module10/module10.html) — covers Visium + 3D visualization + deconvolution on 10x breast cancer - `spacexr` (RCTD) — [github.com/dmcable/spacexr](https://github.com/dmcable/spacexr) - Moses & Pachter 2022 *Nat Methods* — landscape review of spatial methods, [doi:10.1038/s41592-022-01409-2](https://doi.org/10.1038/s41592-022-01409-2)