# 第 12 章 生态位内细胞通讯分析

> 本章对应脚本：`code/07_niche_cellchat.R`。
> 前置：第 10 章的生态位列（`niche`）、第 11 章的 CellChat 流程，以及运行完第 11 章后的全局 CellChat 对象。

上一章我们把整张切片当成一个整体，分析了“全局通讯网络”。但肿瘤微环境在空间上远非均质：同一个切片上，侵袭前沿与肿瘤核心的细胞组成、缺氧程度、免疫状态天差地别。如果只看全局网络，这些空间差异会被“平均”掉。本章的核心操作只有一句话：**把数据按生态位拆开，每个生态位独立跑一遍 CellChat，得到“生态位特异的通讯程序”（niche-specific communication program）**。这一步看似简单，却是全书创新点（以生态位视角替代全局视角）的操作化起点。

## 本章目标

1. 理解“全局通讯平均掉空间异质性”的机制，以及分区分析为什么能发现新信号
2. 学会用 Seurat `subset` 按生态位拆分数据，并把第 11 章流程封装成可复用函数
3. 掌握生态位内通讯特征的三类解读示例（T-B 互作、肿瘤-CAF、血管生成）
4. 会用表格整理各生态位的“主要发送者/接收者/通路”
5. 完成侵袭前沿生态位的完整 CellChat 实战练习

## 1 核心思路：为什么要按生态位拆开分析

### 1.1 全局分析如何“平均掉”空间异质性

设想真实的生物学场景：TGF-β 通讯只发生在侵袭前沿的肿瘤细胞与 CAF 交界处，而肿瘤核心和免疫浸润区几乎没有。全局分析会把整张切片的表达混在一起：TGFB1 的平均表达被“稀释”，通讯概率被压低，甚至低于显著性阈值——于是这个真实存在的、空间特异的信号在全局网络里“消失”了。反过来，某种在全局看起来很强的信号，可能只是某个局部区域的贡献，全局图却会让人误以为“处处都强”。一句话：**全局网络是“平均人”，生态位网络才是“当事人”**。用一个数字例子帮助理解：假设侵袭前沿的 TGFB1 平均表达为 8、其余区域的为 1，全局分析会把它们混在一起求平均（约 3），通讯概率随之被压低；分区分析直接用 8 计算前沿的通讯概率，信号几乎不受其他区域稀释。同理，某条通路可能因稀释而跌破显著性阈值，从“显著”变成“不显著”——这正是“平均掉”的含义。

### 1.2 生态位特异的通讯程序

把数据按生态位拆开后，每个生态位内部的通讯网络反映的是“这个微环境独有的对话模式”，我们称之为生态位特异的通讯程序。图 12-1（模拟数据演示）显示，各生态位的通讯数量与总强度差异明显：侵袭前沿通讯最多最强，免疫浸润区次之，肿瘤核心与基质区规模较小。这种差异本身就是生态位生物学（缺氧、EMT、免疫应答）在通讯层面的投影——也就是说，**生态位不仅细胞组成不同，连“对话方式”都不同**。

![图 12-1 各生态位通讯「数量」与「强度」比较（模拟数据演示，compareInteractions 风格）。柱高分别代表显著通讯数量与总通讯强度，可见不同生态位的通讯规模差异明显，侵袭前沿最强。](figures/fig13_comm_barplot.png){width=85%}

## 2 实操：按生态位拆分并独立建 CellChat

### 2.1 准备：确认生态位列

假设 `obj` 的 meta.data 里有第 10 章生成的 `niche` 列，取值为：肿瘤核心、侵袭前沿、免疫浸润区、基质区。

```r
table(obj$niche)   # 看看每个生态位有多少个 spot
```

运行结果解读：如果某个生态位的 spot 数很少（比如少于 100），后面的 `min.cells` 就要相应调小，而且结论要更谨慎——细胞群太小，通讯推断的统计功效不足。若某个生态位的细胞类型数过少（比如只剩 1–2 类），通讯网络会退化成单点互作，此时建议把该生态位与相邻生态位合并后重跑，并在方法部分如实说明。

### 2.2 把流程封装成函数

第 11 章的 7 步流程在这里完全一样，只是输入对象不同。我们把它封装成函数，避免复制粘贴出错，也为第 13 章批量比较打好基础：

```r
# 输入：一个生态位的 Seurat 子集；输出：该生态位的 CellChat 对象
run_niche_cellchat <- function(obj_sub, group.by = "celltype", min.cells = 10) {
  cellchat <- createCellChat(object = obj_sub, group.by = group.by)
  cellchat@DB <- CellChatDB.human
  cellchat <- subsetData(cellchat)
  cellchat <- identifyOverExpressedGenes(cellchat)
  cellchat <- identifyOverExpressedInteractions(cellchat)
  cellchat <- computeCommunProb(cellchat, type = "triMean")
  cellchat <- filterCommunication(cellchat, min.cells = min.cells)
  cellchat <- computeCommunProbPathway(cellchat)
  cellchat <- aggregateNet(cellchat)
  return(cellchat)
}
```

