1. 别再盯着一个数字看了:RNA-seq表达量指标的选择困境

刚接触RNA-seq数据分析的朋友,经常会拿着一个表格问我:“老师,我这个基因表达量高不高?你看这个RPKM值有1000多,是不是很高了?” 或者“我这个差异分析结果里,log2FC很大,但为什么原始Read count看起来没多少?” 每次听到这些问题,我都仿佛看到了当年那个同样困惑的自己。RNA-seq数据里,表达量从来就不是一个“值”能说清楚的,它更像是一把多功能的尺子,测身高、量腰围、算步长,用的刻度都不一样。你拿量腰围的尺子去测身高,结果能准吗?

简单来说,RNA-seq分析里没有哪个单一的“黄金数值”能直接、绝对地告诉你基因表达量的高低。我们常说的Read count、CPM、RPKM/FPKM、TPM,甚至DESeq2标准化后的值,它们各有各的“脾气”和适用场景。选错了指标,轻则让你的图表看起来很奇怪,重则可能得出完全错误的生物学结论。我见过不少项目,前期实验设计、测序都做得很好,最后却在选择表达量指标这一步栽了跟头,导致后续的聚类、通路分析全盘跑偏,非常可惜。

这篇文章,我就想和你像朋友聊天一样,掰开揉碎地讲讲这些常见的表达量指标。我们不只讲公式(那太枯燥了),更要讲清楚每个指标到底在衡量什么、它背后的假设是什么、以及在什么情况下用它是合理的,什么情况下用它会“踩坑”。我的目标是,看完之后,你再拿到一个表达矩阵,能清楚地知道面对“样本间比较”、“基因间比较”还是“给下游软件喂数据”这些不同的任务时,该掏出哪把“尺子”。

2. 基石与起点:理解原始Read Count

2.1 Read Count到底是什么?

让我们从最根本的开始。Read Count,中文常叫“原始计数”或“读数”,它的定义非常直白:在测序数据比对到参考基因组或转录组后,唯一比对到某个基因(或转录本)上的测序片段(Reads或Fragments)的数量。

你可以把它想象成一次“投票”。实验室提取了细胞里所有的RNA,反转录成cDNA,打碎、测序,产生数百万甚至数十亿条短序列(Reads)。我们的分析就像一场大型的“认亲大会”,每条Read都努力寻找自己来自哪个基因。最后,为基因A“站队”的Read有多少条,基因A的Read Count就是多少。这是一个最原始、最直接的观测值,没有经过任何数学上的“修饰”。

这里有个关键细节需要注意,也是新手容易混淆的地方:对于单端测序(SE),一个测序片段产生一条Read,所以Read Count就是片段数。而对于双端测序(PE),一个测序片段会产生两条Read(R1和R2),但在计数时,通常统计的是片段(Fragment)的数量,而不是Read的对数。也就是说,一个配对成功的Fragment,无论它贡献了两条Read,在计数时只算作“1”。这就是为什么在PE数据中,你更常看到FPKM(Fragments Per Kilobase per Million)这个说法,它和RPKM的核心思想一致,只是计数单位从“Read”换成了“Fragment”。在实际操作中,像HISAT2、STAR这样的比对软件,或者featureCounts、HTSeq这样的计数工具,都会为你处理好这个细节,输出基于基因的Fragment计数。

2.2 Read Count的“神力”与“软肋”

为什么我们如此重视这个看似粗糙的原始计数?因为它是几乎所有下游统计分析的基石。目前最主流的差异表达分析工具,如DESeq2和edgeR,其核心数学模型(负二项分布)就是专门为处理原始计数数据而设计的。

这些工具考虑到了测序数据的本质特性:技术重复间的变异(泊松分布)以及基因表达本身固有的生物变异(Gamma分布),两者结合就成了负二项分布。软件内部会利用整个数据集的信息,来估算每个基因的离散度,并对计数进行基于模型的大小因子(Size Factor)校正,从而精准地判断差异。如果你自作主张,先把Read Count转换成RPKM或TPM再丢给DESeq2,就相当于破坏了数据原有的统计属性,把一道适合红烧的食材硬是做成了清蒸,结果自然不可靠。这是我早期踩过的一个大坑,当时觉得RPKM数值“好看”就用了,导致p值分布异常,后来才明白原理。

但是,Raw Read Count的“软肋”也非常明显:它不具备直接的可比性。你不能因为基因A的Count是1000,基因B的Count是500,就说A的表达量是B的两倍。为什么?原因有二:第一,测序深度。样本A总共测了5000万条Read,样本B只测了2500万条,那么样本A中基因的Count天然就更容易更高。第二,基因长度。一个长达10kb的基因,和一个只有1kb的基因,即使它们每个拷贝的转录效率相同,在随机打断测序的过程中,长基因被“抽中”并产生Read的概率也远高于短基因。因此,Raw Count只能用于同一样本内、相同基因在不同条件间的比较(前提是测序深度相似),或者作为标准化软件的输入,绝不能用于跨基因或跨样本(深度差异大)的直接比较。

