domains / science / spatial-neighbor-graphs / spatial-neighbors
The two skills cover the same task but with different levels of depth and focus. The first copy (OpenClaw) is a practical, code-heavy recipe book that provides specific Python snippets for building various graph types, accessing data, and visualizing results. The second copy (GPTomics) is a conceptual guide that emphasizes the statistical implications of graph choice, the 'modifiable areal unit problem,' and decision-making criteria (e.g., when to use grid vs. generic, density biases). The first copy lacks the critical reasoning about why graph choice affects downstream statistics, while the second copy lacks the specific implementation details for visualization, specific spot querying, and combining spatial/expression graphs found in the first. Combining them provides both the 'how' and the 'why'.
| Repository | Path |
|---|---|
| FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-neighbors/SKILL.md |
| GPTomics/bioSkills | spatial-transcriptomics/spatial-neighbors/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-neighbors/SKILL.md |
| peer | GPTomics/bioSkills | spatial-transcriptomics/spatial-neighbors/SKILL.md |
---
name: bio-spatial-transcriptomics-spatial-neighbors
description: Build spatial neighbor graphs for spatial transcriptomics data using Squidpy. Covers graph type selection (kNN, Delaunay, radius, grid), implementation, and the statistical implications of graph choice.
tool_type: python
primary_tool: squidpy
---
# Spatial Neighbor Graphs
Build spatial neighbor graphs for connectivity-based analyses. The graph is the model that all spatial statistics (Moran's I, enrichment, domains) inherit. Changing the graph definition changes the results.
## Required Imports
```python
import squidpy as sq
import scanpy as sc
import numpy as np
```
## Graph Construction Decision
Choose the graph type based on data geometry and biological question:
1. **Grid (Visium/HD):** Use `coord_type='grid'` and `n_rings`. Best for fixed lattices. Note: spots are mixtures, not single cells.
2. **kNN (Generic):** Use `coord_type='generic'` and `n_neighs`. Best for single-cell platforms (Xenium/MERFISH) where fixed degree is desired. Note: kNN is asymmetric; consider mutual kNN for conservatism.
3. **Delaunay (Generic):** Use `coord_type='generic'` and `delaunay=True`. Parameter-free contact graph. **Warning:** Prune long edges if tissue has gaps/holes to avoid spurious connections across empty space.
4. **Fixed Radius (Generic):** Use `coord_type='generic'` and `radius`. Best when a physical interaction range exists and density is uniform. **Warning:** Density gradients will bias results (dense regions get more neighbors).
**Sensitivity Analysis:** Always test at least two graph definitions (e.g., k=6 vs k=10, or kNN vs Delaunay) to ensure downstream conclusions are robust to the adjacency definition.
## Implementation
### Build K-Nearest Neighbors Graph
```python
sq.gr.spatial_neighbors(adata, n_neighs=6, coord_type='generic')
```
### Build Delaunay Triangulation Graph
```python
sq.gr.spatial_neighbors(adata, delaunay=True, coord_type='generic')
```
### Radius-Based Neighbors
```python
sq.gr.spatial_neighbors(adata, radius=100, coord_type='generic')
```
### For Visium Data (Grid Structure)
```python
sq.gr.spatial_neighbors(adata, n_rings=1, coord_type='grid')
```
## Accessing and Analyzing the Graph
### Check Graph Properties
```python
conn = adata.obsp['spatial_connectivities']
dist = adata.obsp['spatial_distances']
print(f"Edges: {conn.nnz}")
print(f"Mean neighbors: {conn.nnz / adata.n_obs:.1f}")
```
### Get Neighbors for a Specific Spot
```python
spot_idx = 0
neighbor_indices = conn[spot_idx].nonzero()[1]
neighbor_distances = dist[spot_idx, neighbor_indices].toarray().flatten()
```
### Combine Spatial and Expression Neighbors
```python
# Build both
sq.gr.spatial_neighbors(adata, n_neighs=6, coord_type='generic')
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)
# Weighted combination
from sklearn.preprocessing import normalize
alpha = 0.5
spatial_norm = normalize(adata.obsp['spatial_connectivities'], norm='l1', axis=1)
expr_norm = normalize(adata.obsp['connectivities'], norm='l1', axis=1)
adata.obsp['combined_connectivities'] = alpha * spatial_norm + (1 - alpha) * expr_norm
```
### Visualize Neighbor Graph
```python
import matplotlib.pyplot as plt
coords = adata.obsm['spatial']
rows, cols = conn.nonzero()
fig, ax = plt.subplots(figsize=(10, 10))
for i, j in zip(rows, cols):
if i < j:
ax.plot([coords[i, 0], coords[j, 0]], [coords[i, 1], coords[j, 1]], 'k-', alpha=0.1, linewidth=0.5)
ax.scatter(coords[:, 0], coords[:, 1], s=10, c='blue', alpha=0.5)
ax.set_aspect('equal')
plt.show()
```
### Compute Graph Statistics
```python
import networkx as nx
G = nx.from_scipy_sparse_array(conn)
print(f"Nodes: {G.number_of_nodes()}")
print(f"Edges: {G.number_of_edges()}")
print(f"Connected components: {nx.number_connected_components(G)}")
```