skills/scvi-tools/references/models-spatial.md
This document covers models for analyzing spatially-resolved transcriptomics data in scvi-tools.
Purpose: Multi-resolution deconvolution of spatial transcriptomics using single-cell reference data.
Key Features:
When to Use:
Data Requirements:
Basic Usage (DestVI requires a CondSCVI reference model, not plain SCVI):
from scvi.model import CondSCVI, DestVI
# Step 1: Train the single-cell LVM (CondSCVI) on the labeled reference
CondSCVI.setup_anndata(sc_adata, layer="counts", labels_key="cell_type")
sc_model = CondSCVI(sc_adata, weight_obs=False)
sc_model.train(max_epochs=300)
# Step 2: Set up the spatial data and build DestVI from the trained reference
DestVI.setup_anndata(spatial_adata, layer="counts")
st_model = DestVI.from_rna_model(spatial_adata, sc_model)
st_model.train(max_epochs=2500)
# Step 3: Get cell type proportions per spot
proportions = st_model.get_proportions()
spatial_adata.obsm["proportions"] = proportions
# Step 4: Get cell type-specific expression
# Expression of genes specific to each cell type at each spot
ct_expression = st_model.get_scale_for_ct("T cells")
Key Parameters:
weight_obs (CondSCVI): downweight common cell types when training the referenceamortization: Amortization strategy ("both", "latent", "proportion")n_latent: Latent dimensionality (inherited from the CondSCVI reference)Outputs:
get_proportions(): Cell type proportions at each spotget_scale_for_ct(cell_type): Cell type-specific expression patternsget_gamma(): Proportion-specific gene expression scalingVisualization:
import scanpy as sc
import matplotlib.pyplot as plt
# Visualize specific cell type proportions spatially
sc.pl.spatial(
spatial_adata,
color="T cells", # If proportions added to .obs
spot_size=150
)
# Or use obsm directly
for ct in cell_types:
plt.figure()
sc.pl.spatial(
spatial_adata,
color=spatial_adata.obsm["proportions"][ct],
title=f"{ct} proportions"
)
Purpose: Cell type deconvolution for spatial transcriptomics using probabilistic modeling.
Key Features:
When to Use:
Basic Usage (Stereoscope is split into two scvi.external models):
from scvi.external import RNAStereoscope, SpatialStereoscope
# Train the single-cell reference model
RNAStereoscope.setup_anndata(sc_adata, labels_key="cell_type", layer="counts")
sc_model = RNAStereoscope(sc_adata)
sc_model.train(max_epochs=100)
# Build the spatial model from the trained reference
SpatialStereoscope.setup_anndata(spatial_adata, layer="counts")
spatial_model = SpatialStereoscope.from_rna_model(spatial_adata, sc_model)
spatial_model.train(max_epochs=2000)
# Get cell-type proportions per spot
proportions = spatial_model.get_proportions()
Purpose: Spatial mapping and integration of single-cell data to spatial locations.
Key Features:
When to Use:
Data Requirements:
scvi-tools wraps the Tangram method as
scvi.external.Tangram. The example below uses the standalonetangram-scpackage, whose helper functions (annotation/gene projection) are convenient for downstream analysis.
Basic Usage:
import tangram as tg
# Map cells to spatial locations
ad_map = tg.map_cells_to_space(
adata_sc=sc_adata,
adata_sp=spatial_adata,
mode="cells", # or "clusters" for cell type mapping
density_prior="rna_count_based"
)
# Get mapping matrix (cells × spots)
mapping = ad_map.X
# Project cell annotations to space
tg.project_cell_annotations(
ad_map,
spatial_adata,
annotation="cell_type"
)
# Impute genes in spatial data
genes_to_impute = ["CD3D", "CD8A", "CD4"]
tg.project_genes(ad_map, spatial_adata, genes=genes_to_impute)
Visualization:
# Visualize cell type mapping
sc.pl.spatial(
spatial_adata,
color="cell_type_projected",
spot_size=100
)
Purpose: Cross-modality imputation between spatial and single-cell data.
Key Features:
When to Use:
Basic Usage:
# gimVI (scvi.external) jointly models a single-cell and a spatial dataset
scvi.external.GIMVI.setup_anndata(sc_adata, layer="counts")
scvi.external.GIMVI.setup_anndata(spatial_adata, layer="counts")
model = scvi.external.GIMVI(sc_adata, spatial_adata)
model.train()
# Per-dataset latent representations and imputed expression
sc_latent, spatial_latent = model.get_latent_representation()
_, imputed_spatial = model.get_imputed_values(normalized=True)
Purpose: Analyzing cell-environment relationships in spatial data.
Key Features:
When to Use:
Data Requirements:
Basic Usage (scVIVA lives in scvi.external):
# scVIVA jointly models each cell and its niche. Precompute neighborhood
# composition / embeddings first, then register the resulting keys.
scvi.external.SCVIVA.preprocess_anndata(
spatial_adata,
sample_key="sample",
labels_key="cell_type",
cell_coordinates_key="spatial", # coordinates in .obsm
)
scvi.external.SCVIVA.setup_anndata(
spatial_adata,
labels_key="cell_type",
sample_key="sample",
)
model = scvi.external.SCVIVA(spatial_adata)
model.train()
# Latent representation capturing cell state in its spatial context
spatial_adata.obsm["X_scVIVA"] = model.get_latent_representation()
See the scVIVA user guide for the exact preprocessing keys and niche/environment outputs.
Purpose: Addressing spatial transcriptomics noise through resolution-aware modeling.
Key Features:
When to Use:
Basic Usage (ResolVI lives in scvi.external):
scvi.external.RESOLVI.setup_anndata(
spatial_adata,
layer="counts",
)
model = scvi.external.RESOLVI(spatial_adata)
model.train()
# Denoised / corrected expression
denoised = model.get_normalized_expression()
Choose when:
Best for: Visium, spot-based technologies
Choose when:
Best for: Quick deconvolution tasks
Choose when:
Best for: Detailed spatial mapping
Choose when:
Best for: Integration and imputation
Choose when:
Best for: Microenvironment studies
Choose when:
Best for: Noisy data preprocessing
import scvi
import scanpy as sc
import squidpy as sq
# ===== Part 1: Prepare single-cell reference =====
# Load and process scRNA-seq reference
sc_adata = sc.read_h5ad("reference_scrna.h5ad")
# QC and filtering
sc.pp.filter_genes(sc_adata, min_cells=10)
sc.pp.highly_variable_genes(sc_adata, n_top_genes=4000)
# Train the CondSCVI reference model (labels_key carries cell types)
from scvi.model import CondSCVI, DestVI
CondSCVI.setup_anndata(
sc_adata,
layer="counts",
labels_key="cell_type",
)
sc_model = CondSCVI(sc_adata, weight_obs=False)
sc_model.train(max_epochs=300)
# ===== Part 2: Load spatial data =====
spatial_adata = sc.read_visium("path/to/visium")
spatial_adata.var_names_make_unique()
# QC spatial data
sc.pp.filter_genes(spatial_adata, min_cells=10)
# ===== Part 3: Run DestVI =====
DestVI.setup_anndata(
spatial_adata,
layer="counts"
)
destvi_model = DestVI.from_rna_model(
spatial_adata,
sc_model,
)
destvi_model.train(max_epochs=2500)
# ===== Part 4: Extract results =====
# Get proportions
proportions = destvi_model.get_proportions()
spatial_adata.obsm["proportions"] = proportions
# Add proportions to .obs for easy plotting
for i, ct in enumerate(sc_model.adata.obs["cell_type"].cat.categories):
spatial_adata.obs[f"prop_{ct}"] = proportions[:, i]
# ===== Part 5: Visualization =====
# Plot specific cell types
cell_types = ["T cells", "B cells", "Macrophages"]
for ct in cell_types:
sc.pl.spatial(
spatial_adata,
color=f"prop_{ct}",
title=f"{ct} proportions",
spot_size=150,
cmap="viridis"
)
# ===== Part 6: Spatial analysis =====
# Compute spatial neighbors
sq.gr.spatial_neighbors(spatial_adata)
# Spatial autocorrelation of cell types
for ct in cell_types:
sq.gr.spatial_autocorr(
spatial_adata,
attr="obs",
mode="moran",
genes=[f"prop_{ct}"]
)
# ===== Part 7: Save results =====
destvi_model.save("destvi_model")
spatial_adata.write("spatial_deconvolved.h5ad")
.obsm["spatial"]