domains / science / spatial-transcriptomics-preprocessing / spatial-preprocessing

spatial-preprocessing

Skill card

combined

The two skills cover the same task but disagree on the core procedure. The first copy (OpenClaw) provides a standard, platform-agnostic pipeline using Scanpy/Squidpy with fixed thresholds (min_counts=500, min_genes=200) and standard normalization (CP10k/log1p or Pearson residuals). The second copy (GPTomics) explicitly argues that these standard thresholds and normalization methods are incorrect for imaging-based spatial platforms (Xenium, MERFISH, CosMx), stating that they delete real cells and introduce bias. It mandates a 'platform-class fork' decision and different QC/normalization strategies (e.g., area/volume normalization) for imaging data. Because the first copy's procedure is actively contradicted by the second for a major subset of spatial data, crowning one would hide a critical alternative workflow.

Repositories

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

Other copies

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

Combined skill

# Spatial Preprocessing

## 1. Determine Platform Class
Before applying any thresholds, identify the assay family:
- **Sequencing/Spot** (Visium, Slide-seq, Stereo-seq): Unit is a spot (1-10 cells). Library size confounds with cellularity.
- **Imaging/In-situ** (Xenium, MERFISH, CosMx): Unit is a segmented cell. Library size confounds with cell area/size. Targeted panel.

## 2. Calculate QC Metrics
```python
import squidpy as sq
import scanpy as sc

# Standard metrics
sc.pp.calculate_qc_metrics(adata, inplace=True)

# Mitochondrial (only if on-panel)
if 'MT-' in adata.var_names.values:
    adata.var['mt'] = adata.var_names.str.startswith('MT-')
    sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True)
```

## 3. Filter Spots/Cells
**Choose thresholds based on platform:**
- **Sequencing/Spot:** Use standard scRNA-like floors (e.g., `min_counts=500-1000`, `min_genes=200-500`). Filter high mito% (<20%).
- **Imaging/In-situ:** Use low transcript floors (e.g., `min_counts=10-20`). Do NOT use high gene counts (ceilinged by panel). Do NOT use mito% if off-panel. Consider cell area/size metrics.

```python
# Example for Sequencing
sc.pp.filter_cells(adata, min_counts=500)
sc.pp.filter_cells(adata, min_genes=200)

# Example for Imaging
sc.pp.filter_cells(adata, min_counts=10)
```

## 4. Filter Genes
Remove genes detected in too few cells/spots.
```python
sc.pp.filter_genes(adata, min_cells=10)
```

## 5. Normalization
**Choose method based on platform:**
- **Sequencing/Spot:** Standard CP10k + log1p or Pearson residuals (SCTransform-like).
- **Imaging/In-situ:** Avoid gene-count-based normalization (CP10k, Pearson) on skewed panels. Use cell volume/area normalization or spatially-aware methods (e.g., SpaNorm) to avoid region-specific bias.

```python
# Sequencing
adata.layers['counts'] = adata.X.copy()
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)

# Imaging (Pseudocode for area normalization)
# adata.X = adata.X / adata.obs['cell_area']
```

## 6. Feature Selection
Identify Highly Variable Genes (HVGs) and Spatially Variable Genes (SVGs).
```python
# HVGs
sc.pp.highly_variable_genes(adata, n_top_genes=2000, flavor='seurat_v3', layer='counts')

# SVGs (requires spatial neighbors)
sq.gr.spatial_neighbors(adata, coord_type='generic', n_neighs=6)
sq.gr.spatial_autocorr(adata, mode='moran', genes=adata.var_names[:1000])
```

## 7. Scale and PCA
```python
sc.pp.scale(adata, max_value=10)
sc.tl.pca(adata, n_comps=50)
```

## 8. Visualize QC Spatially
Always inspect QC metrics on the tissue image to detect spatial artifacts (e.g., edge effects, hybridization failures) that 1D distributions miss.
```python
sq.pl.spatial_scatter(adata, color=['total_counts', 'n_genes_by_counts'])
```