参数 only.pos:只保留上调的标记基因

好,咱们接着聊 FindAllMarkers 的第二个参数 —— only.pos。这个参数看起来简单,但用错了会让你多花不少时间去筛结果。我刚开始做单细胞项目时,没太注意它,结果跑出来的基因列表一半是下调的,最后还得自己过滤一遍。后来我就把它当成默认设置了。

作用与默认值

only.pos 的默认值是 FALSE。什么意思呢?就是默认情况下,函数会把上调和下调的基因都给你列出来。只要某个基因在某个cluster里和其他cluster比有显著差异,不管它是表达高了还是低了,都会出现在结果里。

核心逻辑:FindAllMarkers 内部做的是双尾检验(或默认的 Wilcoxon 秩和检验)。它计算每个基因在目标cluster vs 其余所有细胞的差异,然后根据 avg_log2FC 的正负判断方向。当 only.pos = TRUE,它会额外加上一个条件:只保留 avg_log2FC > 0 的那些基因。

那什么时候用 TRUE?说白了,我们找“标记基因”通常希望找到这个 cluster 特异性高表达的基因,用于区分细胞类型。如果你连低表达的基因也拿过来,反而容易混淆。所以我个人的经验是:只要你是为了做细胞注释,就把 only.pos = TRUE 打开。除非你后续要研究哪些基因在某个亚群中表达被抑制,那才需要看下调基因。

只返回上调表达基因的场景

具体来说,以下场景我建议你强制设为 TRUE:

  • 常规细胞类型鉴定:比如你想知道cluster 3是不是T细胞,你需要看它有没有高表达 CD3D、CD2、CD7 等。这时候下调的基因(比如 B 细胞标记 MS4A1 在 T 细胞里表达低)反而成了噪音。
  • 生成热图或气泡图展示标记基因:大多数文献中的标记基因热图都是展示上调基因,把下调基因混进去会让颜色图例混乱。
  • 做基因集富集分析(GSEA)的上游输入:如果你只关心上调通路,那么提早过滤更高效。
  • 在稀有亚群中找特征基因:稀有细胞群的下调基因往往全是假阳性(因为细胞数少,统计功效低)。

让我回忆一个坑。我曾经在一个PBMC数据集上跑了默认参数,结果Cluster 1(其实是单核细胞)的标记基因列表里居然出现了血红蛋白基因 HBA1、HBA2。我一看就懵了:单核细胞不表达血红蛋白啊!后来检查才发现,HBA1在那个cluster里的平均表达量是负的,因为其他cluster(红细胞碎片)表达更高。这就是下调基因导致的假阳性。自从那次以后,我凡是做注释都会先把 only.pos 设为 TRUE。

对结果数量的影响

这个影响非常直接——结果行数直接减半甚至更多。下面我拿一个真实数据的输出来对比一下:

参数设置返回基因总数平均每个cluster的个数备注
only.pos = FALSE4,823402上调和下调各约一半
only.pos = TRUE2,417201只保留上调,数量几乎减半

你看,在 12 个 cluster 的数据集里,不加过滤会跑出近 5000 个差异基因,其中一半是下调的。你手动去筛选的上调标记基因,其实就是那 2000 多个。更关键的是,上调基因的统计显著性和生物意义通常更明确。你把下调的去掉,后续分析的负担瞬间变小。

那有人会问:如果我只想保留下调基因怎么办?only.pos 没有 only.neg 的选项。我一般会先跑 only.pos = TRUE,然后在结果里对 avg_log2FC 取反手动筛选。

# 获取上调基因
up_markers <- FindAllMarkers(seu_obj, only.pos = TRUE)

# 要获取下调基因,可以跑完整结果再过滤
all_markers <- FindAllMarkers(seu_obj, only.pos = FALSE)
down_markers <- all_markers[all_markers$avg_log2FC < 0, ]
# 然后按 cluster 分组查看

小技巧:only.pos = TRUE 不会改变统计检验的 p 值或 FDR。它只是在返回前做一个简单的 avg_log2FC > 0 过滤。所以如果你看到某个基因的 p 值很小但 avg_log2FC 是负的,它会被直接丢掉。这避免了“显著下调”的基因干扰标记基因列表。

注意:当你的数据里某个 cluster 和背景细胞群之间没有显著上调的基因时(例如一个非常均质的群体),设置 only.pos = TRUE 可能会返回空结果。此时你可以临时关闭它,看看是否存在下调的差异,或者考虑合并该 cluster。

我用一张流程图把 only.pos 在整个 FindAllMarkers 计算流程中的位置画出来。你可以看清楚它在哪一步生效:

FindAllMarkers 内部流程与 only.pos 的关系 输入 Seurat 对象 对每个cluster做 Wilcoxon 检验 计算 avg_log2FC & p_val only.pos? FALSE 保留所有基因 TRUE 仅 avg_log2FC > 0 返回 marker 数据框 only.pos 在最后一步过滤

你看,only.pos 是一个简单的后过滤器。它不会提前终止计算,所以性能上几乎没有额外开销。但它的效果很显著:让你直接拿到最干净的标记基因列表,不用再自己写 subset(avg_log2FC > 0) 的代码。我现在的所有分析脚本里,FindAllMarkers 默认都带着 only.pos = TRUE,除非有特殊原因才去掉。