Skill Tracer
Skills › Scientific Agent Skills › scvi-tools › models-spatial.md

models-spatial.md

196 lines · skills/scvi-tools/references/models-spatial.md at 5f2bc03

Open on GitHub
1# Spatial models: scvi-tools 1.5.1 compatibility workflows
2
3Upstream moved ongoing maintenance of DestVI, Stereoscope, Tangram, GIMVI, SCVIVA,
4RESOLVI and DIAGVI to [scVIVA-tools](https://scviva-tools.readthedocs.io/en/latest/)
5starting with scvi-tools 1.5. The classes below remain in 1.5.1; these source-verified
6examples support existing pinned workflows and were not biologically validated.
7For new spatial analyses, check the companion package's current API rather than
8assuming these namespaces will persist. No spatial model fitting was executed in
9this refresh.

Spatial models: scvi-tools 1.5.1 compatibility workflows

Upstream moved ongoing maintenance of DestVI, Stereoscope, Tangram, GIMVI, SCVIVA, RESOLVI and DIAGVI to scVIVA-tools starting with scvi-tools 1.5. The classes below remain in 1.5.1; these source-verified examples support existing pinned workflows and were not biologically validated. For new spatial analyses, check the companion package's current API rather than assuming these namespaces will persist. No spatial model fitting was executed in this refresh.

Raw lines 1 to 10
11## Shared preparation
13Keep measured counts for count-based spatial models, unique gene IDs, cell/spot
14IDs, spatial coordinates and slide/library metadata. Align reference and spatial
15genes **before** training a reference that will be transferred. Merely having some
16gene overlap is insufficient when a method expects identical columns.
18```python
19# sc_adata and spatial_adata already contain measured layers['counts'].
20assert sc_adata.var_names.is_unique and spatial_adata.var_names.is_unique
21shared = sc_adata.var_names[sc_adata.var_names.isin(spatial_adata.var_names)]
22assert len(shared) > 0
23sc_adata = sc_adata[:, shared].copy()
24spatial_adata = spatial_adata[:, shared].copy()
25assert sc_adata.var_names.equals(spatial_adata.var_names)
26```
28Choose informative shared genes and check reference cell types/assays and spatial
29QC, not just a nonzero overlap count. Load spatial data with the current reader for
30the assay; preserve coordinates and image scale metadata. Do not assume a Visium
31reader creates a `counts` layer. Independent slides need separate spatial neighborhoods.

Shared preparation

Keep measured counts for count-based spatial models, unique gene IDs, cell/spot IDs, spatial coordinates and slide/library metadata. Align reference and spatial genes before training a reference that will be transferred. Merely having some gene overlap is insufficient when a method expects identical columns.

# sc_adata and spatial_adata already contain measured layers['counts'].
assert sc_adata.var_names.is_unique and spatial_adata.var_names.is_unique
shared = sc_adata.var_names[sc_adata.var_names.isin(spatial_adata.var_names)]
assert len(shared) > 0
sc_adata = sc_adata[:, shared].copy()
spatial_adata = spatial_adata[:, shared].copy()
assert sc_adata.var_names.equals(spatial_adata.var_names)

Choose informative shared genes and check reference cell types/assays and spatial QC, not just a nonzero overlap count. Load spatial data with the current reader for the assay; preserve coordinates and image scale metadata. Do not assume a Visium reader creates a counts layer. Independent slides need separate spatial neighborhoods.

Raw lines 11 to 32
33## CondSCVI to DestVI
35```python
36from scvi.model import CondSCVI, DestVI
38CondSCVI.setup_anndata(sc_adata, layer="counts", labels_key="cell_type")
39reference = CondSCVI(sc_adata, weight_obs=False)
40reference.train(max_epochs=300)
41DestVI.setup_anndata(spatial_adata, layer="counts")
42destvi = DestVI.from_rna_model(spatial_adata, reference)
43destvi.train(max_epochs=2500)
44proportions = destvi.get_proportions() # spots-by-cell-types DataFrame
45spatial_adata.obsm["proportions"] = proportions
46for cell_type in proportions.columns:
47 spatial_adata.obs[f"prop_{cell_type}"] = proportions[cell_type]
48cell_type_expression = destvi.get_scale_for_ct("T cells")
49```
51DestVI requires a labeled **CondSCVI**, not SCVI, reference. The example's epoch
52counts are starting configurations, not convergence guarantees. Inspect reference
53fit and abundance support, sensitivity to cell-type granularity, and tissue markers.
54`get_gamma()` exposes within-type latent variables; it is not a matrix of gene
55scaling factors. DataFrame outputs use label/`.iloc` indexing, not `[:, i]`.
57The base model does not automatically use a spatial-neighbor prior simply because
58coordinates are present. Cell proportions are model estimates, not direct cell counts
59or proof that every reference type is present in every spot. Infer within-type
60expression only where that type is supported.

CondSCVI to DestVI

from scvi.model import CondSCVI, DestVI

CondSCVI.setup_anndata(sc_adata, layer="counts", labels_key="cell_type")
reference = CondSCVI(sc_adata, weight_obs=False)
reference.train(max_epochs=300)
DestVI.setup_anndata(spatial_adata, layer="counts")
destvi = DestVI.from_rna_model(spatial_adata, reference)
destvi.train(max_epochs=2500)
proportions = destvi.get_proportions()  # spots-by-cell-types DataFrame
spatial_adata.obsm["proportions"] = proportions
for cell_type in proportions.columns:
    spatial_adata.obs[f"prop_{cell_type}"] = proportions[cell_type]
cell_type_expression = destvi.get_scale_for_ct("T cells")

DestVI requires a labeled CondSCVI, not SCVI, reference. The example's epoch counts are starting configurations, not convergence guarantees. Inspect reference fit and abundance support, sensitivity to cell-type granularity, and tissue markers. get_gamma() exposes within-type latent variables; it is not a matrix of gene scaling factors. DataFrame outputs use label/.iloc indexing, not [:, i].

The base model does not automatically use a spatial-neighbor prior simply because coordinates are present. Cell proportions are model estimates, not direct cell counts or proof that every reference type is present in every spot. Infer within-type expression only where that type is supported.

Raw lines 33 to 61
62## Stereoscope
64```python
65from scvi.external import RNAStereoscope, SpatialStereoscope
67RNAStereoscope.setup_anndata(sc_adata, labels_key="cell_type", layer="counts")
68rna = RNAStereoscope(sc_adata)
69rna.train(max_epochs=100)
70SpatialStereoscope.setup_anndata(spatial_adata, layer="counts")
71spatial = SpatialStereoscope.from_rna_model(spatial_adata, rna)
72spatial.train(max_epochs=2000)
73proportions = spatial.get_proportions()
74```
76Both datasets need aligned shared genes. Compare reference adequacy and held-out
77markers; there is no universal guarantee that this workflow is faster or more
78accurate than DestVI on a new assay.

Stereoscope

from scvi.external import RNAStereoscope, SpatialStereoscope

RNAStereoscope.setup_anndata(sc_adata, labels_key="cell_type", layer="counts")
rna = RNAStereoscope(sc_adata)
rna.train(max_epochs=100)
SpatialStereoscope.setup_anndata(spatial_adata, layer="counts")
spatial = SpatialStereoscope.from_rna_model(spatial_adata, rna)
spatial.train(max_epochs=2000)
proportions = spatial.get_proportions()

Both datasets need aligned shared genes. Compare reference adequacy and held-out markers; there is no universal guarantee that this workflow is faster or more accurate than DestVI on a new assay.

Raw lines 62 to 79
80## Tangram
82The 1.5.1 wrapper is PyTorch, uses **MuData**, and optimizes a cell-to-location
83mapping. It is not a generic optimal-transport API. Its projection methods differ
84from the standalone `tangram-sc` package, so do not mix their signatures.
86```python
87from mudata import MuData
88from scvi.external import Tangram
90# sc_mapping and sp_mapping have identical selected genes/order and
91# appropriately processed expression for Tangram (not a count likelihood).
92assert sc_mapping.var_names.equals(sp_mapping.var_names)
93map_data = MuData({"sc": sc_mapping, "sp": sp_mapping})
94Tangram.setup_mudata(
95 map_data, density_prior_key=None,
96 modalities={"sc_layer": "sc", "sp_layer": "sp", "density_prior_key": "sp"},
98tangram = Tangram(map_data)
99tangram.train()
100mapper = tangram.get_mapper_matrix()
101projected_types = Tangram.project_cell_annotations(
102 sc_mapping, sp_mapping, mapper, sc_mapping.obs["cell_type"],
104projected_genes = Tangram.project_genes(sc_mapping, sp_mapping, mapper)
105```
107This explicit example omits a density prior. To use one, create a correctly normalized
108spatial `.obs` column and pass its key; the default string alone is not a computed
109prior. The 1.5.1 implementation's feature-equality guard is ineffective (it tests a
110one-element tuple), so the explicit assertion is necessary. Tangram densifies its
111registered matrices and is not minibatched; estimate memory before fitting.
112Projection results are predictions, not observed expression or exact cell locations.
113Validate with held-out genes and spatial controls, and preserve the mapping's cell
114and spot order when projecting full-feature objects.

