domains / science / spatial-domain-detection / spatial-domains

spatial-domains

Skill card

combined

The two skills cover the same task but with different depth and scope. The first copy (OpenClaw) is a practical, code-heavy recipe for Squidpy/Scanpy, including a specific combined-graph implementation, a custom smoothing function, and marker/annotation steps. The second copy (GPTomics) is a conceptual and methodological guide that distinguishes domains from niches, provides a decision matrix for choosing between BayesSpace, BASS, STAGATE, GraphST, SpaGCN, and BANKSY, and emphasizes the 'spatial-weight' knob and biological choice of k. The first copy lacks the method-selection logic and the domain/niche distinction, while the second copy lacks the specific Python implementation for combined graphs and the custom smoothing code. Combining them provides both the 'how to choose' and the 'how to code' aspects.

Repositories

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

Other copies

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

Combined skill

# Spatial Domain Detection

## 1. Conceptual Framework
- **Domain vs. Niche vs. Cell Type**: A spatial domain is a contiguous tissue region (e.g., cortical layer) containing many cell types. It is distinct from a cell type (single-cell identity) and a niche (local cell-type composition). Ensure the biological question matches domain segmentation.
- **Key Decision**: The primary parameter is the **spatial weight** (e.g., BANKSY lambda, BayesSpace smoothing). Too much weight causes over-smoothing (blobs); too little causes salt-and-pepper noise.
- **Number of Domains (k)**: Treat k as a biological choice. Justify k and show sensitivity to k±1. Do not rely solely on silhouette scores for selection.

## 2. Method Selection
Choose a method based on tissue geometry and data type:
- **BayesSpace**: Best for Visium laminar/continuous tissue; provides sub-spot enhancement.
- **STAGATE**: Graph attention; good for continuous tissue and high-res data (Slide-seq/Stereo-seq).
- **GraphST**: Self-supervised; good for low-res Visium and integration.
- **BANKSY**: Neighbor-augmented; flexible spatial weight.
- **SpaGCN**: Use if H&E histology is available and informative.

## 3. Implementation (Squidpy/Scanpy)

### Standard Clustering (Baseline)
```python
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)
sc.tl.leiden(adata, resolution=0.5, key_added='leiden')
sq.pl.spatial_scatter(adata, color='leiden', size=1.3)
```

### Spatial-Aware Clustering
```python
sq.gr.spatial_neighbors(adata, coord_type='generic', n_neighs=6)
sc.tl.leiden(adata, resolution=0.5, key_added='spatial_leiden', neighbors_key='spatial_neighbors')
sq.pl.spatial_scatter(adata, color='spatial_leiden', size=1.3)
```

### Combined Expression + Spatial Graph
```python
from scipy.sparse import csr_matrix
from sklearn.preprocessing import normalize

sq.gr.spatial_neighbors(adata, coord_type='generic', n_neighs=6)
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)

spatial_weight = 0.3
spatial_conn = adata.obsp['spatial_connectivities']
expr_conn = adata.obsp['connectivities']

spatial_norm = normalize(spatial_conn, norm='l1', axis=1)
expr_norm = normalize(expr_conn, norm='l1', axis=1)

combined = spatial_weight * spatial_norm + (1 - spatial_weight) * expr_norm
adata.obsp['combined_connectivities'] = csr_matrix(combined)

sc.tl.leiden(adata, resolution=0.5, key_added='combined_leiden', adjacency=adata.obsp['combined_connectivities'])
```

### STAGATE
```python
import STAGATE
STAGATE.Cal_Spatial_Net(adata, rad_cutoff=150)
STAGATE.Stats_Spatial_Net(adata)
adata = STAGATE.train_STAGATE(adata, alpha=0)
sc.pp.neighbors(adata, use_rep='STAGATE')
sc.tl.leiden(adata, resolution=0.5, key_added='stagate_leiden')
```

## 4. Refinement and Evaluation

### Smooth Domain Boundaries
```python
def smooth_domains(adata, cluster_key, n_iter=1):
    conn = adata.obsp['spatial_connectivities']
    labels = adata.obs[cluster_key].values
    categories = adata.obs[cluster_key].cat.categories
    for _ in range(n_iter):
        new_labels = []
        for i in range(adata.n_obs):
            neighbors = conn[i].nonzero()[1]
            if len(neighbors) > 0:
                neighbor_labels = labels[neighbors]
                unique, counts = np.unique(neighbor_labels, return_counts=True)
                new_labels.append(unique[counts.argmax()])
            else:
                new_labels.append(labels[i])
        labels = np.array(new_labels)
    adata.obs[f'{cluster_key}_smoothed'] = pd.Categorical(labels, categories=categories)

smooth_domains(adata, 'spatial_leiden', n_iter=2)
```

### Evaluate Quality
- **Spatial Coherence**: Use silhouette score on spatial coordinates as a sanity check, but prioritize biological validation and k-sensitivity.
- **Marker Genes**: Use `sc.tl.rank_genes_groups` to identify markers for each domain.
- **Comparison**: Use Adjusted Rand Index (ARI) to compare different clustering methods.

## 5. Annotation
- Manually annotate domains based on marker genes and histology.
- Report the chosen k and the spatial weight used.