4. 探索性数据分析:可视化批次效应归纳总结
拿到一批多批次数据,第一件事千万别直接建模。我个人的习惯是:先看分布,再谈调参。批次效应藏得很深,光靠数值统计不一定能揪出来。可视化反而最直观,一眼就能看出哪些批次在「抱团」,哪些样本像「孤儿」一样飘在外面。
4.1 为什么要先可视化?
说白了,可视化能让你在五分钟内回答三个关键问题:
- 批次之间有没有明显的分离?
- 有没有某个批次的方差特别大?
- 技术偏差和生物学变异,哪个主导了数据的主轴?
我记得有一次,一个同事跑完校正算法,只看指标发现P值好看了,结果我帮他画了个PCA图——校正后批次被强行揉在一起,但生物学信号也被压平了。所以他后面做分类一直过拟合。所以说,可视化不是「锦上添花」,是「上路之前先看导航」。
batch_effect_{method}_{before|after}.png。
4.2 主流可视化方法对比
不同方法各有侧重。下面这个表格是我自己总结的,帮你快速选工具。
| 方法 | 目标 | 适用场景 | 注意事项 |
|---|---|---|---|
| PCA(主成分分析) | 降维 + 看整体簇结构 | 高维数据初探,批次效应明显时一眼可见 | 对异常值敏感,需要先做标准化 |
| t-SNE | 保留局部邻域结构 | 发现细分子群,适合展示校正后效果 | 随机性大,不能跨运行比较;困惑度参数需要调 |
| UMAP | 平衡全局与局部结构 | 大规模数据,比 t-SNE 快很多 | 超参数 n_neighbors 影响全局/局部权衡 |
| 箱线图 + 密度图 | 检查单个基因/特征的分布偏移 | 小规模特征集,或验证某个可疑基因 | 只能展示一维,多特征需要堆叠 |
4.3 实操:用 PCA 快速捕捉批次效应
下面这组代码是我实际项目中用的模板。假设数据是 X(样本×特征),batch_labels 是批次标签。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
def plot_pca_batch(X, batch_labels, title="PCA - Batch Effect"):
# 标准化:每个特征零均值单位方差
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 降维到2D
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 计算解释方差比例
explained = pca.explained_variance_ratio_
# 绘图
plt.figure(figsize=(8, 6))
unique_batches = np.unique(batch_labels)
colors = plt.cm.tab10(np.linspace(0, 1, len(unique_batches)))
for i, batch in enumerate(unique_batches):
mask = batch_labels == batch
plt.scatter(X_pca[mask, 0], X_pca[mask, 1],
c=[colors[i]], label=f'Batch {batch}',
alpha=0.7, edgecolors='k', linewidth=0.5)
plt.xlabel(f'PC1 ({explained[0]:.1f}%)')
plt.ylabel(f'PC2 ({explained[1]:.1f}%)')
plt.title(title)
plt.legend()
plt.grid(True, linestyle='--', alpha=0.4)
plt.tight_layout()
plt.show()
# 使用示例
# plot_pca_batch(X, batch_labels)
运行后,如果不同颜色的点各自聚成一团,那就说明批次效应很明显。如果所有颜色混在一起,恭喜你,数据本身一致性不错。
4.4 可视化流程总结
下面这张图是我梳理的标准操作流程,每次做批次效应分析时都照着走一遍。
你想想看,如果没有这个流程,你可能会跳过标准化步骤——那PCA的结果就会被方差最大的基因带偏,批次效应反而被夸大了。标准化虽然简单,但这一步我见过无数次翻车。
4.5 深入:一维分布检查
降维图能看到整体趋势,但有时候你会怀疑某个基因的表达量在批次间整体偏移了。这时候我习惯画个并列箱线图。
import seaborn as sns
def plot_gene_batch_boxplot(X, batch_labels, gene_idx, gene_name):
df = pd.DataFrame({
'expression': X[:, gene_idx],
'batch': batch_labels
})
plt.figure(figsize=(8, 4))
sns.boxplot(x='batch', y='expression', data=df, palette='Set2')
plt.title(f'Gene {gene_name} expression across batches')
plt.tight_layout()
plt.show()
有一次,我遇到一个基因在批次2里整体比批次1高了0.5个log倍,但校正算法没把它拉回来,导致后续差异分析出现了假阳性。箱线图一出,立马发现——所以要结合多维和一维,老司机都懂。
4.6 什么时候该相信可视化?
可视化也不是万能的。我个人经验里,有几个场景要特别小心:
- 样本量太小(每批次少于5个):PCA会过度拟合,点之间距离没有统计意义。
- 存在离群样本:一个离群点可能把两个批次「拉」到一起,造成批次效应被掩盖。
- 维度爆炸(特征数远大于样本数):先做特征筛选或使用正则化PCA(比如scikit-learn的PCA默认不做奇异值截断,要注意)。
总结一句话: 可视化是你的眼睛,但别只用一只眼。多角度、多方法交叉验证,再决定要不要上校正算法。
好了,这一章的内容就是这些。接下来你可以用上面的代码和流程,在你自己数据上试试。记得先跑一遍标准化,再画PCA,看看批次之间是不是「井水不犯河水」。如果是,那就省事了;不是的话,后面的校正章节会帮你解决。