Skip to content
PostResearch / BioInfo

SpaTalk

2026-09-14
Back to Blog

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 个子步骤:

  1. 空间 KNN:对每个 sender 细胞找最近 10 个细胞 → 筛出 receiver cell type
  2. LR 共表达:sender ligand × receiver receptor 二值共表达比例
  3. 置换检验:有放回抽样 1000 次 → 经验 p 值
  4. 胞内 KG:receptor → TF → target,仅在 receiver 细胞中算共表达比例过滤
  5. 随机游走:从 receptor 走 10000 次 → TF 命中概率 → intra 分数
  6. 合并分数:score=sqrt((1p)×sigmoid(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.05LR

     ├── 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_datagene × spot/cell 矩阵data.frame / matrix / dgCMatrix
st_metadata.frame单细胞:cell, x, y;spot:spot, x, y
species"Human" / "Mouse"用于筛选 LR 数据库
if_st_is_sclogical是否单细胞分辨率
spot_max_cellinteger每个 spot 最多重建细胞数
celltypecharacter (可选)已知 cell type 时可跳过解卷积

scRNA-seq 参考(解卷积用)

r
dec_celltype(object, sc_data, sc_celltype)
  • sc_data:gene × reference-cell counts
  • sc_celltype:每个 reference cell 的 cell-type 标签
  • ST 和 scRNA 至少需要 2 个共同基因

先验知识

r
find_lr_path(object, lrpairs, pathways)
  • lrpairsligand, receptor, species 三列
  • pathwayssrc, 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)       ← 几何均值
符号含义代码
1pinter-cellular scorescore_lr
θkTF k 的 target 数n_target
pk随机游走命中概率random_walk_score
ηkreceptor→TF 步数n_hop
Sintraintra-cellular scorescore_rt
S最终分数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(同单细胞流程)

判断结论证据位置
是否真正使用空间信息:坐标用于伪细胞定位 + KNNutils.R:762.get_cellpair
是否直接在 spot 上推断默认不是:spot 先重建为伪单细胞methods.R:384newdata <- sc_ndata[...]
CCC 基础统计量空间近邻 cell pair 中 LR 的二值共表达比例utils.R:784.co_expx_12 > 0
是否使用表达强度LR 检验不使用,只问是否 > 0同上
是否考虑下游信号:receiver 内 receptor→TF→target KG + 随机游走utils.R:1062-1270.get_tf_res
最终输出粒度cell-type pair × LR,非单个 cell pairmethods.R:650-660
显著性原始经验 p 值,无多重检验校正utils.R:830.co_exp_p
论文与代码完全一致?不一致:置换方式(打标签 vs 有放回抽样)+ KG 边权使用utils.R:817 vs 论文 Methods
是否属于深度学习:NNLM + KNN + 置换检验 + 随机游走整个代码库