
本文介绍在高维稀疏医学数据(如含5300列代谢物、200例样本且缺失值密集)中正确实施pca的方法,重点解析跳过完整样本删除、支持成对有效观测的协方差矩阵构建策略,并提供可直接运行的numpy手动实现代码。
在医学代谢组学研究中,PCA常被用于降维与疾病分型探索——例如区分疾病1(D1)、疾病2(D2)及健康对照。但现实数据(如您描述的Excel中200行×5300列代谢物时序AUC值)往往存在大量缺失:不同患者检出的代谢物种类与时间点差异显著,导致矩阵高度稀疏。此时若机械套用sklearn.PCA,会因默认拒绝NaN而报错;若盲目填充(如均值/中位数插补)或直接删列,则可能引入偏差、丢失关键生物信号,甚至破坏代谢物间的生理相关性。
核心原则:PCA的本质是协方差矩阵的特征分解,而非逐样本线性变换
标准PCA仅依赖变量两两之间的协方差 $ \text{Cov}(X_i, Xj) = \frac{1}{n{ij}} \sum{k: x{ki},x{kj}\text{ observed}} x{ki}x{kj} $,其中 $ n{ij} $ 是同时观测到变量 $ i $ 和 $ j $ 的样本数。这意味着:
✅ 不要求任一患者具备全部5300项代谢物数据;
✅ 只需对每一对代谢物 $ (i,j) $,存在足够多(如 ≥30)共同有效观测的患者即可可靠估计协方差;
❌ 不能简单删除含缺失的整行(将损失全部200例样本),也不应删除高缺失率的代谢物列(可能剔除关键生物标志物)。
推荐实践路径:基于成对有效观测的手动协方差矩阵构建
以下代码展示如何在含海量缺失值(如40%元素为NaN)时稳健计算PCA主成分(无需任何插补或行删除):
import numpy as np
from sklearn.decomposition import PCA
# 假设 data 是您的原始数组 (200, 5300),含 np.nan 表示缺失
# Step 1: 构建成对有效计数矩阵 n_ij
mask = ~np.isnan(data) # True表示有效值
# 利用广播生成三维布尔矩阵:mask[k,i] & mask[k,j] 对所有k,i,j
valid_pairs = mask[:, :, None] & mask[:, None, :] # shape (n_samples, n_features, n_features)
n_ij = valid_pairs.sum(axis=0) # shape (n_features, n_features), 每个元素为对应变量对的有效样本数
# Step 2: 计算加权协方差矩阵(忽略NaN,仅用有效乘积求和)
data_filled = np.where(mask, data, 0) # 将NaN替换为0,便于向量化乘法
sum_products = data_filled.T @ data_filled # sum over samples: Σ_k x_ki * x_kj
cov_matrix = sum_products / n_ij # 逐元素除以对应n_ij
# Step 3: 特征分解获取主成分
eigenvals, eigenvecs = np.linalg.eigh(cov_matrix) # eigh更稳定,返回升序特征值
# 逆序排列以获得最大方差主成分在前
eigenvals = eigenvals[::-1]
eigenvecs = eigenvecs[:, ::-1]
# Step 4: 投影原始数据(对每行样本,仅使用其有效特征计算得分)
def project_to_pcs(X, components, n_features):
"""安全投影:对每样本,仅用其非NaN特征加权求和"""
scores = np.zeros((X.shape[0], components.shape[1]))
for i in range(X.shape[0]):
mask_i = ~np.isnan(X[i])
if mask_i.sum() > 0:
scores[i] = (X[i][mask_i] @ components[mask_i]).T
return scores
# 示例:投影到前2个主成分
X_pca = project_to_pcs(data, eigenvecs[:, :2], data.shape[1])关键注意事项:
- 验证 $ n_{ij} $ 的充分性:执行 print(np.min(n_ij)) 确保最小有效对样本数 ≥ 20–30(小样本下建议≥50)。若存在 $ n_{ij}
- 避免中心化陷阱:传统PCA需先对每列去均值,但缺失值使均值估计不可靠。上述方法隐含“以0为中心”的假设(因NaN置0),对代谢物AUC这类非负数据合理;若需严格中心化,可改用迭代SVD或专用库(如fancyimpute中的IterativeSVD);
- 结果解释优先级:关注前2–3个主成分的累计方差贡献率(eigenvals[:3].sum()/eigenvals.sum()),结合载荷向量(eigenvecs[:, 0])识别驱动分离的关键代谢物及时序组合;
- 替代方案提示:若协方差矩阵病态(条件数 > 1e6),可添加微小正则项 cov_matrix += 1e-8 * np.eye(cov_matrix.shape[0]) 或改用核PCA增强非线性模式捕捉。
综上,面对高缺失率医学数据,放弃“完美数据”执念,回归PCA的数学本源——协方差驱动的线性子空间发现,辅以向量化成对统计,即可在信息不完整约束下释放PCA的强大表征能力。