3. 迈向可比性的第一步:CPM与简单标准化

3.1 CPM的计算与含义

当你需要快速查看数据,或者进行一些对长度不敏感的初步评估时,CPM就派上用场了。CPM的全称是Counts Per Million,计算公式非常简单:

CPM = (基因的Read Count / 样本总比对Read数) * 1,000,000

它的目的很单纯:消除不同样本之间测序深度(Library Size)的差异。通过将每个基因的计数除以总读数再乘以一百万,我们把所有样本“缩放”到了同一个虚拟的“百万级别”测序深度上。这样,样本A中基因X的CPM和样本B中基因X的CPM就可以直接比较了,我们大致能看出同一个基因在不同样本中的相对丰度变化。

举个例子:样本1总Read数500万,基因G的Count为1000;样本2总Read数1000万,基因G的Count为2000。它们的Raw Count翻倍了,但CPM都是 (1000/5,000,000)*1e6 = 200 和 (2000/10,000,000)*1e6 = 200。这说明,扣除测序深度的影响后,基因G在两个样本中的相对表达水平是一致的。

3.2 CPM的应用场景与局限

CPM在哪些场景下比较有用呢?首先,质控(QC)和探索性数据分析。在绘制样本间相关性热图或PCA图进行初步聚类时,使用CPM标准化的数据可以避免测序深度差异主导聚类结果,更能反映真实的生物学差异。其次,在一些不需要考虑基因长度的分析中,比如某些类型的非编码RNA(如miRNA,它们长度非常接近)分析,或者当你只关心同一个基因在不同样本中的相对变化趋势时,CPM是一个轻量且直观的选择。edgeR软件在输出结果时,也常会提供CPM值作为表达量的一个参考。

然而,CPM的局限性就在于它名字里缺少的那个“K”。它没有对基因长度进行校正。因此,CPM绝对不能用于比较不同基因之间的表达水平。一个高表达的短基因,其CPM值完全可能低于一个低表达的长基因。如果你用CPM值来挑“表达量最高”的基因做展示,很可能会选中一堆只是因为它很长而“占便宜”的基因,而不是真正转录活跃的基因。

4. 长度校正的引入:深入理解RPKM/FPKM与TPM

4.1 RPKM/FPKM:它的设计逻辑与时代背景

为了解决跨基因比较的问题,RPKM应运而生。它的全称是Reads Per Kilobase per Million mapped reads。公式比CPM多除了一个基因长度(以千碱基Kb为单位):

RPKM = [基因Read Count / (基因长度(Kb) * 样本总Read数(百万))]

这个公式可以理解为两步:先除以基因长度,校正长度偏差(得到每Kb的密度),再除以总Read数(以百万计),校正测序深度。最终,RPKM值的生物学含义是:在测序深度为一千万条Read的样本中,该基因每千碱基长度上,平均覆盖的Read数。

FPKM与RPKM完全等价,只是把单位从Read换成了Fragment,专门用于双端测序数据。在很长一段时间里,RPKM/FPKM是转录组领域的“标准货币”,大家用它来绘制热图、做聚类、在文章里展示表达量。它的直观性在于,理论上,两个不同基因的RPKM值可以直接比较,数值高的那个被认为表达水平更高。

4.2 RPKM/FPKM的“阿喀琉斯之踵”

尽管RPKM/FPKM看起来完美,但它有一个根本性的、在样本比较时会导致问题的缺陷。这个缺陷就藏在它的标准化方式里:它对每个样本单独进行标准化。

假设有两个样本,样本1中绝大多数表达都来自基因X和Y,样本2中表达则均匀分布在成千上万个基因上。计算RPKM时,每个样本都用自己的总Read数作为分母。这会导致一个后果:样本间RPKM的总和是不固定的。基因X在样本1中的RPKM值,和它在样本2中的RPKM值,其背后的“基准”是不同的。因此,当你比较基因X在两个样本间的RPKM值时,你不仅看到了基因X本身的变化,还可能混杂了样本整体表达谱分布变化所带来的干扰。

说得更直白点,RPKM适合回答“在这个样本里,哪个基因表达最高?”(基因间比较),但在回答“基因X在两个样本中,哪个表达更高?”(样本间比较)时,它可能给出有偏差的答案,尤其是在样本间转录组构成差异很大时(比如比较不同组织、有无病原体感染等)。

