domains / science / spatial-transcriptomics-deconvolution / spatial-deconvolution

spatial-deconvolution

Skill card

combined

The two skills cover the same task but have materially different scope and constraints. The OpenClaw copy is a direct execution recipe for cell2location, Tangram, and RCTD with specific code and parameters. The GPTomics copy is a decision framework that adds a critical 'resolution fork' (deconvolve vs. segment), emphasizes reference quality over method choice, and includes version compatibility checks and compositional data handling. Crowning one would hide the other's essential guidance: the OpenClaw copy lacks the platform-specific decision logic, while the GPTomics copy lacks the concrete, runnable code for the primary tools.

Repositories

RepositoryPath
FreedomIntelligence/OpenClaw-Medical-Skillsskills/spatial-transcriptomics-analysis/bioSkills/spatial-deconvolution/SKILL.md
GPTomics/bioSkillsspatial-transcriptomics/spatial-deconvolution/SKILL.md

Other copies

RoleRepositoryPath
peerFreedomIntelligence/OpenClaw-Medical-Skillsskills/spatial-transcriptomics-analysis/bioSkills/spatial-deconvolution/SKILL.md
peerGPTomics/bioSkillsspatial-transcriptomics/spatial-deconvolution/SKILL.md

Combined skill

---
name: bio-spatial-transcriptomics-spatial-deconvolution
description: Estimate cell type composition in spatial transcriptomics spots using reference-based deconvolution. Use cell2location, RCTD, SPOTlight, or Tangram to infer cell type proportions from scRNA-seq references. Use when estimating cell type composition in spatial spots.
tool_type: python
primary_tool: cell2location
---

# Spatial Deconvolution

Estimate cell type composition in spatial spots using scRNA-seq references.

## Version Compatibility

Reference examples tested with: cell2location 0.1.4+, scvi-tools 1.0+, scanpy 1.10+, anndata 0.10+, numpy 1.26+.

Before using code patterns, verify installed versions match. If versions differ, introspect the installed package and adapt the example to match the actual API.

## The Resolution Fork

Deconvolution recovers the cell-type composition of a MIXTURE. Determine if your data contains mixtures before choosing a method:
- **DECONVOLVE**: Visium, GeoMx, and other multi-cell capture arrays.
- **SEGMENT**: Xenium, MERFISH, CosMx. These are single-cell data. Do NOT deconvolve; use segmentation and annotation instead.
- **AMBIGUOUS**: Visium HD, Stereo-seq, Slide-seqV2. Consider cell reconstruction or deconvolution depending on available morphology.

## Required Imports

```python
import scanpy as sc
import anndata as ad
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```

## Using cell2location

```python
import cell2location
from cell2location.utils.filtering import filter_genes
from cell2location.models import RegressionModel

# Load reference scRNA-seq
adata_ref = sc.read_h5ad('reference_scrna.h5ad')
adata_ref.obs['cell_type'] = adata_ref.obs['cell_type'].astype('category')

# Load spatial data
adata_vis = sc.read_h5ad('spatial_data.h5ad')

# Find shared genes
intersect = np.intersect1d(adata_vis.var_names, adata_ref.var_names)
adata_ref = adata_ref[:, intersect].copy()
adata_vis = adata_vis[:, intersect].copy()
```

## Train Reference Signature Model

```python
# Select genes for deconvolution
selected = filter_genes(adata_ref, cell_count_cutoff=5, cell_percentage_cutoff2=0.03,
                        nonz_mean_cutoff=1.12)
adata_ref = adata_ref[:, selected].copy()

# Prepare reference
cell2location.models.RegressionModel.setup_anndata(
    adata_ref,
    labels_key='cell_type',
)

# Train reference model
mod = RegressionModel(adata_ref)
mod.train(max_epochs=250, use_gpu=True)

# Export reference signatures
adata_ref = mod.export_posterior(adata_ref, sample_kwargs={'num_samples': 1000})
ref_sig = adata_ref.varm['means_per_cluster_mu_fg']
```

## Run Spatial Deconvolution

```python
# Ensure spatial data has same genes
adata_vis = adata_vis[:, adata_ref.var_names].copy()

# Setup spatial data
cell2location.models.Cell2location.setup_anndata(adata_vis)

# Train deconvolution model
mod_spatial = cell2location.models.Cell2location(
    adata_vis,
    cell_state_df=ref_sig,
    N_cells_per_location=10,  # Expected cells per spot
    detection_alpha=20,
)
mod_spatial.train(max_epochs=30000, use_gpu=True)

# Export results
adata_vis = mod_spatial.export_posterior(adata_vis, sample_kwargs={'num_samples': 1000})
```

## Access Deconvolution Results

```python
# Cell type abundances stored in obsm
abundances = adata_vis.obsm['q05_cell_abundance_w_sf']

# Convert to proportions
proportions = abundances / abundances.sum(axis=1, keepdims=True)
adata_vis.obsm['cell_type_proportions'] = proportions

# Add dominant cell type
cell_types = adata_ref.obs['cell_type'].cat.categories
adata_vis.obs['dominant_cell_type'] = cell_types[proportions.argmax(axis=1)]
```

## Using Tangram (Alternative)

```python
import tangram as tg

# Load data
adata_sc = sc.read_h5ad('reference_scrna.h5ad')
adata_sp = sc.read_h5ad('spatial_data.h5ad')

# Preprocess
sc.pp.normalize_total(adata_sc)
sc.pp.log1p(adata_sc)

# Find marker genes
sc.tl.rank_genes_groups(adata_sc, groupby='cell_type', method='wilcoxon')
markers = sc.get.rank_genes_groups_df(adata_sc, group=None)
markers = markers[markers['pvals_adj'] < 0.01].groupby('group').head(100)
marker_genes = markers['names'].unique().tolist()

# Prepare for Tangram
tg.pp_adatas(adata_sc, adata_sp, genes=marker_genes)

# Map single cells to spatial locations
ad_map = tg.map_cells_to_space(
    adata_sc,
    adata_sp,
    mode='clusters',
    cluster_label='cell_type',
    device='cuda:0',
)

# Get cell type proportions
tg.project_cell_annotations(ad_map, adata_sp, annotation='cell_type')
```

## Using RCTD (via R)

```python
# RCTD runs in R; use rpy2 for integration
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
pandas2ri.activate()

# Save data for R
adata_vis.write_h5ad('spatial_for_rctd.h5ad')
adata_ref.write_h5ad('reference_for_rctd.h5ad')

# R code for RCTD
r_code = '''
library(spacexr)
library(Seurat)

# Load data (convert from h5ad first)
# ... R-specific loading code ...

# Create RCTD object
rctd <- create.RCTD(puck, reference, max_cores=4)
rctd <- run_RCTD(rctd, doublet_mode='full')

# Get results
results <- rctd@results
weights <- normalize_weights(results$weights)
'''
```

## Visualize Cell Type Proportions

```python
# Plot cell type abundances spatially
cell_types_to_plot = ['T_cell', 'Macrophage', 'Epithelial', 'Fibroblast']

fig, axes = plt.subplots(2, 2, figsize=(12, 12))
for ax, ct in zip(axes.flatten(), cell_types_to_plot):
    ct_idx = list(adata_ref.obs['cell_type'].cat.categories).index(ct)
    adata_vis.obs[f'{ct}_proportion'] = proportions[:, ct_idx]
    sc.pl.spatial(adata_vis, color=f'{ct}_proportion', ax=ax, show=False,
                  title=ct, cmap='Reds', vmin=0, vmax=1)
plt.tight_layout()
plt.savefig('cell_type_proportions.png', dpi=150)
```