2. 数据准备与格式标准化:把烂摊子收拾利索

做靶点关系批次效应校准,第一步往往不是跑模型,而是跟数据死磕。我见过太多项目,一上来就调包跑校正,结果跑出来的结果一塌糊涂,最后发现原始数据里样本名都没统一。所以今天咱们先别急,把数据格式和清洗这块地基打牢。

2.1 常见数据格式:CSV、HDF5、AnnData

搞组学的人,基本离不开这三种格式。我个人习惯用 AnnData 做最终容器,但实际项目中数据源往往五花八门。先看看它们各自的脾气。

格式 特点 适用场景 痛点
CSV 文本文件,人类可读,可拖入 Excel 小型数据集、中间结果交换 行数多了打开慢;基因名里带逗号会裂开
HDF5 二进制,支持层级存储,读写快 大规模矩阵、稀疏数据存储 没有统一规范,各家存法不一样
AnnData Python 专用,内含 X、obs、var、obsm 等 单细胞全流程,下游无缝对接 scanpy R 用户调用需折腾

我的建议:项目初期先用 CSV 探路,方便肉眼检查。等确定逻辑无误后,再转成 HDF5 或 AnnData 做全量分析。记住,CSV 只适合清洗阶段,别用它跑全量校正。

2.1.1 CSV 格式——最基础,也最容坑

CSV 说白了就是个逗号分隔的表格。但我遇到过很多野生 CSV,分隔符可能是制表符、分号,甚至空格。你想想看,如果在读取时不指定分隔符,数据会乱成什么样?

# 正确读取常见的三种分隔符
import pandas as pd

# 逗号分隔
df = pd.read_csv("data.csv", sep=",")
# 制表符分隔
df = pd.read_csv("data.tsv", sep="\t")
# 分号分隔(欧洲习惯)
df = pd.read_csv("data_eu.csv", sep=";")

我曾经接手过一个公共数据集,里面有个基因名叫 “BRCA1, variant 2”。 CSV 读取时直接拆成了两列。后来我用了 quotechar='"' 才解决。所以,读取任何 CSV 前,先看两眼原文件的前几行。

2.1.2 HDF5 格式——大数据的救星

当矩阵维度超过 1 万行 x 1 万列时,CSV 基本就废了。HDF5 的优势就在于——你不需要把整个矩阵一次性载入内存。

import h5py

# 写入 HDF5
with h5py.File("expression.h5", "w") as f:
    f.create_dataset("counts", data=expression_matrix, compression="gzip")

# 读取时打地鼠,只取需要的行列
with h5py.File("expression.h5", "r") as f:
    subset = f["counts"][0:100, :]   # 只要前 100 个基因

嗯,这里要注意:HDF5 里数据集的命名一定要统一。我见过有人存成 /data/counts,也见过存成 /matrix。所以建议团队内部定一个规范,比如统一使用 /raw_counts、/normalized 这类路径。

2.1.3 AnnData 格式——单细胞标准容器

如果你做单细胞相关的工作,AnnData 基本是标配。它把表达矩阵、细胞元信息、基因元信息和降维结果都塞进了一个对象里。

import anndata as ad
import numpy as np

# 构造一个简单的 AnnData
adata = ad.AnnData(
    X=np.random.rand(100, 2000),      # 100 个细胞,2000 个基因
    obs={"cell_type": ["T cell"] * 50 + ["B cell"] * 50},
    var={"gene_symbol": [f"Gene_{i}" for i in range(2000)]}
)

print(adata)
# AnnData object with n_obs × n_vars = 100 × 2000
#     obs: 'cell_type'
#     var: 'gene_symbol'

对于批次效应校准来说,AnnData 的好处是:你可以在同一个对象里保留多个批次的 obs['batch'] 列,后续校正函数(如 Harmony、scVI)直接读取该列即可。

关键思路:不管原始数据是什么格式,最终都要收拢到 AnnData 或 SeuratObject(R 用户)中。标准化就是为了让所有数据源都长成一个模样,后续步骤才能无痛对接。

2.2 数据清洗与统一命名——不做这件事,后面全白搭

数据清洗不是体力活,是脑力活。我经常跟团队说:清洗数据的时间,至少占整个项目时间的 40%。别嫌多,少了更麻烦。

2.2.1 样本名与批次标识标准化

