1. MOFA简介

多组学因子分析(Multi-Omics Factor Analysis,MOFA)是你做多组学整合时绕不开的一个方法。说白了,它能把转录组、蛋白质组、甲基化组这些不同模态的数据,投影到一组共享的低维因子上。

我最初接触MOFA是在一个肿瘤异质性项目里。当时手上有RNA-seq、DNA甲基化芯片和蛋白质组质谱三套数据,特征维度加起来快两万,却只有几十个样本。传统方法要么硬拼特征,要么分别降维再拼接,结果总是顾此失彼。后来换上MOFA,一次建模就把三组数据里的共同变异和模态特异变异分开了。

为什么需要多组学因子分析?

你想想看,单组学分析已经能看到一些生物信号了,但疾病机制往往跨多个分子层面。转录调控、表观修饰、蛋白丰度之间的交互,单靠一个组学很难串起来。

我见过不少团队用“拼接法”——把每个组学的差异基因/蛋白列表取交集,然后画个Venn图就完事了。嗯,这样会丢掉大量信息。MOFA的做法更优雅:它假设不同组学里存在共享的潜在因子,这些因子驱动了样本间的变异。

  • 数据异构性:不同组学数据类型不同(连续值、计数、beta值),MOFA统一用概率模型处理。
  • 缺失值:很多样本不会测全所有组学,MOFA天然支持部分缺失,不用丢掉样本。
  • 可解释性:每个因子对应一组特征权重,你能直接看出哪些基因/蛋白/甲基化位点贡献最大。
个人习惯:我在做整合前一定会先跑一遍单组学分型,看看每个组学自身能解释多少变异。这样后面用MOFA时,才能判断共享因子到底“共享”了多少。

MOFA核心原理

MOFA本质上是一种贝叶斯因子分解方法。它把每个组学的数据矩阵 Xm(n×pm)分解为:

Xm = Z WmT + εm

其中 Z 是共享因子矩阵(n×k),Wm 是第 m 个组学的权重矩阵(pm×k),εm 是噪声。模型通过自动相关性确定(ARD)自动选择因子数 k,你基本不需要手动调参。

为什么会这样?贝叶斯框架里每个因子有一个超参数 αk,如果某个因子在数据里解释力弱,它的 αk 会趋向无穷大,权重被压到接近零,因子就被“关掉”了。

MOFA核心分析流程 转录组 (RNA-seq) n × p1 蛋白质组 (MS) n × p2 甲基化组 (450K) n × p3 MOFA 贝叶斯分解 共享因子 Z (n × k) 自动选择 k W₁ (p₁ × k) W₂ (p₂ × k) W₃ (p₃ × k) 共享变异 +模态特异
曾经踩过的坑:我用默认参数跑MOFA,发现前两个因子解释方差超过80%,但因子载荷全是NaN。查了半天才发现数据里有一组学全是常数(只有两个样本有值)。MOFA对极端稀疏的组学很敏感,建议预处理时每组至少保留10个非空样本。

主要应用场景

场景描述典型因子解释
疾病亚型发现整合RNA+蛋白+甲基化识别亚型因子1解释临床表型,因子2解释免疫浸润
药物响应预测多组学特征投影到响应相关因子因子权重高的基因可作为生物学标志物
跨模态数据融合同一批样本的不同组学数据对齐共享因子作为样本的低维嵌入
缺失值填充指导利用其他组学辅助填充缺测组学因子分解隐式完成矩阵补全

我个人觉得最有意思的是药物响应场景。以前做耐药分析,单看转录组总找不到稳定标志物。后来把同一批细胞系的RNA-seq、CRISPR screens和蛋白质组一起进MOFA,因子2上的高权重基因正好是已知耐药通路(比如EGFR-STAT3),而因子1全是细胞周期相关。这就清楚了:耐药其实是两群细胞在同时作用。

一句话总结:MOFA不是万能的,但它能帮你从多组学数据中提炼出最紧凑的信号结构。你只需要准备一个多矩阵列表(每个组学是一个n×p矩阵),剩下的模型选择和正则化都交给贝叶斯框架。

实际跑起来长什么样?

下面是一个最简单的Python调用示例(用MOFA2包):

import mofa2
import pandas as pd

# 三个组学的数据矩阵,行名统一为样本ID
rna = pd.read_csv("rna_log2.csv", index_col=0)
prot = pd.read_csv("prot_log2.csv", index_col=0)
meth = pd.read_csv("meth_beta.csv", index_col=0)

# 构建输入
data = {
    "RNA": rna,
    "protein": prot,
    "methylation": meth
}

# 训练模型
model = mofa2.run_mofa(
    data,
    n_factors=10,       # 最大因子数,ARD会自动剪枝
    likelihoods=["gaussian", "gaussian", "beta"],
    outfile="model.hdf5"
)

# 查看因子
factors = model.get_factors()          # 样本 × 因子
weights = model.get_weights("RNA")     # 基因 × 因子
variance = model.get_variance_explained()

嗯,这里要注意:likelihood参数要和数据类型匹配。如果甲基化是beta值但用了gaussian,模型会偏向保守;我个人习惯先用beta分布试一下,再对比结果。

快速排查:跑完看一眼variance_explained的总和,如果低于30%说明模型没学到多少信号,可能是数据噪声太大或者样本量太小。我一般会先把单组学PCA跑一遍,确保每个组学至少有一个主成分能解释>5%变异,再进MOFA。

什么时候不该用MOFA?

  • 样本数小于50且组学数超过5:因子数估计极易过拟合。
  • 各组学之间完全独立(生物学不相关):共享因子会拆成很多小因子,不好解释。
  • 你的目的是做预测而不是解释:MOFA更擅长描述变异结构,分类/回归精度不一定比XGBoost高。

我最近处理一个代谢组+微生物组整合项目时就犯了第二个错误——两组数据在动物实验中确实不相关,结果MOFA跑出7个因子,每个只解释3%-5%变异,完全没法用。后来改成分别降维再做关联分析,反而更清楚。