4、TMB阈值设定与临床分型归纳总结
TMB 阈值到底怎么定?说实话,这个问题我每年都要被问很多次。不同癌种、不同平台、不同检测试剂盒,出来的阈值可能完全不一样。你如果直接拿别人的cutoff用在自己数据上,很容易出问题。今天我把这些年踩过的坑、总结的经验,梳理清楚。
4.1 阈值设定的底层逻辑
TMB 阈值本质上是一个二分或三分界点。我们根据某个数值把患者分成“高 TMB”和“低 TMB”两组,然后看哪一组对免疫治疗更敏感。但问题来了:这个“高”到底多高才算高?
我个人习惯这样做:先拿到一个足够大的参考人群(至少几百例),计算他们的 TMB 分布。然后看免疫治疗临床试验中,响应患者的 TMB 集中在哪个范围。我曾在某 NSCLC 项目中遇到过,如果用全外显子组数据,建议 threshold 是 10 mut/Mb,但用 panel 数据做出来只有 8.5——因为 panel 覆盖区间的背景突变率不一样。
现在主流做法有两种:
- 基于百分位数:取参考人群 TMB 值的 85%~90% 分位数作为高 TMB cutoff。我曾见过有的中心用 75% 分位,结果把很多中等 TMB 的人也划进去了,反应率没明显提升。
- 基于临床结局:找一组有免疫治疗应答数据的患者,用 ROC 曲线确定最佳 cutoff。这个更费事,但结果最可靠。
| 方法 | 适用场景 | 我遇到的坑 |
|---|---|---|
| 百分位数法 | 没有临床结局数据时先用 | 容易受离群值影响,比如某个样本超高频突变会把整体分布拉偏 |
| ROC曲线法 | 有≥30例疗效数据时 | 样本量太小则曲线波动大,cutoff不稳定 |
| 固定经验值 | 特定癌种(如肺癌10 mut/Mb) | 不同panel必须重新换算 |
4.2 不同癌种阈值差异实例
说到底,TMB 阈值没有“一招鲜”。我把常见癌种的典型阈值整理了一下——注意,这些数字只供参考。你自己用的时候要换算成 panel 对应值。
# 示例:基于最新文献和我的项目经验整理的阈值参考(WES当量)
癌种 TMB-H 阈值 (mut/Mb) 备注
非小细胞肺癌 10 FDA 批准阈值,但实际有 8-15 波动
黑色素瘤 15 部分研究用 20
尿路上皮癌 10 和 NSCLC 接近
结直肠癌 5-8 微卫星稳定型阈值更低
胃/食管腺癌 8-10 亚洲人群可能偏高
你可能会问:为什么结直肠癌阈值这么低?说白了,MSI-H 的结直肠癌本来 TMB 就高,但 MSI 稳定型的 TMB 中位数只有 3~4。如果用 10 做 cutoff,几乎没人入选。我见过一个中心就是死守 10,结果他们的直肠癌队列里一个 TMB-H 都没有,报告根本写不出阳性结论。
这里有个技巧:如果你手头数据量不大,可以用公共数据库(TCGA、cBioPortal)算一下自己癌种的 TMB 分布,然后用分位数法初步拟定阈值。别上来就建队列做 ROC,太烧钱。
4.3 临床分型归纳——TMB 三分法
目前临床上最常用的是三分法:低 TMB、中 TMB、高 TMB。为什么分三档?因为中间那个“中 TMB”人群最让人头疼——他们可能有一部分对免疫有效,但比例不高。我习惯把这组叫“灰色地带”。
具体分型标准我建议参考以下原则(个人经验版):
- 低 TMB:TMB 值低于目标癌种第 25 百分位数。这类患者使用免疫单药效果差,更适合化疗或靶向。
- 中 TMB:第 25~85 百分位数。部分患者可能获益,尤其是联合治疗时。我没记错的话,CheckMate 227 事后分析显示,中 TMB 组双药免疫优于化疗。
- 高 TMB:超过第 85 百分位数(或临床确定 cutoff)。这类患者是免疫治疗的最佳适合人群。
| 分型 | 百分位数范围(参考) | 临床推荐 |
|---|---|---|
| 低 TMB | < P25 | 不推荐单药免疫,考虑化疗/靶向 |
| 中 TMB | P25 ~ P85 | 可考虑联合免疫,需结合 PD-L1 等标志 |
| 高 TMB | > P85 | 推荐免疫单药或联合,疗效概率大 |
嗯,有一点要特别注意:同一患者在不同时间点检测,TMB 可能不一样。我曾遇到一个案例,活检时 TMB 9,半年后再测变成 12,刚好从“中”跨进“高”。这里面有克隆演化的问题,也有检测重复性的影响。所以分型最好基于最近一次样本。
4.4 工具与流程——实操中怎么算阈值
我做这块工作的标准流程大致是这样的:
- 收集参考人群 TMB 值:至少 200 例同一平台检测的数据。
- 去除异常样本:有 MSI-H 或 POLE 突变的样本会拉高均值,建议单独分析。我一开始没剔除 POLE 突变,结果 TMB 中位数虚高,差点误导了 cutoff。
- 计算百分位数:用 Python 的 numpy 做一下非常快。
- 可视化分布:画个直方图,看看有没有双峰。如果有,可能代表两种不同的基因组不稳定机制。
- 确定候选阈值:比如 P75, P85, P90。
- 如果有效应数据:用 ROC 曲线验证,保存逻辑回归结果。
import numpy as np
import matplotlib.pyplot as plt
# 假设 tmb_list 是参考人群的 TMB 值(mut/Mb)
tmb_arr = np.array(tmb_list)
percentiles = np.percentile(tmb_arr, [25, 50, 75, 85, 90, 95])
print("P25:", percentiles[0], "P50:", percentiles[1],
"P75:", percentiles[2], "P85:", percentiles[3])
# 绘制直方图
plt.hist(tmb_arr, bins=30, edgecolor='k', alpha=0.7)
plt.axvline(x=percentiles[3], color='r', linestyle='--', label='P85')
plt.axvline(x=percentiles[4], color='orange', linestyle='--', label='P90')
plt.legend()
plt.xlabel('TMB (mut/Mb)')
plt.ylabel('Count')
plt.show()
这段代码我经常在项目初期快速跑一遍,看看数据大概长什么样。
小提示:如果参考人群中包含多癌种,别混在一起算百分位。我习惯按癌种分别统计,因为各癌种 TMB 分布差别太大了。
4.5 阈值设定与临床分型核心逻辑图
为了让整体思路更直观,我画了一张图。这张图把数据输入、校准过程、分型输出串起来了。
4.6 最后几句实在话
阈值不是死的。你今年做的 cutoff,明年发现新药反应数据变了,就得跟着调。我见过一些团队一次性定好阈值后再也不动,结果临床反馈越来越差。我建议每半年或每次新增 50 例治疗后,重新校准一次。
还一点:报告里一定要写清楚阈值来源。比如“本报告参考本实验室最近 300 例同癌种检测数据 P85 分位数(XX mut/Mb)”。这样临床医生看到才知道怎么用。嗯,把这件事做规范了,后续分析会省掉很多争论。