domains / science / spatial-cell-cell-communication / spatial-communication
The two skills cover the same task (spatial cell-cell communication) but with different depths and emphases. The first copy (OpenClaw) provides a complete, executable Python workflow using Squidpy, including code for filtering, visualization (heatmaps, networks), and custom databases. The second copy (GPTomics) provides a critical methodological framework, emphasizing the limitations of co-expression vs. signaling, the impact of segmentation spillover, and a decision table for choosing between space-blind (Squidpy) and distance-aware (COMMOT) methods. The first copy lacks the critical caveats and method selection logic of the second, while the second copy lacks the detailed implementation code for visualization and custom analysis found in the first. Combining them yields a skill that is both methodologically rigorous and practically executable.
| Repository | Path |
|---|---|
| FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-communication/SKILL.md |
| GPTomics/bioSkills | spatial-transcriptomics/spatial-communication/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | FreedomIntelligence/OpenClaw-Medical-Skills | skills/spatial-transcriptomics-analysis/bioSkills/spatial-communication/SKILL.md |
| peer | GPTomics/bioSkills | spatial-transcriptomics/spatial-communication/SKILL.md |
---
name: bio-spatial-transcriptomics-spatial-communication
description: Analyze cell-cell communication in spatial transcriptomics using Squidpy and COMMOT. Includes method selection based on spatial modeling, critical interpretation of co-expression vs. signaling, and full Python workflows for visualization.
tool_type: python
primary_tool: squidpy
---
# Spatial Cell-Cell Communication
## 1. Method Selection & Critical Interpretation
Before running analysis, select the appropriate method based on spatial modeling needs:
| Method | Spatial Mechanism | Best When | Caution |
|--------|-------------------|-----------|---------|
| `squidpy.gr.ligrec` | None (Space-blind) | Fast baseline, large datasets | Does not model distance; permutes cluster labels. |
| `commot.tl.spatial_communication` | Optimal Transport (Distance-aware) | When spatial proximity is critical | Slower; requires distance matrix. |
**Governing Principle:**
- **Co-expression is not communication:** Results are hypotheses of ligand-receptor co-expression, not validated signaling.
- **Spillover Artifact:** In imaging data, segmentation spillover can fabricate short-range hits. Validate segmentation quality.
- **Database Impact:** The choice of L-R database (CellPhoneDB, CellChatDB) drives results as much as the algorithm. Report the database version.
## 2. Required Imports
```python
import squidpy as sq
import scanpy as sc
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
# import commot as cm # If using distance-aware analysis
```
## 3. Ligand-Receptor Analysis (Squidpy)
```python
# Load data
adata = sc.read_h5ad('clustered_spatial.h5ad')
# Build spatial neighbors
sq.gr.spatial_neighbors(adata, coord_type='generic', n_neighs=6)
# Run ligand-receptor analysis
sq.gr.ligrec(
adata,
cluster_key='cell_type',
n_perms=100,
threshold=0.01,
copy=False,
)
```
## 4. Access and Filter Results
```python
ligrec_results = adata.uns['cell_type_ligrec']
means = ligrec_results['means']
pvalues = ligrec_results['pvalues']
# Filter significant interactions
pval_threshold = 0.05
interactions = []
for source_target in pvalues.index:
for lr_pair in pvalues.columns:
pval = pvalues.loc[source_target, lr_pair]
mean_expr = means.loc[source_target, lr_pair]
if pval < pval_threshold and not np.isnan(mean_expr):
source, target = source_target
ligand, receptor = lr_pair
interactions.append({
'source': source,
'target': target,
'ligand': ligand,
'receptor': receptor,
'mean': mean_expr,
'pvalue': pval,
})
interactions_df = pd.DataFrame(interactions)
```
## 5. Visualization
### Dot Plot
```python
sq.pl.ligrec(
adata,
cluster_key='cell_type',
source_groups=['Macrophage', 'T_cell'],
target_groups=['Epithelial', 'Fibroblast'],
pvalue_threshold=0.05,
remove_empty_interactions=True,
)
```
### Interaction Heatmap
```python
def count_interactions_per_pair(pvalues, threshold=0.05):
counts = {}
for source_target in pvalues.index:
sig_count = (pvalues.loc[source_target] < threshold).sum()
counts[source_target] = sig_count
return counts
counts = count_interactions_per_pair(pvalues)
cell_types = adata.obs['cell_type'].unique()
count_matrix = pd.DataFrame(0, index=cell_types, columns=cell_types)
for (source, target), count in counts.items():
count_matrix.loc[source, target] = count
plt.figure(figsize=(8, 8))
plt.imshow(count_matrix.values, cmap='Reds')
plt.xticks(range(len(cell_types)), cell_types, rotation=45, ha='right')
plt.yticks(range(len(cell_types)), cell_types)
plt.colorbar(label='Number of significant interactions')
plt.title('Cell-cell communication strength')
plt.tight_layout()
plt.savefig('interaction_heatmap.png', dpi=150)
```
### Network Visualization
```python
G = nx.DiGraph()
for ct in adata.obs['cell_type'].unique():
G.add_node(ct)
for _, row in interactions_df.iterrows():
if G.has_edge(row['source'], row['target']):
G[row['source']][row['target']]['weight'] += 1
else:
G.add_edge(row['source'], row['target'], weight=1)
pos = nx.spring_layout(G, k=2, seed=42)
weights = [G[u][v]['weight'] for u, v in G.edges()]
plt.figure(figsize=(10, 10))
nx.draw_networkx_nodes(G, pos, node_size=1000, node_color='lightblue')
nx.draw_networkx_labels(G, pos, font_size=10)
nx.draw_networkx_edges(G, pos, width=[w/max(weights)*5 for w in weights],
edge_color='gray', arrows=True, arrowsize=20)
plt.title('Cell-cell communication network')
plt.axis('off')
plt.savefig('communication_network.png', dpi=150)
```
## 6. Custom Ligand-Receptor Database
```python
custom_pairs = pd.DataFrame({
'ligand': ['GENE1', 'GENE2'],
'receptor': ['GENE3', 'GENE4'],
})
sq.gr.ligrec(
adata,
cluster_key='cell_type',
interactions=custom_pairs,
n_perms=100,
)
```