4.3 TPM:一个更合理的“升级版”

正是为了解决RPKM在样本间比较时的潜在问题,TPM被提出并越来越受欢迎。TPM的全称是Transcripts Per Million,它的计算步骤与RPKM/FPKM有所不同:

  1. 首先,计算每个基因的 “每千碱基读数”:Rate = Read Count / 基因长度(Kb)
  2. 然后,计算样本中所有基因的Rate之和。
  3. 最后,将每个基因的Rate除以上述总和,再乘以一百万,得到TPM:TPM = (Rate / 所有基因Rate之和) * 1,000,000

关键区别来了:TPM标准化的分母是“样本中所有基因的长度校正后读数之和”,而RPKM的分母是“样本的总原始读数”。这个微妙的差别带来了巨大的优势:每个样本的TPM值总和总是恒定的100万。这使得TPM值在不同样本之间具有真正意义上的可比性。一个基因的TPM从10上升到20,可以明确解释为其表达比例在整个转录组中翻倍了。

在实际应用中,对于大多数需要跨样本比较表达水平的可视化(如热图、箱线图)或分析(如WGCNA共表达网络分析),TPM通常是比RPKM/FPKM更推荐的选择。现在很多流程,如Salmon、kallisto等基于比对(alignment-free)的定量工具,其默认输出就是TPM(或估算的计数),也反映了这一趋势。

5. 实战指南:不同场景下的指标选择地图

理论说了这么多,到底该怎么选?我总结了一个简单的决策流程图,你可以把它贴在墙上:

5.1 场景一:我要做差异表达分析(DESeq2, edgeR)

毫不犹豫,使用原始Read/Fragment Count。 这是铁律。将这些原始计数直接输入DESeq2或edgeR,让它们用自己的内部算法(如DESeq2的median-of-ratios, edgeR的TMM)进行标准化。这些方法比简单的CPM或RPKM更稳健,能更好地处理一些极端表达基因的影响。千万不要手痒先去转换。

5.2 场景二:我想比较同一个基因在不同样本中的表达高低(制作折线图、柱状图)

首选TPM,其次CPM。 TPM是这里的最佳选择,因为它保证了样本间的可比性。如果你只是快速查看趋势,使用CPM也可以,但要确保所有样本的测序深度不要相差太悬殊(比如不超过2-3倍)。绝对不要使用RPKM/FPKM来跨样本绘制单个基因的表达条形图,这可能会引入偏差。

5.3 场景三:我想比较不同基因在同一个样本中的表达高低(寻找高表达基因、绘制样本内分布图)

可以选择RPKM/FPKM或TPM。 在这个场景下,RPKM/FPKM和TPM的目标是一致的,且结果通常高度相似。TPM由于标准化方式更优,理论上稍好一点。你可以用这个样本中基因的TPM值排序,找出表达量最高的那些基因。CPM在这里不适用。

5.4 场景四:我要做聚类分析(PCA、热图)或构建共表达网络(WGCNA)

强烈推荐使用TPM,或者经过log2转换的TPM。 这类分析通常需要跨样本比较基因的表达模式。TPM提供了样本间一致的标度,使得样本间的距离或相关性计算更准确。对于热图,通常会对TPM值进行一个log2(TPM + 1)的转换,以减弱极高表达基因的视觉主导,让颜色梯度更均衡。WGCNA官方教程也推荐使用合适的标准化数据(如log2转换的TPM或FPKM)。

5.5 场景五:我只是想快速检查一下数据质量或表达概况

使用CPM。 计算简单快捷,能快速反应样本的测序深度是否均一,以及基因的大致表达范围。可以用CPM值绘制样本的表达密度分布图,检查是否有异常样本。

为了更直观,我把核心指标的特点和适用场景总结成下面这个表格:

指标全称校正因素核心用途不适用于
Read CountRaw Count无差异表达分析的唯一输入直接进行跨样本、跨基因比较
CPMCounts Per Million测序深度样本间初步比较、质控、非编码RNA分析跨基因比较
RPKM/FPKMReads/Fragments Per Kb per Million测序深度 + 基因长度同一样本内,不同基因间的表达比较跨样本的基因表达水平直接比较
TPMTranscripts Per Million测序深度 + 基因长度跨样本的基因表达比较、聚类、可视化、共表达分析差异表达分析的直接输入

6. 从理论到代码:如何计算和转换这些指标

知道选什么,还得知道怎么算。虽然很多上游定量工具会直接输出TPM或FPKM,但理解如何从原始计数转换过来,能让你更胸有成竹。这里我用R语言演示一下,假设你有一个原始计数矩阵 count_matrix 和一个包含基因长度的向量 gene_lengths。

