749. 空间域识别SpaGCN/STAGATE¶
一句话概述:利用图神经网络整合基因表达和空间位置信息,自动识别组织中的"功能区域"——就像给一张组织切片自动标注"这里是肿瘤区""这里是免疫浸润区""这里是基质区"。
核心知识点速查表¶
| 概念 | 白话解释 | 关键工具 |
|---|---|---|
| 空间域(Spatial Domain) | 组织中功能相似的区域 | SpaGCN, STAGATE |
| GNN | 图神经网络,在图结构上做机器学习 | PyTorch Geometric |
| SpaGCN | 用图卷积整合表达+空间+组织学图像 | Python |
| STAGATE | 用图注意力网络做空间聚类 | Python |
| 邻居图 | 每个spot与其空间邻居的连接关系 | 空间坐标计算 |
| 组织学图像 | H&E染色的组织形态学图片 | SpaGCN特色 |
一、原理(白话版)¶
1.1 传统聚类 vs 空间聚类¶
传统聚类(Leiden/Louvain):
只看基因表达 → 不考虑空间位置
→ 可能把空间上很远但表达相似的spot聚在一起
→ 聚类结果在空间上可能"碎片化"
空间域识别:
同时看基因表达 + 空间位置 → 空间上相邻且表达相似的spot优先聚在一起
→ 聚类结果在空间上更连续、更有生物学意义
→ 更像真实的组织区域
1.2 SpaGCN vs STAGATE¶
| 特性 | SpaGCN | STAGATE |
|---|---|---|
| 方法 | 图卷积网络(GCN) | 图注意力自编码器(GAT) |
| 输入 | 表达+空间+H&E图像 | 表达+空间 |
| 特色 | 可利用组织学图像 | 注意力机制自动学习权重 |
| 速度 | 较快 | 中等 |
| 适用 | 有H&E图像时最佳 | 通用 |
二、SpaGCN分析流程¶
2.1 安装与运行¶
# ===== 安装SpaGCN =====
# pip install SpaGCN
import SpaGCN as spg # 导入SpaGCN
import scanpy as sc # 导入scanpy
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image # 读取图像
# 读取Visium数据
adata = sc.read_visium("visium_output/") # 读取10x Visium数据
adata.var_names_make_unique()
# 预处理
sc.pp.normalize_total(adata, target_sum=1e4) # 归一化
sc.pp.log1p(adata) # log转换
# 读取H&E组织学图像
img = plt.imread("visium_output/spatial/tissue_hires_image.png")
# 提取空间坐标
x_array = adata.obs["array_row"].values # 行坐标
y_array = adata.obs["array_col"].values # 列坐标
x_pixel = adata.obsm["spatial"][:, 0] # 像素x坐标
y_pixel = adata.obsm["spatial"][:, 1] # 像素y坐标
# ===== 整合组织学图像信息 =====
# SpaGCN可以从H&E图像中提取RGB值作为额外特征
adj = spg.calculate_adj_matrix(
x=x_pixel, # x像素坐标
y=y_pixel, # y像素坐标
x_pixel=x_pixel,
y_pixel=y_pixel,
image=img, # H&E图像
beta=49, # 组织学权重(默认49)
alpha=1, # 空间权重
histology=True # 使用组织学信息
)
# ===== 设定聚类数 =====
# 自动搜索最优分辨率
n_clusters = 7 # 预期的空间域数(可以根据组织学估计)
l = spg.search_l(
p=0.5, # Louvain分辨率搜索参数
adj=adj, # 邻接矩阵
start=0.01, # 搜索起始值
end=1000, # 搜索结束值
tol=0.01 # 容差
)
# ===== 运行SpaGCN =====
clf = spg.SpaGCN() # 创建SpaGCN对象
clf.set_l(l) # 设置l值
# 训练
random_seed = 42
clf.train(
adata,
adj,
num_pcs=50, # PCA维度数
lr=0.05, # 学习率
max_epochs=200, # 最大训练轮数
seed=random_seed
)
# 预测空间域
y_pred, prob = clf.predict() # 预测聚类标签和概率
# 将结果添加到adata
adata.obs["SpaGCN_domain"] = y_pred.astype(str) # 添加空间域标签
# 可选:细化边界(refine)
refined_pred = spg.refine(
sample_id=adata.obs.index,
pred=y_pred,
dis=adj,
shape="hexagon" # Visium是六边形排列
)
adata.obs["SpaGCN_refined"] = refined_pred.astype(str)
# ===== 可视化 =====
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 原始SpaGCN结果
sc.pl.spatial(adata, color="SpaGCN_domain",
title="SpaGCN Domains", ax=axes[0], show=False)
# 细化后的结果
sc.pl.spatial(adata, color="SpaGCN_refined",
title="SpaGCN Refined", ax=axes[1], show=False)
plt.tight_layout()
plt.savefig("spagcn_domains.png", dpi=300)
plt.show()
三、STAGATE分析流程¶
# ===== STAGATE分析 =====
# pip install STAGATE_pyG # PyTorch Geometric版本
import STAGATE_pyG as STAGATE # 导入STAGATE
import scanpy as sc
import torch
# 读取数据
adata = sc.read_visium("visium_output/")
adata.var_names_make_unique()
# 预处理
sc.pp.highly_variable_genes(adata, flavor="seurat_v3", n_top_genes=3000)
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
# ===== 构建空间邻居图 =====
STAGATE.Cal_Spatial_Net(
adata,
rad_cutoff=150 # 邻居距离阈值(像素单位)
# 或用 k_cutoff=6 指定每个spot的邻居数
)
# 查看空间网络统计
print(f"平均邻居数: {adata.uns['Spatial_Net']['Cell1'].value_counts().mean():.1f}")
# ===== 训练STAGATE =====
adata = STAGATE.train_STAGATE(
adata,
alpha=0, # KL散度权重
n_epochs=1000, # 训练轮数
hidden_dims=[512, 30], # 隐藏层维度
random_seed=42
)
# 结果存储在 adata.obsm["STAGATE"]
# 这是STAGATE学习到的潜在表示
# ===== 聚类 =====
sc.pp.neighbors(adata, use_rep="STAGATE") # 用STAGATE表示构建邻居图
sc.tl.umap(adata) # UMAP可视化
# mclust聚类(STAGATE推荐)
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
pandas2ri.activate()
# 在R中运行mclust
ro.r('library(mclust)')
embedding = adata.obsm["STAGATE"]
ro.globalenv['embedding'] = embedding
ro.r('result <- Mclust(embedding, G=7)') # G=预期聚类数
labels = np.array(ro.r('result$classification'))
adata.obs["STAGATE_domain"] = labels.astype(str)
# 或用Leiden替代
sc.tl.leiden(adata, resolution=0.5)
adata.obs["STAGATE_domain"] = adata.obs["leiden"]
# ===== 可视化 =====
sc.pl.spatial(adata, color="STAGATE_domain",
title="STAGATE Spatial Domains")
四、常见报错与解决¶
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
SpaGCN: adj matrix error | 坐标格式不对 | 确保x_pixel/y_pixel是数值数组 |
STAGATE: CUDA error | GPU显存不足 | 用CPU: device=torch.device('cpu') |
Too many/few clusters | 聚类数设置不合适 | 调整n_clusters或resolution |
Spatial_Net: no edges | rad_cutoff太小 | 增大rad_cutoff或用k_cutoff |
mclust: R not found | 未安装R或rpy2 | 改用Leiden聚类替代 |
Image not found | H&E图像路径错误 | 检查spatial/目录结构 |
五、面试高频问题¶
Q1: 空间域识别和普通聚类有什么本质区别?¶
A: 普通聚类只考虑基因表达相似性,空间域识别同时考虑基因表达和空间位置。实际效果:空间域在组织切片上更连续,更接近真实的组织解剖学区域。
Q2: SpaGCN如何利用H&E图像?¶
A: 从H&E图像中提取每个spot位置的RGB值作为额外特征。这样空间邻接矩阵不仅考虑物理距离,还考虑组织形态学相似性。组织学外观相似的区域会被更强地连接。
Q3: 如何确定最佳聚类数?¶
A: ①参考组织学解剖结构(如大脑皮层有6层);②尝试多个聚类数,用ARI对比参考注释;③使用mclust的BIC准则自动选择;④结合生物学知识判断。
六、速查表¶
# ===== 空间域识别速查 =====
# SpaGCN
pip install SpaGCN
adj = spg.calculate_adj_matrix(x, y, image=img, histology=True)
clf = spg.SpaGCN()
clf.train(adata, adj, num_pcs=50)
y_pred, prob = clf.predict()
# STAGATE
pip install STAGATE_pyG
STAGATE.Cal_Spatial_Net(adata, rad_cutoff=150)
adata = STAGATE.train_STAGATE(adata, n_epochs=1000)
sc.pp.neighbors(adata, use_rep="STAGATE")
sc.tl.leiden(adata, resolution=0.5)
# 方法选择:
# 有H&E图像 → SpaGCN(利用组织学信息)
# 无H&E图像 → STAGATE(纯表达+空间)
# 追求速度 → SpaGCN
# 追求灵活性 → STAGATE