| name | bio-spatial-transcriptomics-spatial-domains |
| description | Identify spatial domains and tissue regions in spatial transcriptomics data using Squidpy and Scanpy. Cluster spots considering both expression and spatial context to define anatomical regions. Use when identifying tissue domains or spatial regions. |
| tool_type | python |
| primary_tool | squidpy |
| measurable_outcome | Execute skill workflow successfully with valid output within 15 minutes. |
| allowed-tools | ["read_file","run_shell_command"] |
Spatial Domain Detection
Identify spatial domains and tissue regions by combining expression and spatial information.
Required Imports
import squidpy as sq
import scanpy as sc
import numpy as np
import matplotlib.pyplot as plt
Standard Clustering (Expression Only)
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 with Squidpy
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
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'])
BayesSpace (R Integration)
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
pandas2ri.activate()
ro.r('sce <- readRDS("sce_bayesspace.rds")')
spatial_clusters = ro.r('colData(sce)$spatial.cluster')
adata.obs['bayesspace'] = list(spatial_clusters)
STAGATE for Spatial Domains
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')
Evaluate Domain Quality
from sklearn.metrics import silhouette_score
coords = adata.obsm['spatial']
labels = adata.obs['spatial_leiden'].values
spatial_silhouette = silhouette_score(coords, labels)
print(f'Spatial silhouette score: {spatial_silhouette:.3f}')
expr_silhouette = silhouette_score(adata.obsm['X_pca'], labels)
print(f'Expression silhouette score: {expr_silhouette:.3f}')
Refine Domain Boundaries
from scipy import sparse
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, 'leiden', n_iter=2)
sq.pl.spatial_scatter(adata, color=['leiden', 'leiden_smoothed'], ncols=2)
Compare Domain Methods
from sklearn.metrics import adjusted_rand_score
methods = ['leiden', 'spatial_leiden', 'combined_leiden']
for i, m1 in enumerate(methods):
for m2 in methods[i+1:]:
ari = adjusted_rand_score(adata.obs[m1], adata.obs[m2])
print(f'{m1} vs {m2}: ARI = {ari:.3f}')
Domain Markers
sc.tl.rank_genes_groups(adata, groupby='spatial_leiden', method='wilcoxon')
markers = sc.get.rank_genes_groups_df(adata, group=None)
print(markers.groupby('group').head(5))
top_markers = markers.groupby('group').head(1)['names'].tolist()
sq.pl.spatial_scatter(adata, color=top_markers[:6], ncols=3)
Annotate Domains
domain_annotations = {
'0': 'White matter',
'1': 'Cortex layer 1',
'2': 'Cortex layer 2/3',
'3': 'Cortex layer 4',
'4': 'Cortex layer 5',
'5': 'Cortex layer 6',
}
adata.obs['domain'] = adata.obs['spatial_leiden'].map(domain_annotations)
sq.pl.spatial_scatter(adata, color='domain', size=1.3)
Related Skills
- spatial-neighbors - Build spatial graphs (prerequisite)
- spatial-statistics - Compute spatial statistics per domain
- single-cell/clustering - Standard clustering methods