| name | bio-imaging-mass-cytometry-spatial-analysis |
| description | Spatial analysis of cell neighborhoods and interactions in IMC data. Covers neighbor graphs, spatial statistics, and interaction testing. Use when analyzing spatial relationships between cell types, testing for neighborhood enrichment, or identifying cell-cell interaction patterns in imaging mass cytometry data. |
| tool_type | python |
| primary_tool | squidpy |
Spatial Analysis for IMC
Build Spatial Graph
import squidpy as sq
import anndata as ad
adata = ad.read_h5ad('imc_phenotyped.h5ad')
sq.gr.spatial_neighbors(adata, coord_type='generic', delaunay=True)
sq.gr.spatial_neighbors(adata, coord_type='generic', radius=50)
print(f'Built graph with {adata.obsp["spatial_connectivities"].nnz} edges')
Neighborhood Enrichment
sq.gr.nhood_enrichment(adata, cluster_key='cell_type')
sq.pl.nhood_enrichment(adata, cluster_key='cell_type', save='nhood_enrichment.png')
zscore = adata.uns['cell_type_nhood_enrichment']['zscore']
Co-occurrence Analysis
sq.gr.co_occurrence(adata, cluster_key='cell_type')
sq.pl.co_occurrence(adata, cluster_key='cell_type', save='co_occurrence.png')
Ripley's Statistics
sq.gr.ripley(adata, cluster_key='cell_type', mode='L')
sq.pl.ripley(adata, cluster_key='cell_type', save='ripley.png')
Cell-Cell Interaction
sq.gr.interaction_matrix(adata, cluster_key='cell_type', normalized=True)
interaction = adata.uns['cell_type_interactions']
Custom Neighborhood Analysis
import pandas as pd
import numpy as np
from scipy.sparse import csr_matrix
def neighborhood_composition(adata, cluster_key='cell_type'):
'''Calculate cell type composition of each cell's neighborhood'''
conn = adata.obsp['spatial_connectivities']
cell_types = adata.obs[cluster_key]
type_categories = cell_types.cat.categories
type_onehot = pd.get_dummies(cell_types).values
nhood_composition = conn @ type_onehot
nhood_sum = np.array(nhood_composition.sum(axis=1)).flatten()
nhood_sum[nhood_sum == 0] = 1
nhood_frac = nhood_composition / nhood_sum[:, np.newaxis]
for i, ct in enumerate(type_categories):
adata.obs[f'nhood_frac_{ct}'] = nhood_frac[:, i]
return nhood_frac
nhood_frac = neighborhood_composition(adata)
Spatial Clustering
sq.gr.spatial_neighbors(adata, coord_type='generic', radius=30)
sc.tl.leiden(adata, adjacency=adata.obsp['spatial_connectivities'],
resolution=0.5, key_added='spatial_cluster')
Interaction Hotspots
def find_interaction_hotspots(adata, type1, type2, cluster_key='cell_type', radius=50):
'''Find regions with high interaction between two cell types'''
mask1 = adata.obs[cluster_key] == type1
mask2 = adata.obs[cluster_key] == type2
spatial = adata.obsm['spatial']
from scipy.spatial import cKDTree
tree2 = cKDTree(spatial[mask2])
interaction_scores = np.zeros(mask1.sum())
for i, (x, y) in enumerate(spatial[mask1]):
neighbors = tree2.query_ball_point([x, y], r=radius)
interaction_scores[i] = len(neighbors)
return interaction_scores
cd8_tumor_interactions = find_interaction_hotspots(adata, 'CD8 T cell', 'Tumor', radius=30)
Visualize Spatial Patterns
import matplotlib.pyplot as plt
sq.pl.spatial_scatter(adata, color='cell_type', size=3, save='spatial_celltypes.png')
sq.pl.spatial_scatter(adata, color=['CD8', 'CD4', 'CD68'], size=2, save='spatial_markers.png')
fig, ax = plt.subplots(figsize=(10, 10))
spatial = adata.obsm['spatial']
ax.scatter(spatial[:, 0], spatial[:, 1], c='lightgray', s=1, alpha=0.5)
for ct, color in [('CD8 T cell', 'red'), ('Tumor', 'blue')]:
mask = adata.obs['cell_type'] == ct
ax.scatter(spatial[mask, 0], spatial[mask, 1], c=color, s=5, label=ct)
ax.legend()
ax.set_aspect('equal')
plt.savefig('cd8_tumor_spatial.png', dpi=150)
Statistical Testing
from scipy import stats
def spatial_association_test(adata, type1, type2, cluster_key='cell_type', n_perm=1000):
'''Permutation test for spatial association between cell types'''
sq.gr.nhood_enrichment(adata, cluster_key=cluster_key)
obs_zscore = adata.uns[f'{cluster_key}_nhood_enrichment']['zscore']
idx1 = list(adata.obs[cluster_key].cat.categories).index(type1)
idx2 = list(adata.obs[cluster_key].cat.categories).index(type2)
observed = obs_zscore[idx1, idx2]
pvalue = 2 * (1 - stats.norm.cdf(abs(observed)))
return {'zscore': observed, 'pvalue': pvalue}
result = spatial_association_test(adata, 'CD8 T cell', 'Tumor')
print(f"CD8-Tumor association: z={result['zscore']:.2f}, p={result['pvalue']:.4f}")
Export Results
adata.write('imc_spatial_analyzed.h5ad')
nhood_df = pd.DataFrame(
adata.uns['cell_type_nhood_enrichment']['zscore'],
index=adata.obs['cell_type'].cat.categories,
columns=adata.obs['cell_type'].cat.categories
)
nhood_df.to_csv('neighborhood_enrichment.csv')
Related Skills
- phenotyping - Assign cell types first
- spatial-transcriptomics/spatial-statistics - Similar spatial methods
- single-cell/cell-communication - Interaction concepts