Tangram

The 1.5.1 wrapper is PyTorch, uses MuData, and optimizes a cell-to-location mapping. It is not a generic optimal-transport API. Its projection methods differ from the standalone tangram-sc package, so do not mix their signatures.

from mudata import MuData
from scvi.external import Tangram

# sc_mapping and sp_mapping have identical selected genes/order and
# appropriately processed expression for Tangram (not a count likelihood).
assert sc_mapping.var_names.equals(sp_mapping.var_names)
map_data = MuData({"sc": sc_mapping, "sp": sp_mapping})
Tangram.setup_mudata(
    map_data, density_prior_key=None,
    modalities={"sc_layer": "sc", "sp_layer": "sp", "density_prior_key": "sp"},
)
tangram = Tangram(map_data)
tangram.train()
mapper = tangram.get_mapper_matrix()
projected_types = Tangram.project_cell_annotations(
    sc_mapping, sp_mapping, mapper, sc_mapping.obs["cell_type"],
)
projected_genes = Tangram.project_genes(sc_mapping, sp_mapping, mapper)

This explicit example omits a density prior. To use one, create a correctly normalized spatial .obs column and pass its key; the default string alone is not a computed prior. The 1.5.1 implementation's feature-equality guard is ineffective (it tests a one-element tuple), so the explicit assertion is necessary. Tangram densifies its registered matrices and is not minibatched; estimate memory before fitting. Projection results are predictions, not observed expression or exact cell locations. Validate with held-out genes and spatial controls, and preserve the mapping's cell and spot order when projecting full-feature objects.

