Cell Deconvolution with Tangram
Deconvolve spatial spots into individual cells using Tangram's constrained mapping mode with cell segmentation.
When to Use
- Spatial technology has multiple cells per spot (Visium, Slide-seq)
- You have histology images with cell segmentation
- Goal: Assign cell types to individual segmented cells
Requirements
Before running deconvolution, you need:
- scRNA-seq reference with cell type annotations
- Spatial data with:
- Gene expression
- Cell segmentation from histology (via Squidpy)
Workflow Overview
- Segment cells from histology image (Squidpy)
- Calculate image features
- Preprocess for Tangram
- Run constrained mapping
- Assign cell types to segments
- Visualize deconvolved cells
Step 1: Cell Segmentation (Squidpy)
If not already done, segment cells from histology:
import squidpy as sq
import scanpy as sc
# Load spatial data with image
adata_sp = sc.read_h5ad("spatial.h5ad")
img = sq.datasets.visium_fluo_image_crop() # or load your image
# Smooth image
sq.im.process(img=img, layer="image", method="smooth")
# Segment nuclei
sq.im.segment(
img=img,
layer="image_smooth",
method="watershed",
channel=0, # DAPI channel
)
# Calculate features (cell counts per spot)
sq.im.calculate_image_features(
adata_sp,
img,
layer="image",
features="segmentation",
features_kwargs={
"segmentation": {
"label_layer": "segmented_watershed",
"props": ["label", "centroid"],
}
},
mask_circle=True,
)
# Check cell counts
print(adata_sp.obs["cell_count"].describe())
adata_sp.write_h5ad("spatial_with_segmentation.h5ad")
Step 2: Visualize Segmentation
Verify segmentation quality:
import matplotlib.pyplot as plt
sc.pl.spatial(adata_sp, color="cell_count", title="Cells per Spot")
plt.savefig("cell_counts.png")
# Check total cells
total_cells = adata_sp.obsm["image_features"]["segmentation_label"].sum()
print(f"Total segmented cells: {total_cells}")
Step 3: Preprocess for Tangram
Tool: tangram_preprocess
tangram_preprocess(
adata_sc_path="scrna.h5ad",
adata_sp_path="spatial_with_segmentation.h5ad",
marker_genes="auto",
cell_type_key="cell_type"
)
Step 4: Constrained Mapping
Run Tangram in constrained mode to fit exactly the number of segmented cells:
import scanpy as sc
import tangram as tg
import numpy as np
# Load preprocessed data
adata_sc = sc.read_h5ad("experiments/tangram_sc_prep.h5ad")
adata_sp = sc.read_h5ad("experiments/tangram_sp_prep.h5ad")
# Get cell counts from segmentation
cell_counts = adata_sp.obsm["image_features"]["segmentation_label"]
target_count = int(cell_counts.sum())
density_prior = np.array(cell_counts) / cell_counts.sum()
print(f"Target cell count: {target_count}")
# Constrained mapping
ad_map = tg.map_cells_to_space(
adata_sc,
adata_sp,
mode="constrained",
target_count=target_count,
density_prior=density_prior,
num_epochs=1000,
device="cuda:0", # GPU recommended
)
ad_map.write_h5ad("experiments/tangram_constrained_mapping.h5ad")
Step 5: Assign Cell Types to Segments
# Create segment dataframe
tg.create_segment_cell_df(adata_sp)
# Count cell types per spot
tg.count_cell_annotations(
ad_map,
adata_sc,
adata_sp,
annotation="cell_type",
)
# Deconvolve to individual cells
adata_segment = tg.deconvolve_cell_annotations(adata_sp)
adata_segment.write_h5ad("experiments/deconvolved_cells.h5ad")
print(f"Deconvolved {adata_segment.shape[0]} cells")
print(adata_segment.obs["cluster"].value_counts())
Step 6: Visualize Deconvolved Cells
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 12))
sc.pl.spatial(
adata_segment,
color="cluster",
size=0.4,
show=False,
frameon=False,
alpha_img=0.2,
ax=ax,
)
plt.title("Deconvolved Cell Types")
plt.savefig("deconvolved_spatial.png", dpi=200)
Output Files
spatial_with_segmentation.h5ad- Spatial data with cell countstangram_constrained_mapping.h5ad- Constrained mapping resultdeconvolved_cells.h5ad- Individual cell annotations- Visualization plots
Interpreting Results
The deconvolved AnnData contains:
obs['cluster']: Assigned cell type for each segmented cellobs['x'],obs['y']: Spatial coordinates of cell centroidsobsm['spatial']: Spatial coordinates array
Note: Deconvolution assigns cell types probabilistically based on the mapping. Cells are assigned to the most likely type given the spot's expression profile.
Tips
- Segmentation quality matters: Poor segmentation = poor deconvolution
- Use GPU: Constrained mode is computationally intensive
- More epochs: Use 1000+ epochs for constrained mode
- Validate patterns: Check that deconvolved cell types match expected tissue architecture
- Compare to spot-level: Deconvolved proportions should roughly match spot-level predictions