File size: 5,110 Bytes
bd52a47 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | # 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:
1. **scRNA-seq reference** with cell type annotations
2. **Spatial data** with:
- Gene expression
- Cell segmentation from histology (via Squidpy)
---
## Workflow Overview
1. Segment cells from histology image (Squidpy)
2. Calculate image features
3. Preprocess for Tangram
4. Run constrained mapping
5. Assign cell types to segments
6. Visualize deconvolved cells
---
## Step 1: Cell Segmentation (Squidpy)
If not already done, segment cells from histology:
```python
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:
```python
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:
```python
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
```python
# 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
```python
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 counts
- `tangram_constrained_mapping.h5ad` - Constrained mapping result
- `deconvolved_cells.h5ad` - Individual cell annotations
- Visualization plots
---
## Interpreting Results
The deconvolved AnnData contains:
- `obs['cluster']`: Assigned cell type for each segmented cell
- `obs['x']`, `obs['y']`: Spatial coordinates of cell centroids
- `obsm['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
1. **Segmentation quality matters**: Poor segmentation = poor deconvolution
2. **Use GPU**: Constrained mode is computationally intensive
3. **More epochs**: Use 1000+ epochs for constrained mode
4. **Validate patterns**: Check that deconvolved cell types match expected tissue architecture
5. **Compare to spot-level**: Deconvolved proportions should roughly match spot-level predictions
|