不同来源的数据,样本命名风格千奇百怪。有的用 Sample_001,有的用 S1,还有的用 patient-01_visit-2。如果不统一,后续合并时会直接裂开。

# 目标:统一为 project_batch_sampleID
def standardize_sample_name(old_name, project="P001", batch="batch1"):
    # 去掉奇怪的空格和特殊字符
    clean = old_name.strip().replace(" ", "_")
    return f"{project}_{batch}_{clean}"

print(standardize_sample_name(" Sample 01 ", "P001", "batch1"))
# 输出:P001_batch1_Sample_01

你想想看,如果样本名是 Sample_01,但在另一个文件里变成了 sample-01,合并时就会多出一倍的行数。这就是典型的命名不统一导致的重复或缺失。

2.2.2 基因名与特征标识去重

基因名里也经常有坑。有些数据集的基因名是 ENSEMBL ID,有些是 SYMBOL,还有些是 ENTREZ ID。更麻烦的是——同一个基因可能出现多次(比如不同转录本)。

# 去重策略:保留表达量均值最高的那个
import pandas as pd

# 假设 df 的索引是基因名,有多行同名基因
df_unique = df.groupby(df.index).mean()
# 如果是单细胞数据,建议保留求和或保留最大值
df_unique = df.groupby(df.index).sum()

我曾经在肺癌数据里遇到过基因名 MIR-21 和 miR-21 共存。如果不做大小写和连字符的标准化,校正时这两个会被当作不同基因处理,结果就是信号被稀释。所以建议统一转为大写且去掉连字符。

2.2.3 缺失值处理——别让 NA 毁了你的模型

批次效应校准算法大多对缺失值敏感。你直接用原始 NA 矩阵输入 Harmony,大概率会报错或产出垃圾结果。

处理策略分两步走:

  1. 基因层面:如果一个基因在超过 50% 的样本里是 NA,直接丢掉。
  2. 细胞/样本层面:如果缺失率低(<5%),用该基因的中位数填充。
import numpy as np

# 删除缺失率 > 50% 的基因
valid_genes = np.sum(np.isnan(expression_matrix), axis=0) < 0.5 * expression_matrix.shape[0]
expression_matrix = expression_matrix[:, valid_genes]

# 中位数填充(按基因为单位)
gene_medians = np.nanmedian(expression_matrix, axis=0)
for i in range(expression_matrix.shape[1]):
    mask = np.isnan(expression_matrix[:, i])
    expression_matrix[mask, i] = gene_medians[i]

一个小技巧:填充后,最好记录一下每个基因的填充比例。如果某个基因的填充比例超过 10%,建议在后续分析中给它一个较低的权重,或者干脆报告出来。

2.3 格式统一与标准化输出

清洗完成后,所有数据必须输出为同一个格式。我建议直接使用 AnnData 作为最终输出,因为它自带批次标识位,且能被主流校正工具直接食用。

import anndata as ad
import pandas as pd

# 假设已经清洗完毕
cleaned_matrix  # numpy array, shape: (cells, genes)
cell_metadata   # DataFrame, 必含 'batch' 列
gene_metadata   # DataFrame, 必含 'gene_symbol' 列

# 打包成 AnnData
adata_final = ad.AnnData(
    X=cleaned_matrix,
    obs=cell_metadata,
    var=gene_metadata
)

# 保存到磁盘
adata_final.write("standardized_pipeline_input.h5ad")

这就是我们接下来所有步骤的起点。你把这个文件交给任何下游算法,它们都能正常理解。

记住:批次效应校准的成败,大半取决于数据准备阶段。数据干净了,一个简单的线性校正可能就够了;数据垃圾,再厉害的深度网络也救不回来。

下面这张图总结了本章的核心流程:从原始数据格式到标准化 AnnData 的完整路径。

数据准备与标准化流程 CSV 格式 HDF5 格式 AnnData 数据清洗与统一命名 样本名标准化 | 基因名去重 | 缺失值填充 标准化 AnnData 文件 任何输入格式,最终归一到同一套命名规则与结构

从 CSV、HDF5 或现成的 AnnData 出发,经过样本名、基因名和缺失值的清洗,最终打包成一份标准化的 AnnData 对象。这就是流水线的心脏——数据统一接口。把这个接口定好了,后面的步骤就是换不同的校正算法罢了。