跳转至

代谢组学多元统计(PLS-DA / OPLS-DA)


一句话说明

PLS-DA和OPLS-DA是代谢组学最常用的"找差异"工具——它们能从几百上千个代谢物中,找出真正区分不同组别(如疾病vs健康)的关键代谢物,就像在一堆嫌疑人中找出真正的罪犯。


核心知识点

要点1:PCA vs PLS-DA vs OPLS-DA

  • PCA(主成分分析):无监督,不用分组信息,只看数据自然分布——用于质控和异常值检测
  • PLS-DA(偏最小二乘判别分析):有监督,利用分组信息最大化组间差异——用于分类和筛选标志物
  • OPLS-DA(正交PLS-DA):在PLS-DA基础上分离正交变异(组间无关的变异),让模型更清晰——是代谢组学论文中最常用的方法

要点2:模型质量评估(超重要!)

  • R2X:模型对X(代谢物数据)的解释率,越高越好
  • R2Y:模型对Y(分组信息)的解释率,越高越好
  • Q2:交叉验证的预测能力,最重要的指标!Q2 > 0.5算可接受,>0.7算好
  • 置换检验(Permutation Test):随机打乱分组标签200次,重新建模。如果原模型的Q2远高于随机模型,说明不是过拟合。面试必问!

要点3:VIP值筛选差异代谢物

  • VIP(Variable Importance in Projection):衡量每个代谢物对分类贡献的大小
  • VIP > 1:该代谢物对分组贡献高于平均水平,是潜在的差异代谢物
  • 常用筛选标准:VIP > 1 且 p值 < 0.05(单变量检验),双重筛选更可靠
  • S-plot:OPLS-DA特有的图,横轴=相关性,纵轴=协方差。四角的点是最可靠的差异代谢物

要点4:常见陷阱

  • 过拟合:样本少变量多时极易发生,必须做置换检验验证
  • 组内变异大:个体差异大会让PLS-DA效果变差
  • 不平衡样本:组间样本量差异大会影响模型,需要注意
  • 多组比较:超过2组时PLS-DA仍可用,但OPLS-DA只适合两组比较

实战代码

# === PLS-DA和OPLS-DA分析(使用scikit-learn和pyopls) ===
import numpy as np
import pandas as pd
from sklearn.cross_decomposition import PLSRegression
from sklearn.model_selection import cross_val_predict, LeaveOneOut
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt

# 1. 准备数据
data = pd.read_csv("metabolomics_normalized.csv", index_col=0)  # 读取归一化后的数据
X = data.iloc[:, 1:].values  # 代谢物矩阵(去掉分组列)
y_labels = data.iloc[:, 0].values  # 分组标签(如"Disease", "Control")
le = LabelEncoder()
y = le.fit_transform(y_labels)  # 将标签转为0/1编码

# 2. PLS-DA建模
pls = PLSRegression(n_components=2, scale=True)  # 2个主成分,自动缩放
pls.fit(X, y)  # 拟合模型

# 3. 获取得分(Scores)用于绘制得分图
scores = pls.x_scores_  # 每个样本在主成分上的投影坐标

# 4. 绘制PLS-DA得分图
fig, ax = plt.subplots(figsize=(8, 6))
for label in np.unique(y):
    mask = y == label  # 筛选当前组的样本
    ax.scatter(scores[mask, 0], scores[mask, 1],
               label=le.inverse_transform([label])[0], s=60, alpha=0.7)
ax.set_xlabel("Component 1")
ax.set_ylabel("Component 2")
ax.legend()
ax.set_title("PLS-DA Score Plot")
plt.savefig("plsda_scores.png", dpi=150)

# 5. 交叉验证评估Q2
loo = LeaveOneOut()  # 留一法交叉验证
y_pred_cv = cross_val_predict(pls, X, y, cv=loo)  # 交叉验证预测
ss_res = np.sum((y - y_pred_cv) ** 2)  # 残差平方和
ss_tot = np.sum((y - y.mean()) ** 2)  # 总平方和
q2 = 1 - ss_res / ss_tot  # Q2计算公式
print(f"Q2 = {q2:.3f}")  # Q2>0.5说明模型有预测能力

# 6. 计算VIP值
def calculate_vip(pls_model):
    """计算PLS-DA的VIP值"""
    t = pls_model.x_scores_        # 得分矩阵
    w = pls_model.x_weights_       # 权重矩阵
    q = pls_model.y_loadings_      # Y载荷
    p, h = w.shape                 # p=变量数, h=主成分数
    vips = np.zeros(p)
    s = np.diag(t.T @ t @ q.T @ q).reshape(h, -1)  # 各成分贡献
    total_s = np.sum(s)
    for i in range(p):
        weight = np.array([(w[i, j] / np.linalg.norm(w[:, j])) ** 2 for j in range(h)])
        vips[i] = np.sqrt(p * np.sum(s.T * weight) / total_s)
    return vips

vip_scores = calculate_vip(pls)
# 筛选VIP>1的代谢物
important_features = data.columns[1:][vip_scores > 1]
print(f"VIP>1的代谢物数量: {len(important_features)}")

面试常问点

Q1: PLS-DA模型的置换检验怎么做?为什么要做?

参考答案:置换检验是验证PLS-DA是否过拟合的"金标准"。做法是:随机打乱Y标签(分组信息)200次,每次重新建PLS-DA模型计算R2Y和Q2。如果原模型的Q2远高于所有随机模型的Q2(p<0.05),说明模型确实捕捉到了真实的组间差异,而不是在拟合噪音。如果不做置换检验,样本少变量多时PLS-DA几乎总能"找到"差异,但那可能是假的。

Q2: VIP值和p值怎么配合使用?

参考答案:VIP是多变量分析的结果(考虑代谢物之间的协同作用),p值是单变量分析的结果(只看单个代谢物的组间差异)。两者互补:VIP>1说明这个代谢物在多变量模型中贡献大,p<0.05说明单独看也有显著差异。两个条件同时满足的代谢物是最可靠的差异代谢物。这叫"双筛策略"。

Q3: OPLS-DA比PLS-DA好在哪?

参考答案:OPLS-DA把变异拆成两部分:与分组相关的(预测成分)和与分组无关的(正交成分)。这样得分图更清晰,只有一个预测主成分(横轴),组间分离一目了然。而且OPLS-DA有S-plot,可以直观看到哪些代谢物既与分组高度相关又有大的协方差变化。但OPLS-DA只适合两组比较,多组还是用PLS-DA。


速查卡片

问题一句话答案
Q2多少算好?>0.5可接受,>0.7好,>0.9优秀
VIP阈值是多少?通常VIP>1表示该代谢物贡献显著
置换检验做多少次?通常200次,看原模型Q2是否显著高于随机
OPLS-DA适合多组吗?不适合,只适合两组比较
S-plot怎么看?四角(右上/左下)的点是最可靠的差异代谢物
R2Y和Q2差距大说明什么?可能过拟合,需要做置换检验确认