数据准备:输入数据格式要求
做ComBat校正,最磨人的往往不是算法本身,而是数据准备。我见过太多人把时间花在调参上,结果数据格式一塌糊涂,跑出来的结果根本没法用。说白了,这一步要是没搞对,后面全是白干。
这一章我们就来聊聊,ComBat到底需要什么样的输入数据。别小看这个,格式对了,后面的活儿能省下一半时间。
核心输入:基因表达矩阵
ComBat的核心输入就是一个矩阵。你可以把它想象成一张大表——行是基因(或者探针、蛋白、代谢物),列是样本。
我个人习惯把表达矩阵存成一个CSV或TSV文件,行名是基因名,列名是样本名。举个例子:
Gene_Symbol,Sample1,Sample2,Sample3,...
TP53,12.3,11.8,15.2,...
BRCA1,8.9,9.2,7.5,...
EGFR,5.6,5.1,6.8,...
...
关键点看这里:
- 数值类型必须是连续型——像是log2表达量、FPKM、TPM这些都行。计数数据(比如原始read count)就别直接往里扔了,ComBat是为连续数据设计的。
- 不要有空值——我曾经一次项目里,有个基因在某个批次里全是NA,ComBat直接报错。后来我用均值补了,结果还是把那个批次搞得一塌糊涂。所以要么补全,要么删掉。
- 数据最好已经做过标准化——ComBat不负责帮你归一化。我建议至少做完log2转换或quantile normalization再输入。
批次标签:怎么告诉ComBat哪个样本属于哪个批次
表达矩阵之外,你还得给出一份批次标签信息。说白了就是一张“样本-批次对照表”。
格式大概是这样的:
Sample_ID,Batch
Sample1,Batch1
Sample2,Batch1
Sample3,Batch2
Sample4,Batch2
Sample5,Batch3
...
你想想看,如果这个表搞错了,比如样本2和样本3的批次标反了,ComBat会做什么?它会错误地把批次间的差异当成真实生物学变异去校正,结果就是你把好的数据洗坏了。
我个人习惯的做法是:在准备数据阶段,先画一个箱线图或者PCA图,看看样本是否真的按批次聚类。如果肉眼都能看出批次效应,那这个标签大概率是对的。
特征注释(可选但推荐)
ComBat还有一个进阶输入——特征注释。这个不是必须的,但我建议你加上。
典型用途是:你想告诉ComBat,“这批探针是基因,那批探针是对照,校正时不要把对照弄乱了”。
格式同样简单:
Probe_ID,Type
A_01_P000001,gene
A_01_P000002,gene
A_01_P000003,control
...
如果不提供这个,ComBat默认把所有特征当成一类来校正。大多数场景下这没问题,但如果你手上有对照探针或spike-in,那就别省这一步了。
数据质量要求:别拿垃圾数据糊弄ComBat
嗯,这里要注意:ComBat不是魔法。你再好的算法,输入一堆垃圾数据,结果也是垃圾。
数据底线要求:
- 每个批次至少要有3个样本(越多越好,我建议每个批次至少5个)
- 不能有全零行或全NA行
- 数据分布不要严重偏态(比如未log2的原始count数据)
- 不同批次间的基因数量必须一致
我曾经在一个多中心项目中遇过——某个中心只提供了2个样本。ComBat跑是能跑,但校正完这批样本就像被“吸”到了其他批次一样,完全失真了。所以2个样本的批次,要么合并到其他批次,要么就直接剔除吧。
一张图看清整个数据准备流程
下面这个SVG图总结了数据准备的关键节点和它们之间的关系:
常见数据格式问题排查
我把这些年在项目中踩过的坑整理了一下,做成一个速查表,你直接对着检查就好:
| 问题类型 | 表现 | 我的处理方式 |
|---|---|---|
| 基因名重复 | 同一基因多行,ComBat报错 | 取均值或中位数合并,或按表达量保留最高的一行 |
| 样本名与批次表不匹配 | ComBat运行后样本顺序错乱 | 强制对齐:match(colnames(expr), batch$Sample_ID) |
| 批次标签是数值 | ComBat把批次当成连续变量处理 | 转为因子:batch$Batch <- as.factor(batch$Batch) |
| 表达矩阵有负值 | 部分模型参数计算异常 | 检查是否做过log2转换,某些归一化会残留负值 |
一个小技巧:
在把数据喂给ComBat之前,先做一个dim(expr)和table(batch),确认维度一致,批次样本数合理。这个习惯救了我好几次。
代码里的数据格式示例
如果你用R,最经典的方式是这样读取数据:
# 读取表达矩阵
expr_matrix <- as.matrix(read.csv("expression_data.csv", row.names = 1))
# 读取批次信息
batch_info <- read.csv("batch_labels.csv")
batch_info$Batch <- as.factor(batch_info$Batch)
# 检查样本顺序是否一致
all(colnames(expr_matrix) == batch_info$Sample_ID)
# 如果不一致,就重新排序
batch_info <- batch_info[match(colnames(expr_matrix), batch_info$Sample_ID), ]
如果你用Python,通常是用pandas处理:
import pandas as pd
# 读取表达矩阵
expr_df = pd.read_csv("expression_data.csv", index_col=0)
# 读取批次信息
batch_df = pd.read_csv("batch_labels.csv")
# 按样本名对齐批次标签
batch_series = batch_df.set_index("Sample_ID").loc[expr_df.columns, "Batch"]
代码本身没什么高深的,但样本顺序对齐这一步,我至少见过5个人在这上面翻车。ComBat对输入顺序敏感——批次标签的顺序必须和表达矩阵的列顺序严格一致。
嗯,说到这里,其实数据准备的核心就这些了。总结一下就是:表达矩阵+批次标签+特征注释(可选),格式要对,顺序要对,质量要对。做到这三条,ComBat就能跑得很顺畅。