表达矩阵预处理:低表达基因过滤与归一化
拿到表达矩阵后,很多人上来就扔进模型里跑。我早期也这么干过,结果模型训练半天loss死活不降。后来才发现,原始计数矩阵里的垃圾特征太多,把模型都带偏了。
预处理算是整个流程里最“脏”的活,但它直接影响后续所有分析的质量。说白了,这一步做不好,后面全白搭。
为什么要过滤低表达基因?
一个典型的RNA-seq数据里,可能有三分之一的基因只有零星几条reads。这些基因在大部分样本里都是零,偶尔冒个1、2,纯粹是技术噪声。把它们留在矩阵里,会带来几个麻烦:
- 噪声干扰统计检验,false positive暴涨
- 归一化时被大量零值拉偏
- 模型训练时增加维度和计算开销,毫无收益
- 降维可视化时,零基因会把样本点挤在一起
我记得有个项目,客户给的乳腺癌数据包含60000多个基因,过滤后只剩不到18000。模型AUC直接从0.72提到了0.81。你想想看,省下三分之一的特征,效果反而更好。
常用的过滤策略
过滤没有绝对标准,但业内有一些约定俗成的做法。下面这张表总结了最常见的几种:
| 策略 | 规则 | 适用场景 |
|---|---|---|
| 基于总计数 | 过滤总counts < 10 的基因 | 极低深度数据,快速粗筛 |
| 基于CPM阈值 | 至少n个样本的CPM > 1 | 最常用,推荐n≥3 |
| 基于表达样本比例 | 在m%样本中counts > 0 | 组间比较时,保留在至少一组内表达的基因 |
| 基于方差 | 保留方差最大的top N个基因 | 纯降维或聚类,不适用差异表达 |
我个人习惯用CPM阈值法,因为CPM可跨样本比较,选≥1的threshold能把大部分噪音滤掉。代码实现也很简单:
# R语言示例
cpm_matrix <- edgeR::cpm(count_matrix)
keep <- rowSums(cpm_matrix > 1) >= 3 # 至少3个样本CPM>1
filtered_matrix <- count_matrix[keep, ]
这里有个坑——如果你后续要做差异表达,过滤一定要基于原始样本分组分别做,不能在合并数据里一刀切。我吃过这个亏,把两组特有的低表达基因全干掉了,最后差异基因列表里啥也没剩。
归一化方法详解
过滤完,接下来就是让不同样本“公平对话”。不同样本的文库大小、基因长度、GC含量都不一样,直接比counts没意义。
归一化方法有好几种,我在项目中换着用过,说说差异:
| 方法 | 全称 | 公式要点 | 能比较什么 |
|---|---|---|---|
| CPM | Counts Per Million | counts / 文库大小 × 1e6 | 样本间基因表达横向比较 |
| RPM | Reads Per Million | 同CPM(原名不同) | 同CPM |
| TPM | Transcripts Per Million | 考虑基因长度,先除长度再归一化 | 基因内、样本间、跨研究比较 |
| log2转换 | log2(x + 伪计数) | 通常log2(CPM + 1) 或 log2(TPM + 1) | 线性模型、降维、可视化 |
CPM vs TPM:大多数场景下CPM就够了,尤其是做差异表达时。如果想比较不同基因的表达丰度,或者用到一些需要基因长度的下游工具,那就上TPM。我个人的经验是,做机器学习特征时,log2(CPM + 1) 基本不会翻车。
TPM的计算逻辑稍微绕一点,建议跑一遍代码体会:
# TPM计算伪代码
# 输入:counts矩阵、基因长度向量(bp)
rpk <- counts / (gene_length / 1000) # 每千碱基reads
tpm <- t(t(rpk) / colSums(rpk) * 1e6)
log2转换的常见误区
log2转换几乎是必做步骤,但容易踩两个坑:
- 伪计数太小:log2(x+0.5) 或 log2(x+0.01) 会在低表达区域产生剧烈波动。建议用 log2(x + 1),0就变成0,1就变成1,比较干净。
- 转换时机不对:先归一化再log2,还是先log2再归一化?一定是先归一化(CPM、TPM),再log2。否则零值取对数后会变成负无穷。
预处理完整流程
从原始计数到可以喂给机器学习模型,整个预处理路径大概是这样的:
如果你用Python,可以试试 pypx 或者 scanpy 的预处理管线,它们内置了过滤函数 filter_genes 和归一化 normalize_total、log1p。一行命令就能跑完上面整套流程。
嗯,预处理这块看似基础,但水挺深的。我记得有一次帮别人Review代码,发现他先log2了原始counts,再归一化,结果矩阵里全是NaN——因为零的log2是负无穷,再求和归一化就直接炸了。这种细节,踩过一次就再也忘不掉了。