SpaTalk
#R #CellCellCommunication #SpaTalk
loadData() → createSpaTalk() → dec_celltype() → find_lr_path() → dec_cci() / dec_cci_all()
- loadData()
- createSpaTalk()
- dec_celltype()
- find_lr_path()
- dec_cci() / dec_cci_all()
Load Data
SpaTalk 不提供文件读盘函数 。需要在 R 里把数据加载进来,再传给它的接口。
- gene × spot/cell
- data structure: data.frame / matrix / dgCMatrix
r
# 你的空间转录组数据
st_data <- ... # gene × spot/cell 矩阵(data.frame / matrix / dgCMatrix)
st_meta <- ... # spot-based: data.frame(spot, x, y)
# single-cell: data.frame(cell, x, y)
# scRNA-seq 参考(解卷积用,选传)
sc_data <- ... # gene × cell 矩阵
sc_celltype <- ... # 每个 cell 的 cell-type 标签向量createSpaTalk()
r
obj <- createSpaTalk(
st_data = st_data,
st_meta = st_meta,
species = "Human", # or "Mouse"
if_st_is_sc = FALSE, # TRUE=ST, FALSE=spot ST
spot_max_cell = 10, # spot 模式:每个 spot 最多重建 N 个细胞
celltype = NULL # 已知 celltype 时跳过解卷积
)Two Branch
- if_st_is_sc = TRUE → st_type = "single-cell",后续直接对原始细胞做 CCC
- if_st_is_sc = FALSE → st_type = "spot",必须先解卷积 + 重建伪单细胞
dec_celltype()
- dec_celltype() 在 celltype = NULL 时强制需要调用,无论 ST 数据是单细胞分辨率还是 spot 分辨率。
- 单细胞 ST + celltype = NULL → 做 非负回归,给每个细胞分配细胞类型(不是严格意义上的"解卷积")
- Spot ST + celltype = NULL → 做 真正的解卷积 + 伪单细胞重建
- 若 celltype 已知 → 两步都跳过,直接进入 find_lr_path()
r
obj <- dec_celltype(
object = obj,
sc_data = sc_data,
sc_celltype = sc_celltype,
min_percent = 0.5, # 单细胞:>此比例才赋予type
iter_num = 1000, # spot:抽样轮数
method = 1, # 1=SpaTalk内置, 2=RCTD, 3=Seurat...
)内部发生了什么:
- 所有模式:先对 scRNA 参考按 cell type 求均值 → NNLM::nnlm() 非负回归 → 得到每个 spot/cell 的 cell-type 比例
- 单细胞 ST:归一化比例 > 0.5 → 赋予 cell type;否则 "unsure" 排除
- Spot ST:比例 × spot_max_cell → 整数分配 + 小数抽签 → iter_num 轮从 scRNA 参考中抽样 → 选与真实 spot 表达最像的组合 → 伪细胞的表达来自参考细胞
find_lr_path()
过滤可用 LR
r
obj <- find_lr_path(
object = obj,
lrpairs = lrpairs, # 内置 CellTalkDB,也可自定义
pathways = pathways, # 内置 KEGG + Reactome + AnimalTFDB
max_hop = NULL # Human=3, Mouse=4
)只保留:
- 物种匹配
- 配体/受体都在表达矩阵中
- receptor 能在 max_hop 步内连接到 TF
这步不涉及空间,是纯知识图谱预过滤。
dec_cci() / dec_cci_all()
推断 CCC
对 sender → receiver 这个 cell-type pair:
r
# 一对指定类型
obj <- dec_cci(obj, celltype_sender = "T_cell", celltype_receiver =
"Macrophage")
# 全部 cell-type pair(自动枚举)
obj <- dec_cci_all(obj)内部 5 个子步骤:
- 空间 KNN:对每个 sender 细胞找最近 10 个细胞 → 筛出 receiver cell type
- LR 共表达:sender ligand × receiver receptor 二值共表达比例
- 置换检验:有放回抽样 1000 次 → 经验 p 值
- 胞内 KG:receptor → TF → target,仅在 receiver 细胞中算共表达比例过滤
- 随机游走:从 receptor 走 10000 次 → TF 命中概率 → intra 分数
- 合并分数:
python
dec_cci(sender, receiver)
│
├── 1. 取数据:st_meta, st_data, celltype_dist(距离矩阵)
├── 2. 验证 cell type 是否存在
├── 3. .get_cellpair() ──→ 空间 KNN,选出 sender→receiver 的 cell pair
├── 4. 检查 cell pair 数量是否 ≥ min_pairs
│
├── 5. .lr_distance() ──→ LR 共表达比例 + 置换检验
│ 保留 p < 0.05 的 LR
│
├── 6. .get_tf_res() ──→ 对每个显著 receptor 做胞内通路
│ ├─ .generate_ggi_res() BFS 展开 receptor→TF
│ ├─ .generate_tf_gene_all() 找可达 TF
│ ├─ .generate_tf_res() 记录 n_hop, n_target
│ └─ .random_walk() 10000 次随机游走 → TF score
│
├── 7. .get_score() ──→ 合并分数
│ score = sqrt((1-p) × sigmoid(intra))
│
└── 8. 写回 object@lrpair, object@tf, object@cellpair##SpaTalk 方法详解
核心 Pipeline
读取数据 → createSpaTalk() → dec_celltype() → find_lr_path() → dec_cci() / dec_cci_all()1. 输入
空间转录组数据
| 参数 | 结构 | 说明 |
|---|---|---|
st_data | gene × spot/cell 矩阵 | data.frame / matrix / dgCMatrix |
st_meta | data.frame | 单细胞:cell, x, y;spot:spot, x, y |
species | "Human" / "Mouse" | 用于筛选 LR 数据库 |
if_st_is_sc | logical | 是否单细胞分辨率 |
spot_max_cell | integer | 每个 spot 最多重建细胞数 |
celltype | character (可选) | 已知 cell type 时可跳过解卷积 |
scRNA-seq 参考(解卷积用)
r
dec_celltype(object, sc_data, sc_celltype)sc_data:gene × reference-cell countssc_celltype:每个 reference cell 的 cell-type 标签- ST 和 scRNA 至少需要 2 个共同基因
先验知识
r
find_lr_path(object, lrpairs, pathways)lrpairs:ligand, receptor, species三列pathways:src, dest, pathway, source, type, src_tf, dest_tf, species八列- 默认来源:CellTalkDB + KEGG/Reactome + AnimalTFDB
2. 解卷积:dec_celltype()
输入输出
输入:ST counts + scRNA 参考
↓
Seurat LogNormalize(默认)
↓
对每个 cell type 求 scRNA 均值 → 参考谱 X (gene × cell-type)
↓
NNLM::nnlm(X, Y, method="lee", loss="mkl")
Y = Xβ + ε, β ≥ 0
↓
β → 归一化为 cell-type 比例
↓
输出:每个 spot/cell 的 cell-type 比例分支一:单细胞 ST
- 归一化比例 > 0.5 → 赋予该 cell type
- 否则 →
"unsure"(后续排除)
分支二:Spot ST → 伪单细胞重建
比例 × spot_max_cell
↓
整数部分 → 直接分配细胞数
小数部分 > 0.5 → 再加一个细胞
↓
对每个伪细胞,从对应 type 的 scRNA 参考中随机抽一个真实细胞
↓
重复抽 1000 次,选 sum 表达与真实 spot 相关性最高的组合
↓
newdata <- sc_ndata[, best_cell_ids] ← 表达来自参考细胞关键:伪细胞的表达来自参考 scRNA 细胞,不是拆分原始 spot 的 counts。
3. LR 预过滤:find_lr_path()
lrpairs → 物种匹配 → ligand/receptor 在表达矩阵中
↓
pathways → 物种匹配 → src/dest 在表达矩阵中
↓
receptor 必须在 KEGG/Reactome 中有出边
↓
对每个 receptor 做 BFS(max_hop: Human=3, Mouse=4)
↓
检查是否有可达的 TF(src_tf == "YES" 或 dest_tf == "YES")
↓
保留通过过滤的 LR 对不涉及空间,纯知识图谱预过滤。
4. CCC 推断:dec_cci() / dec_cci_all()
步骤 1:空间 KNN — .get_cellpair()
对每个 sender 细胞:
在全部细胞中找最近 10 个
再筛选出属于 receiver cell type 的细节:先全体 KNN 再筛 type,不是"为 sender 找最近的 receiver"。
步骤 2:LR 共表达 — .lr_distance()
对每个 LR pair:
真实共表达比例:
ligand(sender) × receptor(receiver) 逐对相乘
统计乘积 > 0 的比例 ← 二值:只看是否表达,不看幅度
置换检验(1000 次):
从所有细胞中有放回抽样,重新算共表达比例
经验 p = (#null ≥ #obs) / 1000 ← 无 +1 修正
保留 p < 0.05 的 LR论文-代码差异:论文说是"打乱 cell labels 保持空间图",代码实际上是有放回独立抽样。
步骤 3:胞内通路 + 随机游走 — .get_tf_res()
对每个显著 LR 的 receptor:
① BFS 展开受体下游子图(max_hop 步)
② 对每条边,在 receiver 细胞中算共表达比例
只保留 co_ratio > 0.1 的边
③ 找同时满足条件的 TF:
- dest_tf == "YES"
- 还有下游靶基因
④ 记录每个 TF 的 n_hop 和 n_target
⑤ 随机游走:
从 receptor 出发,走 10000 次,每次最多 10 步
在分叉口等概率选择出边 ← 不是按 co_ratio 加权
记录每个 TF 被命中的频率 p_k论文-代码差异:论文说 KG 边权是 co-expression coefficient,代码中 co_ratio 只用于硬阈值过滤,随机游走是均匀抽样。
步骤 4:合并分数 — .get_score()
score_lr = 1 - p ← inter
T_intra = sum(θ_k × p_k / η_k) × (-1)
score_rt = 1 / (1 + exp(T_intra)) ← intra (sigmoid)
score = sqrt(score_lr × score_rt) ← 几何均值| 符号 | 含义 | 代码 |
|---|---|---|
| inter-cellular score | score_lr | |
| TF k 的 target 数 | n_target | |
| 随机游走命中概率 | random_walk_score | |
| receptor→TF 步数 | n_hop | |
| intra-cellular score | score_rt | |
| 最终分数 | score |
5. 输出
object@lrpair 的一行:
celltype_sender × celltype_receiver × ligand × receptor
↓
lr_co_exp_num 共表达 cell pair 数
lr_co_ratio 共表达比例
lr_co_ratio_pvalue 置换 p 值
score 综合分数 (inter × intra)object@tf 的一行:
sender × receiver × receptor × TF
↓
n_hop receptor→TF 步数
n_target TF 下游 target 数
score 随机游走命中概率6. 分支总结
┌─ known celltype → 跳过解卷积,直接 KNN + CCC
│
┌─ single-cell ST ───────┤
│ └─ unknown → NNLM 解卷积 → 赋予 type → KNN + CCC
│
──┤
│ ┌─ NNLM 解卷积 → 比例 → iter_num 抽样
│ │
└─ spot ST ──────────────┤→ 伪单细胞重建(坐标偏移 + 参考细胞表达)
│
└─ 后续 KNN + CCC(同单细胞流程)| 判断 | 结论 | 证据位置 |
|---|---|---|
| 是否真正使用空间信息 | 是:坐标用于伪细胞定位 + KNN | utils.R:762(.get_cellpair) |
| 是否直接在 spot 上推断 | 默认不是:spot 先重建为伪单细胞 | methods.R:384(newdata <- sc_ndata[...]) |
| CCC 基础统计量 | 空间近邻 cell pair 中 LR 的二值共表达比例 | utils.R:784(.co_exp:x_12 > 0) |
| 是否使用表达强度 | LR 检验不使用,只问是否 > 0 | 同上 |
| 是否考虑下游信号 | 是:receiver 内 receptor→TF→target KG + 随机游走 | utils.R:1062-1270(.get_tf_res) |
| 最终输出粒度 | cell-type pair × LR,非单个 cell pair | methods.R:650-660 |
| 显著性 | 原始经验 p 值,无多重检验校正 | utils.R:830(.co_exp_p) |
| 论文与代码完全一致? | 不一致:置换方式(打标签 vs 有放回抽样)+ KG 边权使用 | utils.R:817 vs 论文 Methods |
| 是否属于深度学习 | 否:NNLM + KNN + 置换检验 + 随机游走 | 整个代码库 |