代谢组学多元统计(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差距大说明什么? | 可能过拟合,需要做置换检验确认 |