### 2.3 按生态位循环运行

```r
library(Seurat); library(CellChat)
niches <- unique(obj$niche)
cc_list <- list()                            # 用列表装每个生态位的结果
for (n in niches) {
  message("正在分析生态位：", n)
  obj_sub <- subset(obj, subset = niche == n)   # 只保留该生态位的 spot
  cc_list[[n]] <- run_niche_cellchat(obj_sub, min.cells = 10)
}
saveRDS(cc_list, file = "results/cc_list_by_niche.rds")   # 保存，第 13 章继续用
```

运行结果解读：循环会逐个生态位打印分析进度。若某生态位报“细胞类型太少”类错误，把该生态位的 `min.cells` 调低（如 5）重试；若某生态位只剩一两种细胞类型，通讯网络会非常稀疏——这本身就是一个发现（该生态位细胞组成单一），但要如实报告，不要强行解读。

### 2.4 一个常见的小坑

`subset(obj, subset = niche == n)` 要求 meta.data 里确实存在 `niche` 列。如果列名与 Seurat 内部槽位冲突，`subset` 会报错，建议用 `obj$niche <- ...` 显式赋值，并检查 `colnames(obj@meta.data)` 确认列名无误。

### 2.5 每个生态位的网络可视化

分区分析的一个额外好处是“图更好讲”：把 4 个生态位的 `netVisual_circle` 分别保存后并排拼接（如图 11-1 的三联图），读者一眼就能看出“不同生态位连的边都不一样”。拼图时注意统一边的粗细比例尺，否则两张图之间无法比较粗细；同时为每个面板标注生态位名称与最强的 L-R 对，图注里写明“模拟数据演示”。

```r
# 每个生态位分别保存网络图，再在文稿中并排拼接
# （netVisual_circle 输出基础图形，不能用 patchwork 直接组合）
for (n in names(cc_list)) {
  png(paste0("figures/comm_network_", n, ".png"),
      width = 6, height = 6, units = "in", res = 300)
  netVisual_circle(cc_list[[n]], title.name = n)
  dev.off()
}
```

## 3 生态位内通讯特征解读示例

拿到 4 个生态位的网络后，怎么讲出生物学故事？建议按“三步走”：第一步看网络规模（图 11-1 中哪张图边最多、最粗）；第二步看主要细胞对（谁和谁在对话）；第三步看最强通路（对话的内容是什么）。下面结合文献与本书模拟数据（见图 11-1），给出三个典型模式的解读示例——它们是全书模拟数据的设计蓝本，你在真实数据上看到的不一定完全一样，但解读方法相同：

- **免疫浸润区：T-B 互作**。免疫浸润区富集 T 细胞与 B 细胞，典型信号是趋化因子回路，如 CXCL13-CXCR5（B 细胞向 T 细胞区域趋化）与 CCL19/CCL21-CCR7。这类 T-B 互作与“免疫 hub”（immune hub）概念一致：结直肠癌研究中，B 细胞、滤泡辅助 T 细胞与巨噬细胞在空间上聚集形成 hub，并与良好预后相关 [Pelka 2021]。读图时如果看到 T→B 的 CXCL 边，可以写“提示该生态位存在组织化的免疫应答结构”。
- **侵袭前沿：肿瘤-CAF 互作**。侵袭前沿是肿瘤细胞与基质细胞（尤其 CAF）的交界，EMT 特征显著（VIM/ZEB1/TGF-β 高）。典型信号是 TGFb 通路（TGFB1-TGFBR1/TGFBR2）：CAF 分泌 TGF-β 促进肿瘤细胞 EMT 与侵袭，肿瘤细胞反过来也能激活 CAF。读图时注意“谁发谁收”：若 TGFb 边以 CAF→肿瘤为主，就是“基质驱动”的故事。
- **肿瘤核心：血管生成**。肿瘤核心缺氧，VEGF 通路（VEGFA-KDR/FLT1）活跃：肿瘤细胞分泌 VEGFA，内皮细胞表达受体 KDR。读图时看到“肿瘤细胞→内皮”的 VEGF 边，提示该生态位正在招募血管。

以上解读遵循同一句式：**信号 → 细胞对 → 生物学含义 →“这提示……”**。记住：网络图只告诉你“谁在跟谁说什么”，生物学含义是读者（你）补上的解释，需要文献与后续验证支撑（第 13 章会给出验证方案）。

## 4 生态位内 vs 全局通讯：为什么分区能发现新信号

| 对比维度 | 全局通讯分析 | 生态位内通讯分析 |
| --- | --- | --- |
| 分析对象 | 整张切片所有 spot | 单个生态位内的 spot |
| 空间信息 | 忽略（或仅作辅助） | 隐含（生态位本身即空间单元） |
| 对局部信号 | 被平均稀释，可能低于阈值而丢失 | 保留局部强度，容易被发现 |
| 对全局信号 | 可能把局部强信号误读为全局普遍 | 明确信号的“主场”在哪 |
| 生物学解释 | “整个组织在对话” | “这个微环境在对话” |
| 典型风险 | 空间特异信号消失或错位 | 跨生态位长程信号被截断 |

