1. 为什么“推断肿瘤细胞”是单细胞分析里最绕不开的一步
做单细胞转录组数据分析的人,迟早都会撞上同一个问题:我拿到一堆细胞,也做好了降维聚类,可到底哪些是肿瘤细胞?这个问题的分量,比想象中重得多。因为后续的拟时序分析、细胞通讯、转录因子调控网络、差异表达,全都建立在“哪些细胞是肿瘤、哪些是微环境细胞”这个基础上。如果你分错了,后面每一步都是在错误的地基上盖楼,而且盖得越高塌得越狠。
很多人以为肿瘤细胞很好认,毕竟它和正常细胞长得不一样、突变多、增殖快。但真正拿到单细胞数据后你会发现,事情远没有那么简单。肿瘤组织里的细胞构成极其复杂,有真正的肿瘤细胞,有浸润进来的T细胞、B细胞、巨噬细胞,有基质细胞里的成纤维细胞、内皮细胞,还有可能混着正常的上皮细胞。关键问题在于:肿瘤细胞大多数情况下也表达上皮细胞的marker,比如EPCAM、KRT8、KRT18,你要是单看这些基因去挑,极大概率会把正常上皮也圈进来。反过来,某些肿瘤细胞因为上皮-间质转化(EMT)的存在,上皮marker表达反而很低,你又可能把它们漏掉。
推断肿瘤细胞不是一道有标准答案的判断题,更像是一道需要多条线索交叉印证的推理题。核心思路无非几种:看拷贝数变异(CNV)、看突变位点、看表达特征打分、看染色体层面的异常信号。这里面最常用、也最能在公开数据集上复现的,就是基于CNV推断的思路。代表性的工具是inferCNV,这也是我这次要重点展开的东西。
这篇文章适合谁看?我觉得只要你在做肿瘤单细胞数据分析,不管你是生信入门不久的新手,还是已经跑过几套流程的老手,都应该仔细读一遍。新手可以通过这篇文章搞清楚为什么不能只看marker基因来定肿瘤细胞,老手则可以对照自己的分析流程,看看哪些地方其实存在隐患。
2. 分析思路与实际方案选型
2.1 从“不寻常的转录特征”到CNV推断的基本原理
先说生物学基础。肿瘤细胞之所以是肿瘤细胞,是因为它基因组层面发生了大规模的变异,包括染色体片段的扩增和缺失。这些DNA层面的拷贝数变化,会在转录组层面留下“痕迹”——CNV扩增的区域,相应基因的表达量普遍会偏高;CNV缺失的区域,相应基因的表达量普遍会偏低。单个基因的表达波动可能受到各种噪音干扰,但当一整段染色体区域的基因都表现出系统性偏差时,这就很难用随机波动来解释了。
inferCNV干的事情,就是在一大堆单细胞里,通过滑动窗口扫描基因在染色体上的位置,计算每个细胞在每个基因组区域的相对表达水平,然后和一组“正常”参考细胞做比较,最终推断出每个细胞可能存在的CNV信号。因为这个思路利用的是整个染色体区域的基因表达趋势,而不是某几个marker基因,所以对肿瘤细胞判断的敏感性和特异性都会好很多。
这里需要理解一个关键点:inferCNV输出的不是“这个细胞是不是肿瘤细胞”的结论,而是“这个细胞在哪些基因组区域可能存在拷贝数异常”的信号强度。你需要根据CNV信号的整体模式,去判断一个细胞亚群是不是具有肿瘤细胞特征的群体。举个例子,如果一个细胞亚群在染色体1q、5p、8q等区域都表现出明显的扩增信号,在3p、17p等区域表现出明显的缺失信号,而且这个模式在整个亚群里高度一致,那这个亚群是肿瘤细胞的可能性就非常大了。
2.2 为什么不能只看marker基因
我知道很多人一开始都会想:肿瘤细胞不就是表达EPCAM、KRT的那些细胞吗?我按marker基因把上皮细胞挑出来,再用CNV工具确认一下,不就行了吗?这个思路不能说错,但在实际操作中会遇到几个扎心的问题。
第一,肿瘤组织的上皮细胞群体不是“纯”的。里面除了肿瘤细胞,还混着正常上皮。如果是肝细胞癌,那肿瘤细胞和正常肝细胞都表达高水平的ALB;如果是肺癌,肿瘤细胞和正常肺泡上皮都可能表达SFTPC或KRT。仅凭上皮marker区分不出来。
第二,肿瘤细胞在体内经历了极其复杂的演化,基因表达状态已经被打乱。有些肿瘤细胞和正常细胞在转录组层面上的差异,远小于同一种肿瘤内部不同亚克隆之间的差异。你以为是同质的肿瘤群体,其实可能由多个转录状态明显不同的亚群组成。这种情况下,单纯依赖表达marker挑肿瘤细胞,会把异质性的群体人为地切碎。
第三,EMT和循环肿瘤细胞状态下,上皮marker会下调甚至消失。一个发生了EMT的肿瘤细胞,它的EPCAM表达可能低到几乎检测不到,但它确确实实是肿瘤细胞。你要是只用上皮marker去圈,这些细胞就被漏掉了,而它们恰恰可能是导致转移和耐药的最危险群体。
所以我的建议是:可以先快速用已知的marker基因做一个粗略的筛查,心里有个底,但最终判断一定要回到CNV证据上来。marker基因是线索,CNV是证据,二者结合才能下结论。
2.3 inferCNV与CopyKAT的取舍
单细胞CNV推断工具有好几个,除了inferCNV,还有CopyKAT、CaSpER、SparseIsoCNV等。其中大家用得最多的是inferCNV和CopyKAT。
| 对比项 | inferCNV | CopyKAT |
|---|---|---|
| 输入数据 | 表达矩阵 + 细胞注释 | 表达矩阵(依赖等位基因信息较少) |
| 核心思路 | 滑动窗口比较肿瘤与正常细胞的相对表达 | 基于贝叶斯混合模型,估计单细胞拷贝数状态 |
| 输出结果 | 每个细胞每个基因组区域的CNV信号热图 | 每个细胞的CNV状态(扩增/缺失/中性)及预估倍数 |
| 优势 | 结果可视化直接,适合全局观察 | 能给出较清晰的“肿瘤/非肿瘤”划分,方便后续直接标注 |
| 劣势 | 阈值判断需要经验,不同样本差异较大 | 对数据质量要求高,低测序深度下噪声大 |
| 适用场景 | 组织构成复杂、需要人工审核的场景 | 样本量大、需要自动化分类的场景 |
我在实际项目里通常两个都会跑一遍。先把inferCNV的结果做出来,看整体热图;再用CopyKAT的结果做交叉验证。如果两个工具的结论高度一致,那这个细胞亚群是不是肿瘤细胞基本可以盖棺定论了。如果结论不一致,我会仔细检查那一部分细胞的原始表达特征,看看是不是某个亚群在测序深度或批次效应上出了问题。
坦白说,没有任何一个工具是百分百准确的。CNV推断的结果本质上是基于概率的判断,最终结论一定要结合病理知识、样本类型、肿瘤纯度等信息综合判定。这一点后面我会专门展开讲。
3. 实操过程与核心环节实现
3.1 前期准备:从原始数据到标准注释
在跑inferCNV之前,第一步是确保你的Seurat对象已经完成了标准分析流程:质控、归一化、降维、聚类、细胞类型注释。这一步做得好不好,直接影响后续CNV推断的准确度。我见过太多人在聚类注释阶段马虎糊弄,后面inferCNV跑出来一片模糊,还以为是工具不行,其实是前期的基因过滤和细胞筛选就没做到位。
我比较推荐的处理细节有几个:首先,在质控环节,对线粒体基因比例要设置合理阈值,一般我会用20%作为上限,线粒体比例过高的细胞往往是破损细胞或者空液滴,它们的表达谱会出现严重的偏差。其次,在归一化之后,要过滤掉在极少数细胞中表达的基因,比如至少在10个细胞中检测到才保留,这能显著降低背景噪音。还有一个很多人忽略的点:如果你计划用inferCNV,建议不要用SCTransform的残差作为输入。inferCNV默认的输入格式是原始的UMI count或者归一化后的表达值,虽然技术上它接受多种格式,但为了保持表达值的可解释性,我通常直接用LogNormalize之后的矩阵。
细胞类型注释这一步,原则是宁粗勿细。什么意思?就是不需要你精细标注到“CD8+ T细胞耗竭亚群”、“Treg亚群”这种级别,只需要把大类分清楚:T细胞、B细胞、NK细胞、髓系细胞、成纤维细胞、内皮细胞、上皮细胞等。CD45+的免疫细胞和间质细胞,是天然的“正常参考细胞”来源。这里有一个非常重要的原则:参考细胞里绝对不能混入肿瘤细胞。一旦参考组被污染,inferCNV的结果会被严重干扰,最典型的表现就是所有细胞的CNV信号都变得模糊、整体上移,你根本区分不出哪群是肿瘤。
3.2 用inferCNV推断拷贝数变异的完整步骤
inferCNV的输入文件格式和运行方式,和常见的Seurat流程不太一样,需要单独准备。我用的是10x单细胞数据,下面直接给出我验证过的完整流程。
首先准备两个文件。一个是表达矩阵,行是基因,列是细胞,每一格是表达值。我建议你直接把Seurat对象里的归一化矩阵导出来,写成表格存成txt文件。另一个是细胞注释文件,两列:第一列是细胞名,第二列是细胞类型分组,格式大致如下:
cell_1 Normal cell_2 Normal cell_3 Tumor_Macro cell_4 T_cell这里注意,分组名称里不要有空格,不要用中文,不要用特殊符号。我习惯把非肿瘤的免疫细胞和基质细胞统一标注成Normal,把候选的肿瘤上皮细胞标注成Tumor。inferCNV会以Normal组作为背景参考,推断Tumor组中每个细胞相对Normal组的基因组区域表达偏差。
接下来写一个R脚本调用inferCNV,核心代码如下:
library(infercnv) # 创建infercnv对象 infercnv_obj <- CreateInfercnvObject( raw_counts_matrix = "expr_matrix.txt", annotations_file = "cell_annotations.txt", gene_order_file = "gene_order.txt", ref_group_names = c("Normal") ) # 运行inferCNV infercnv_obj <- infercnv::run( infercnv_obj, cutoff = 0.1, out_dir = "inferCNV_output", cluster_by_groups = TRUE, denoise = TRUE, HMM = TRUE )这段代码里需要注意的是第7行那个cutoff参数。对于10x数据,我一般用0.1;对于smart-seq2这类全长转录组数据,可以用1。这是因为不同平台的表达值分布差异很大,cutoff的作用是把低表达的基因过滤掉,减少背景噪音。如果不确定该用多少,可以先用默认值跑一遍,然后在输出目录里检查分析日志,看看过滤后的基因数量是否还在一个合理的范围。
gene_order.txt是第三个输入文件,它告诉inferCNV每个基因位于哪条染色体上的什么位置。这个文件可以从Bioconductor的TxDb.Hsapiens.UCSC.hg19.knownGene等注释包中导出,也可以直接从UCSC的table browser下载。关键点是:基因名的格式要和表达矩阵的行名完全一致,我用过Ensembl ID也用过Gene Symbol。这里有一个我自己踩过很多次坑的细节:如果你用Gene Symbol,那么同一基因可能会对应多行注释记录,需要先去重再写入gene_order文件。一般遇到这种情况,我会对每个基因保留染色体坐标区间最大的那一条记录。
3.3 如何阅读inferCNV热图并划定肿瘤细胞
跑完inferCNV之后,输出目录里有一张巨大的热图,通常是PDF格式的,比如infercnv.observations_dendrogram.pdf教你怎么看这张图。但在此之前,我先解释一下热图里那些红色、蓝色都代表什么。
inferCNV热图默认的配色是:红色代表该基因组区域基因表达相对参考组偏高,也就是可能的CNV扩增;蓝色代表表达偏低,也就是可能的CNV缺失。白色说明和参考组水平差不多。你在热图里会看到,参考细胞那部分区域基本是均匀的白色或很浅的颜色。如果参考细胞区域也是大片红色蓝色,只有两种可能:一是你选的参考细胞本身就不是“正常”的,被肿瘤细胞污染了;二是样本本身是纯肿瘤组织,里面根本找不到足够的正常细胞,这种情况在富集了肿瘤细胞的样本里尤为常见。
判断肿瘤细胞的标准,不是看单个细胞有没有红蓝信号,而是看整群细胞是否呈现出一致的CNV模式。比如你关注的那个上皮细胞大群,如果几乎所有细胞在相同基因组区间都呈现出一致的扩增或缺失信号,那就高度倾向于是一个肿瘤细胞群体。如果信号断断续续,只在个别细胞中出现,那更可能是噪音。
把HMM预测结果也用起来会更好。inferCNV的HMM模式会为每个细胞在每个基因组区间预测一个拷贝数状态,比如0代表缺失,1代表中性杂合,2代表扩增,3代表高度扩增。你可以从输出目录里读取HMM的观察结果表,算一下每个细胞亚群有多少比例的基因组存在非中性状态。这个量化指标在面对不同样本时非常有参考价值。比如我的经验是,如果一个亚群的平均非中性CNV区域比例超过20%,这个亚群被认定为肿瘤细胞的把握就很大了。
不过我要提醒你一句:CNV热图很容易让人“看图说话”。同一个数据集,不同人看同一张热图可能得出不同结论。所以我的习惯是,除了肉眼观察,还必须结合下游的细胞亚群构成来验证。比如,如果你判断某个大群是肿瘤细胞,但这个群里同时高表达CD3D和CD8A,那就必须停下来检查一下——大概率是双重标签的细胞或粘附造成的doublet被混进来了。
3.4 用CopyKAT辅助交叉验证
inferCNV更像是一个“证据展示器”,它把染色体区域的异常信号可视化出来,但最终判断还是要人来做。CopyKAT则更直接,它会直接给每个细胞一个分类结果:aneuploid(非整倍体,也就是嫌疑肿瘤细胞)还是diploid(二倍体,正常细胞)。这个明确的结果,特别适合做交叉验证。
CopyKAT跑起来也不复杂,核心代码如下:
library(CopyKAT) copykat <- runCopyKAT( raw_mat = "expr_matrix.txt", species = "human", id.type = "S" )它会自动计算CNV矩阵,然后对每个细胞做分类预测,结果保存在CopyKAT_out文件夹里。需要注意,CopyKAT对输入的表达矩阵有格式要求:基因名要求是Symbol格式,矩阵需要是raw counts,不需要归一化。另外它对测序深度比较敏感,我自己的实测经验是,平均每个细胞检测到的基因数低于1500的时候,CopyKAT的输出会变得很不可靠,会出现大面积的“unclassified”结果。
如果你发现inferCNV和CopyKAT的结论冲突,先别急着改工具参数,回到数据本身排查。我就遇到过一种情况:某个上皮细胞亚群被inferCNV判为肿瘤,但CopyKAT判为正常。仔细检查后发现,这个亚群其实包含两种细胞,一种是非常典型的肿瘤细胞,另一种是表达特征接近肿瘤细胞的正常上皮细胞。降维聚类时它们因为转录相似被分到了同一个cluster里,实际上内部是高度异质的。这个时候需要对这个亚群做一次子聚类,重新跑一遍分析,往往就能把两拨细胞拆开了。
4. 常见问题与排查技巧实录
4.1 没有正常细胞参考怎么办
这是被问得最多的问题。很多肿瘤样本尤其是高度富集的纯肿瘤样本,里面几乎找不到明显的免疫细胞和基质细胞。没有正常参考组,inferCNV就不能直接跑。这时候有几个变通思路。
一种做法是,从外部引入一个正常组织样本的公共数据作为参考。比如你研究的是肺癌,那就去公共数据库里下载肺癌患者的癌旁正常组织或者正常肺组织的单细胞数据,用里面的上皮细胞作为参考。这种做法偶尔有批次效应的风险,但inferCNV本身对参考组的绝对表达水平并不那么敏感,它更看重参考组内部的均质性。所以只要外部数据的质量可靠,这个思路通常行得通。
还有一种做法是,如果样本中混合了正常上皮和肿瘤上皮,但你看不出来,可以通过CNV模式的分层聚类来排除。你可以先不设参考组,对全上皮细胞跑一遍inferCNV,或者用copykat的“不含参考组”模式。这样癌性上皮通常会在聚类树的某一支形成一大簇,正常上皮则聚到另一支。然后你就可以把看起来正常的上皮作为参考,再跑一轮inferCNV做精细化分析。
4.2 免疫细胞被误判为肿瘤细胞
免疫细胞被误判为肿瘤,最容易出现在B细胞和浆细胞身上。B细胞的受体基因(IGK、IGL、IGH)不仅表达量极高,而且基因组层面上B细胞的受体基因座本身就存在重排。这些特征在CNV算法眼里看起来就像“扩增信号”,容易导致B细胞被错误地标成aneuploid。浆细胞更离谱,它有大量的免疫球蛋白分泌活动,基因表达水平高得离谱,非常容易被误判。
如果你发现自己的肿瘤细胞里混着一群同时高表达CD79A、MS4A1、JCHAIN的细胞,那八成就是B细胞/浆细胞被误判了。解决策略有两个层面:一是分析前就把这些细胞从候选组里剔除,只保留上皮细胞或者你要关注的细胞类型;二是如果无法剔除,那就不要用全转录组去跑CNV,而是先把免疫球蛋白基因和TCR基因放进排除列表,再运行inferCNV。
同样的道理也适用于肝细胞癌。肝细胞本身表达非常高水平的白蛋白基因ALB,而且肝脏细胞有独特的代谢基因表达谱。如果你拿普通细胞作为参考组,肝细胞自身的这些代谢基因特征,很容易被CNV算法识别成异常信号。所以肝细胞癌样本中,我建议不仅需要排除IG/TCR基因,还要谨慎评估高表达代谢基因的影响。
4.3 测序深度不够导致结果不可信
单细胞CNV推断的本质是看表达量的相对变化,这是典型的“统计推断”,对数据质量的要求非常高。如果你的数据每个细胞只有几百个基因被检测到,那么计算出来的CNV信号会非常稀疏,你会看到热图上有大片灰色的区域,信号断断续续,根本无法判断细胞到底有没有CNV异常。
深度不够的解决方案,跟硬件升级一样,最直接有效但很费钱:重新补测序。但在补测序之前,你可以先试试调整参数,看能不能榨出更多信息。inferCNV里有一个参数叫noise_filter,通过设置这个参数可以过滤掉单细胞表达中随机噪声带来的假信号,让真正的CNV信号更突出。具体的设置逻辑是:先做几次试验,逐渐提高过滤强度,观察主要信号是变得更清晰还是被滤没了。如果主要信号随着过滤强度增加反而变得更清晰,说明信号是真实存在的。
另外一个容易忽略的点是基因数量过滤散点造成的“边缘污染”。如果你在10x数据里用了过低的基因数阈值做质控,导致很多空液滴或破损细胞被保留进来,它们的表达谱本身就不具备代表性,会给CNV推断引入大量随机噪声。所以做数据质控时,千万别小气,该滤掉的细胞就滤掉。宁可损失一些真实细胞,也不要让垃圾细胞干扰核心判断。
4.4 肿瘤纯度和异质性对判定标准的影响
不同样本的肿瘤纯度差异极大。一个纯度高的样本,几乎所有上皮细胞都是肿瘤细胞,CNV信号会比较清晰;但一个纯度低、基质和免疫细胞比例极高的样本,肿瘤信号会被稀释,inferCNV的热图看起来也会模糊一些。纯度低的样本里,单个肿瘤细胞和免疫细胞之间的转录差异巨大,参考组的背景会更“干净”,但候选组中肿瘤细胞的信号比例也会更低。在这种样本中,我建议适当降低对个体细胞CNV信号的置信度,转向关注细胞亚群整体的一致性。
肿瘤异质性还需要特别强调。同一个患者的肿瘤组织里,可能存在不同亚克隆,有的亚克隆有大规模染色体断裂重排,有的亚克隆相对“安静”,只有少数区域有拷贝数改变。你不能期望所有肿瘤细胞都表现出强烈的CNV信号。如果一个细胞亚群只是表达一个肿瘤特异性的marker而没有明显的CNV信号,不代表它就不是肿瘤细胞,它有可能是这个肿瘤演化过程中的一个早期分支,或者某个亚克隆的拷贝数变异本来就比较温和。
我在写结论时,会加一个“肿瘤细胞概率”的定性描述,例如:高置信度、中等置信度、低置信度。高置信度指CNV信号强且亚群内一致性高;中等置信度指CNV信号存在但较弱或仅在部分细胞中看到;低置信度指没有明显CNV信号,仅靠marker表达和位置推断。这个定性描述比单纯画一个“肿瘤”或“非肿瘤”的二分类边界,要诚实得多,也更能帮助下游的分析团队做判断。
4.5 从“细胞注释”到“肿瘤状态”的完整操作清单
把前面的内容汇总成一张可直接照做的速查清单。我在新项目里一定会过一遍这个清单,基本可以挡住80%以上的常见问题。
第一步,数据质控。确保每个细胞的基因数、UMI数、线粒体比例都合理,过滤掉Doublet。第二步,标准聚类。SPCA还是UMAP都行,关键是把cluster分清楚,然后用已知marker注释大类。第三步,标记候选细胞。把上皮细胞、内皮细胞、成纤维细胞这些非免疫细胞的类别列为候选组。第四步,选择参考组。从免疫细胞和基质细胞中选出至少200个细胞作为正常参考,注意排除浆细胞。第五步,运行inferCNV,仔细阅读热图和HMM结果。第六步,运行CopyKAT做交叉验证。第七步,对比两种工具的结果,对差异部分单独检查。第八步,对候选瘤群做子聚类分析,确认内部的一致性。第九步,在论文或报告中写出判定依据,附上CNV热图和HMM结果。
这个流程跑下来,肿瘤细胞的判定基本就有了扎实的证据基础。而且在审稿人眼里,相比只说“根据marker基因注释为肿瘤细胞”这种毫无说服力的表述,一套从CNV推断、交叉验证到手动审核的完整分析流程,会显得专业得多。
最后再分享一个小技巧:如果你处理的是多例患者的样本,不要只在单个样本里推断肿瘤细胞,把多例样本放在一起比较非常有用。一个患者样本中被判为肿瘤的细胞群体,其CNV模式可能跟同癌种的其他患者有相似之处。这种跨样本的模式对比,能帮你判断哪些CNV信号是这个癌种的共同特征,哪些是某位患者独有的个性化事件。如果你在多个样本中反复看到某条染色体区域的扩增信号,那这条区域就可能是一个成瘤驱动事件,值得做进一步的生存分析或功能实验。这也是单细胞CNV分析除了细胞注释之外,为数不多能直接产出生物学假说的价值点。