# 假设 count_matrix 的行是基因,列是样本
# gene_lengths 是一个与行名顺序对应的向量,单位是碱基数

# 1. 计算CPM
calculate_cpm <- function(count_matrix) {
  library_size <- colSums(count_matrix) # 每个样本的总读数
  cpm_matrix <- t(t(count_matrix) / library_size) * 1e6
  return(cpm_matrix)
}
cpm_values <- calculate_cpm(count_matrix)

# 2. 计算RPKM
calculate_rpkm <- function(count_matrix, gene_lengths) {
  # 基因长度转换为Kb
  length_kb <- gene_lengths / 1000
  # 计算每百万读数标准化因子
  rpm_factor <- t(t(count_matrix) / colSums(count_matrix)) * 1e6
  # 计算RPKM
  rpkm_matrix <- rpm_factor / length_kb
  return(rpkm_matrix)
}
rpkm_values <- calculate_rpkm(count_matrix, gene_lengths)

# 3. 计算TPM (推荐方式)
calculate_tpm <- function(count_matrix, gene_lengths) {
  # 基因长度转换为Kb
  length_kb <- gene_lengths / 1000
  # 计算每千碱基读数速率
  rate_matrix <- count_matrix / length_kb
  # 计算每样本的总速率
  total_rate_per_sample <- colSums(rate_matrix)
  # 计算TPM
  tpm_matrix <- t(t(rate_matrix) / total_rate_per_sample) * 1e6
  return(tpm_matrix)
}
tpm_values <- calculate_tpm(count_matrix, gene_lengths)

# 验证:每个样本的TPM总和应为100万
colSums(tpm_values) # 应该都接近 1,000,000

对于差异分析,使用DESeq2的示例则更为直接:

library(DESeq2)
# 假设 coldata 是样本信息数据框
dds <- DESeqDataSetFromMatrix(countData = count_matrix,
                              colData = coldata,
                              design = ~ condition)
# DESeq2内部会使用原始计数进行模型拟合和标准化
dds <- DESeq(dds)
# 获取标准化后的计数(可用于作图,非差异分析本身)
normalized_counts <- counts(dds, normalized=TRUE)
# 获取差异分析结果
res <- results(dds)

记住,normalized_counts 是DESeq2根据其模型估算出的“校正后计数”,它已经考虑了测序深度和组成偏差,可以用于一些需要标准化数据的可视化,但它和TPM/CPM不是一回事,不要混用。

7. 避坑与进阶思考:指标之外的注意事项

选择了正确的指标,只是走对了第一步。在实际项目中,还有一些陷阱需要留心。

首先,关于“零”的表达。 很多基因在部分样本中计数为0,这在进行log2转换(如log2(TPM+1))时会产生问题。那个“+1”的伪计数(pseudocount)是为了避免对零取对数,但它的大小会影响低表达基因的稳定性。DESeq2和edgeR等工具在内部处理零值和离散度时有一套更复杂稳健的机制,这也是为什么不要用简单转换后的数据做差异分析的原因之一。

其次,转录本水平的定量。 我们上面讨论的都是基于基因水平的定量。但一个基因可能有多个异构体(Isoform)。像Salmon、kallisto这类工具直接估计的是转录本水平的表达量(TPM)。如果你拿到的是转录本TPM,想得到基因水平的TPM,不能简单地将同一个基因的所有转录本TPM相加。正确做法是,使用工具(如tximport)将转录本水平的估算计数(estimated counts)汇总到基因水平,然后再用上述方法计算基因水平的TPM。直接加和TPM会破坏“总和为百万”的约束。

最后,也是最重要的:生物学重复和统计检验。 无论你用什么指标来展示表达量高低,当你要下结论说“A条件下基因X的表达显著高于B条件”时,必须依靠基于原始计数的统计检验(如DESeq2, edgeR的结果),而不是直接比较TPM或RPKM的均值。统计检验考虑了组内重复的变异,能给出可靠的p值和错误发现率(FDR)。只看均值差,很容易被个别样本的波动或技术噪音所误导。

在我自己的分析生涯里,见过太多因为指标误用而需要返工的例子。最深刻的一次是协助一个合作者复查数据,他们用FPKM值做样本间聚类,发现一个关键处理组的所有样本都聚不到一起,怀疑实验失败。我检查后发现,他们组内测序深度差异极大,用FPKM放大了这种差异。换用TPM并去除一个低深度异常样本后,生物学重复立刻呈现出漂亮的聚类关系,后续分析才得以继续。这件事让我坚信,理解手中工具的原理,和会使用工具本身同样重要,甚至更重要。希望这些经验之谈,能帮你绕过这些坑,让你在RNA-seq数据的海洋里,航行得更顺畅。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