举例：全局分析中 TGF-β 通路可能完全不显著（被稀释），而分区分析发现它在侵袭前沿是排名第一的通路——这就是“分区分析能看到全局看不到的信号”的典型场景。当然，分区也有代价：跨生态位的长程信号（如免疫浸润区分泌的细胞因子作用于肿瘤核心）会被截断。另一个典型场景是免疫浸润区的 CXCL 信号：全局分析中趋化因子通讯可能被大片肿瘤区域的噪声淹没，分区分析则能干净地看到 T 细胞→B 细胞的 CXCL13-CXCR5 边。所以分区分析与全局分析是**互补关系，不是替代关系**：全局网络负责“有没有”，生态位网络负责“在哪、多强”。第 13 章的差异分析正是建立在这两层之上。

## 5 结果整理：各生态位通讯网络汇总表

把 4 个生态位浓缩成一张表，是“从结果到故事”的第一步：

```r
# 汇总每个生态位的"主要发送者/主要接收者/主要通路"
for (n in niches) {
  cc <- cc_list[[n]]
  w <- cc@net$weight                        # 群 × 群的通讯强度矩阵
  sender   <- rowSums(w)                    # 行和 = 该群作为发送者的总强度
  receiver <- colSums(w)                    # 列和 = 该群作为接收者的总强度
  top_sender   <- names(sort(sender,   decreasing = TRUE))[1]
  top_receiver <- names(sort(receiver, decreasing = TRUE))[1]
  pw <- apply(cc@netP$prob, 3, sum)         # 每个通路的全网络总概率
  top_pathways <- names(sort(pw, decreasing = TRUE))[1:3]
  cat("生态位:", n, "\n",
      "  主要发送者:", top_sender, "\n",
      "  主要接收者:", top_receiver, "\n",
      "  主要通路:", paste(top_pathways, collapse = ", "), "\n\n")
}
```

运行结果解读：把打印内容整理成下面的表格（数值为模拟数据演示示例）：

| 生态位 | 主要发送者 | 主要接收者 | 主要通路 |
| --- | --- | --- | --- |
| 侵袭前沿 | 肿瘤细胞、CAF | CAF、肿瘤细胞 | TGFb、MIF、ECM |
| 免疫浸润区 | T 细胞、巨噬细胞 | B 细胞、T 细胞 | CXCL、CCL、IL10 |
| 肿瘤核心 | 肿瘤细胞 | 内皮细胞、肿瘤细胞 | VEGF、ECM |
| 基质区 | CAF | CAF、内皮细胞 | ECM、TGFb |

这张表就是第 13 章“通讯重编程”叙事的骨架：生态位之间的差异已经跃然纸上——同样是“肿瘤细胞说话”，在侵袭前沿说的是 TGFb，在肿瘤核心说的是 VEGF；同样是“接收者”，免疫浸润区听的是趋化因子，基质区听的是 ECM 信号。

这张汇总表的每一列都有讲究：主要发送者回答“谁在主导对话”，主要接收者回答“谁在被影响”，主要通路回答“对话内容是什么”。三列组合起来，就能给每个生态位起一个“人设”：侵袭前沿是“基质驱动的 TGF-β 放大器”，免疫浸润区是“T-B 趋化网络”，肿瘤核心是“血管生成发动机”。给生态位起人设不是卖弄文笔，而是逼迫自己把数据读透——如果人设与生物学先验矛盾，往往意味着注释或参数出了问题。

## 本章小结

本章完成了从“全局通讯”到“生态位通讯”的方法学跃迁：把数据按生态位拆开、独立构建 CellChat、封装函数批量运行、用表格汇总每个生态位的通讯特征。核心收获是理解“平均化陷阱”——全局分析会稀释空间特异信号，而分区分析让每个微环境的通讯程序显形。下一章将正式比较生态位之间的差异，把“差异”升级为“重编程”。另外提醒：本章的通讯规模汇总（数量/强度）只是重编程分析的第一个层面，第 13 章还会叠加通路活性与差异配体-受体对比较——三个层面合起来才是完整的“通讯指纹”。

## 练习与思考

1. 用一句话解释：为什么全局通讯分析会“平均掉”空间异质性？分区分析能发现什么样的全局分析看不到的信号？
2. （动手题）对本书乳腺癌数据跑侵袭前沿生态位的完整 CellChat 流程（第 2 节代码），找出它的主要发送者、主要接收者与排名前三的通路，并写一段 3 句话的解读。
3. （动手题）把 `min.cells` 从 10 改为 3，重新运行免疫浸润区生态位，比较两次结果的通讯数量差异，思考参数对结论的影响。
4. （思考题）分区分析会截断跨生态位的长程信号。如果某细胞因子由免疫浸润区分泌、作用于肿瘤核心，你会怎么设计分析来捕获它？