Raw lines 80 to 115
116## GIMVI
118```python
119import scvi
121scvi.external.GIMVI.setup_anndata(sc_adata, layer="counts")
122scvi.external.GIMVI.setup_anndata(spatial_panel_adata, layer="counts")
123gimvi = scvi.external.GIMVI(sc_adata, spatial_panel_adata)
124gimvi.train()
125sc_latent, spatial_latent = gimvi.get_latent_representation()
126_, imputed_spatial = gimvi.get_imputed_values(normalized=True)
127```
129Unlike DestVI, GIMVI can use a spatial feature subset to predict unmeasured genes;
130each spatial gene must be identifiable in the sequencing feature space. Preserve
131full RNA features and gene mapping rather than reducing both matrices blindly to
132the measured spatial panel. Evaluate imputation on held-out measured genes and do
133not use imputed values as independent experimental validation.

GIMVI

import scvi

scvi.external.GIMVI.setup_anndata(sc_adata, layer="counts")
scvi.external.GIMVI.setup_anndata(spatial_panel_adata, layer="counts")
gimvi = scvi.external.GIMVI(sc_adata, spatial_panel_adata)
gimvi.train()
sc_latent, spatial_latent = gimvi.get_latent_representation()
_, imputed_spatial = gimvi.get_imputed_values(normalized=True)

Unlike DestVI, GIMVI can use a spatial feature subset to predict unmeasured genes; each spatial gene must be identifiable in the sequencing feature space. Preserve full RNA features and gene mapping rather than reducing both matrices blindly to the measured spatial panel. Evaluate imputation on held-out measured genes and do not use imputed values as independent experimental validation.

Raw lines 116 to 134
135## SCVIVA
137The preprocessing method is named `preprocessing_anndata`, not
138`preprocess_anndata`. It needs labels, coordinates, sample membership and an
139expression embedding before neighborhood construction.
141```python
142# spatial_adata.obsm['X_scVI'] already contains an expression embedding.
143# Each sample has enough cells for the selected neighbor count.
144scvi.external.SCVIVA.preprocessing_anndata(
145 spatial_adata, sample_key="sample", labels_key="cell_type",
146 cell_coordinates_key="spatial", expression_embedding_key="X_scVI",
147 k_nn=20,
149scvi.external.SCVIVA.setup_anndata(
150 spatial_adata, layer="counts", sample_key="sample", labels_key="cell_type",
151 cell_coordinates_key="spatial", expression_embedding_key="X_scVI",
153scviva = scvi.external.SCVIVA(spatial_adata)
154scviva.train()
155spatial_adata.obsm["X_scVIVA"] = scviva.get_latent_representation()
156```
158Keep sample boundaries and coordinate units explicit; do not connect separate slides
159in a common numerical coordinate plane. Neighborhood effects are associations,
160not evidence of cell-cell causal signaling. Test robustness to neighborhood size,
161label uncertainty and tissue coverage.

