2. 数据质量与预处理实战

在ADMET建模中,模型的上限往往不由算法决定,而由数据质量决定。一个化合物在实验中被记录为“溶解度低”,另一个被记录为“不溶”,第三个干脆缺失该字段——如果直接把这些记录喂给模型,模型学到的不是化学规律,而是数据录入习惯。本节围绕一条真实可执行的数据预处理流水线展开:从原始ADMET数据表出发,依次完成结构标准化、重复结构去重、缺失值处理、异常值检测与数据清洗,最终得到可用于建模的干净数据集。

一、先明确“脏”在哪里:ADMET数据的典型质量问题

ADMET数据通常来自多个来源:企业内部实验记录、公开数据库(如ChEMBL、PubChem)、文献补充材料。常见问题包括:

  • 结构表示不统一:同一分子可能以SMILES、InChI、InChIKey或图片形式存在,且SMILES存在芳香性、互变异构、盐形式、立体化学等差异。
  • 重复结构:同一化合物因不同盐型、不同立体异构体或不同数据来源被重复记录。
  • 缺失值:某些端点(如hERG、CYP3A4抑制)只有部分化合物有实验值。
  • 异常值:单位换算错误、数量级错误、实验操作失误导致的极端值。
  • 单位与量纲不一致:logP、logD、pIC50、IC50(nM/μM)混用。

这些问题如果不处理,会直接导致模型在训练集上表现良好、在外部验证集上崩溃。下面给出一条可复现的预处理流程,并用一张关系图展示各步骤之间的依赖与数据流向。

ADMET数据预处理流水线:从原始记录到建模数据集 原始ADMET数据表 SMILES / 端点值 / 单位 结构标准化 去盐 / 中和 / 芳香化 重复结构去重 InChIKey / 骨架聚类 缺失值处理 删除 / 插补 / 掩码 异常值检测 Z-score / IQR / 模型残差 数据清洗 单位统一 / 量纲校正 标准化数据集 建模输入 / 适用域分析 质量报告 缺失率 / 重复率 / 异常率 异常值复核后回写或剔除 关键判断依据 • 结构标准化:以InChIKey为唯一标识,去除盐离子、中和电荷、统一芳香性表达 • 去重:同一InChIKey保留实验值最完整、单位最规范的一条;骨架聚类用于发现近似重复 • 缺失值:缺失率>40%的端点建议弃用;连续值用中位数或KNN插补,分类值单独设“未知”类 • 异常值:|Z|>3或超出1.5倍IQR范围先标记,结合化学合理性复核,不盲目删除

二、结构标准化:让同一分子只有一个“身份证”

结构标准化是后续所有步骤的基础。推荐使用RDKit完成以下操作:

from rdkit import Chem
from rdkit.Chem import SaltRemover, rdMolStandardize

def standardize_smiles(smi):
    mol = Chem.MolFromSmiles(smi)
    if mol is None:
        return None
    # 1. 去盐
    remover = SaltRemover.SaltRemover()
    mol = remover.StripMol(mol, dontRemoveEverything=True)
    # 2. 中和电荷
    uncharger = rdMolStandardize.Uncharger()
    mol = uncharger.uncharge(mol)
    # 3. 标准化互变异构体
    enumerator = rdMolStandardize.TautomerEnumerator()
    mol = enumerator.Canonicalize(mol)
    # 4. 生成规范SMILES
    return Chem.MolToSmiles(mol, isomericSmiles=True)

# 示例
print(standardize_smiles("CC(=O)[O-].[Na+]"))  # 输出 CC(=O)O

标准化后,用InChIKey作为唯一结构标识。InChIKey的前14位表示骨架连接性,后10位表示立体化学和同位素信息。对于ADMET建模,通常以前14位作为去重主键,保留立体化学信息用于后续分析。

三、重复结构去重:不只是“删掉一样的”

重复结构有三种典型情况:

  1. 完全重复:InChIKey完全相同,直接保留一条。
  2. 盐型/溶剂化物重复:标准化后InChIKey相同,但原始记录不同,保留实验值最完整的一条。
  3. 近似重复:骨架相同、取代基不同,需通过骨架聚类或相似度阈值(如Tanimoto > 0.85)识别,避免训练集与测试集泄漏。

实操中建议先按InChIKey去重,再用Butina聚类或Murcko骨架分组,检查是否存在“同一骨架多个类似物”被分到不同集合的情况。如果发现,应在划分训练/测试集时按骨架分组,而不是按分子随机划分。

四、缺失值处理:删除、插补还是掩码?

ADMET数据缺失通常不是随机的。例如,hERG实验往往只对高风险化合物做了测试,导致缺失与活性相关。处理策略取决于缺失比例和缺失机制:

缺失比例推荐策略注意事项
<5%删除缺失样本检查删除后是否引入分布偏移
5%–40%中位数/KNN/迭代插补插补后增加“是否插补”指示变量
>40%弃用该端点或单独建模避免用大量插补值训练模型
分类端点单独设“未知”类不要用众数填充后当作真实标签

对于多端点联合建模,还可以使用掩码机制:在损失函数中只计算有观测值的端点,缺失位置不参与梯度更新。这种方式比插补更保守,适合缺失比例较高的场景。

五、异常值检测:先标记,再判断

异常值检测常用三种方法:

  • Z-score:适用于近似正态分布,|Z| > 3 标记为异常。
  • IQR:适用于偏态分布,超出 Q1−1.5×IQR 或 Q3+1.5×IQR 标记。
  • 模型残差:用简单模型(如随机森林)预测,残差超过阈值标记。

关键原则是:异常值不等于错误值。例如,某个化合物的logD异常高,可能是真实的强亲脂性分子,也可能是单位写错。必须结合化学合理性复核:检查分子量、logP预测值、结构警示子,判断是否合理。只有确认是录入或单位错误时才删除,否则应保留并标记,供后续适用域分析使用。

六、数据清洗与标准化:单位、量纲与格式统一

最后一步是把所有端点值统一到可比较的尺度。常见操作包括:

  • IC50/EC50统一转换为pIC50 = −log10(IC50[M]),避免nM/μM混用。
  • logP、logD保留原始对数值,注意实验条件(pH)是否一致。
  • 分类端点统一为0/1或0/1/2(如无、弱、强)。
  • 所有数值保留一致的有效数字,避免浮点误差。
实操建议:预处理每一步都输出一份质量报告,记录原始记录数、去重后数量、缺失率、异常值数量、最终可用样本数。这份报告不仅是数据质量的凭证,也是后续适用域评估的重要输入。
常见陷阱:不要在划分训练集和测试集之后再去做缺失值插补或异常值检测,否则会引入数据泄漏。正确顺序是:先划分,再在训练集上拟合插补器和异常检测阈值,最后应用到测试集。

完成以上步骤后,得到的数据集应满足:结构唯一、单位统一、缺失值有明确处理策略、异常值有标记和复核记录。这样的数据集才能支撑后续的ADMET模型训练与适用域评估,也才能让模型真正学到化学规律,而不是数据噪声。