domains / science / spatial-transcriptomics-deconvolution / spatial-deconvolution
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.
| Repository | Path |
|---|---|
| FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-deconvolution/SKILL.md |
| GPTomics/bioSkills | spatial-transcriptomics/spatial-deconvolution/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-deconvolution/SKILL.md |
| peer | GPTomics/bioSkills | spatial-transcriptomics/spatial-deconvolution/SKILL.md |
---
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)
```