SCVIVA

The preprocessing method is named preprocessing_anndata, not preprocess_anndata. It needs labels, coordinates, sample membership and an expression embedding before neighborhood construction.

# spatial_adata.obsm['X_scVI'] already contains an expression embedding.
# Each sample has enough cells for the selected neighbor count.
scvi.external.SCVIVA.preprocessing_anndata(
    spatial_adata, sample_key="sample", labels_key="cell_type",
    cell_coordinates_key="spatial", expression_embedding_key="X_scVI",
    k_nn=20,
)
scvi.external.SCVIVA.setup_anndata(
    spatial_adata, layer="counts", sample_key="sample", labels_key="cell_type",
    cell_coordinates_key="spatial", expression_embedding_key="X_scVI",
)
scviva = scvi.external.SCVIVA(spatial_adata)
scviva.train()
spatial_adata.obsm["X_scVIVA"] = scviva.get_latent_representation()

Keep sample boundaries and coordinate units explicit; do not connect separate slides in a common numerical coordinate plane. Neighborhood effects are associations, not evidence of cell-cell causal signaling. Test robustness to neighborhood size, label uncertainty and tissue coverage.

Raw lines 135 to 162
163## RESOLVI
165RESOLVI models expression contamination/background and misassignment in
166single-cell-resolution spatial assays. It is not a general multiscale resolution
167converter or a generic substitute for spot deconvolution.
169```python
170scvi.external.RESOLVI.setup_anndata(
171 spatial_adata, layer="counts", batch_key="slide",
172 prepare_data_kwargs={"spatial_rep": "spatial", "n_neighbors": 10},
174resolvi = scvi.external.RESOLVI(spatial_adata)
175resolvi.train()
176corrected_expression = resolvi.get_normalized_expression()
177```
179Setup prepares spatial neighbors by default. Supply the actual coordinate key:
180its internal default is `X_spatial`, whereas many readers use `spatial`. There must
181be enough cells within each slide for the requested neighborhood. Its validation
182split support differs from SCVI; 1.5 raises errors for unsupported validation settings.
183Assess contamination assumptions and rare-cell preservation using external controls.
185For plotting proportions, put named columns in `.obs` and use the appropriate
186spatial plotting API with correct image/coordinate scaling. Do not pass a Series
187as a `color` key or treat spatial autocorrelation of inferred proportions as
188independent confirmation of the same model.
190Sources: [spatial maintenance](https://github.com/scverse/scvi-tools/blob/1.5.1/docs/user_guide/models/destvi.md),
191[DestVI](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/model/_destvi.py),
192[Stereoscope](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/external/stereoscope/_model.py),
193[Tangram](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/external/tangram/_model.py),
194[GIMVI](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/external/gimvi/_model.py),
195[SCVIVA](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/external/scviva/_model.py),
196[RESOLVI](https://github.com/scverse/scvi-tools/blob/1.5.1/src/scvi/external/resolvi/_model.py).

RESOLVI

RESOLVI models expression contamination/background and misassignment in single-cell-resolution spatial assays. It is not a general multiscale resolution converter or a generic substitute for spot deconvolution.

scvi.external.RESOLVI.setup_anndata(
    spatial_adata, layer="counts", batch_key="slide",
    prepare_data_kwargs={"spatial_rep": "spatial", "n_neighbors": 10},
)
resolvi = scvi.external.RESOLVI(spatial_adata)
resolvi.train()
corrected_expression = resolvi.get_normalized_expression()

Setup prepares spatial neighbors by default. Supply the actual coordinate key: its internal default is X_spatial, whereas many readers use spatial. There must be enough cells within each slide for the requested neighborhood. Its validation split support differs from SCVI; 1.5 raises errors for unsupported validation settings. Assess contamination assumptions and rare-cell preservation using external controls.

For plotting proportions, put named columns in .obs and use the appropriate spatial plotting API with correct image/coordinate scaling. Do not pass a Series as a color key or treat spatial autocorrelation of inferred proportions as independent confirmation of the same model.

Sources: spatial maintenance, DestVI, Stereoscope, Tangram, GIMVI, SCVIVA, RESOLVI.

Raw lines 163 to 196