3、ADMET 数据中的批次效应识别:箱线图、变异系数、批次相关性矩阵

好,咱们进入实操环节。上一章聊了批次效应是什么、它怎么来的,这一章直接上手——怎么把它揪出来。你手头可能已经攒了几百个化合物的 ADMET 数据,做了好几批实验。这时你第一反应应该是:这批数据里,有没有“批次烙印”?

我个人习惯,不会一上来就套复杂模型。先用三种简单粗暴的方法扫一遍:箱线图看分布、变异系数看波动、相关性矩阵看亲疏。这三个工具组合起来,基本能把批次效应的“鬼影”照出来。下面一个个拆开讲。

3.1 箱线图:让分布“现原形”

箱线图,说白了就是把数据的“五个数”画出来——最小值、下四分位数、中位数、上四分位数、最大值。如果不同批次的箱体位置、箱体宽度差别很大,那八成是有批次效应。

我在项目中遇到过这样的情况:某公司做了一批 CYP 抑制实验,前两批数据的中位数高度重合,第三批的中位数突然跳了 30%。嗯,一看实验记录,第三批用的试剂盒批号不同。箱线图一眼就逮住了它。

怎么用 Python 画?我一般用 seaborn 的 boxplot。来看代码:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 加载你的 ADMET 数据
df = pd.read_csv("admet_data.csv")

# 只看某个关键指标,比如 Caco-2 渗透性
sns.boxplot(data=df, x='batch', y='caco2_permeability')
plt.title('Caco-2 渗透性按批次分布')
plt.show()

画出来以后怎么看关键点?三条线记一下:

  • 中位数线:批次间中位数差异 > 20%,就要警惕了。
  • 箱体高度(IQR):某个批次箱体异常宽,说明该批实验的变异度失控。
  • 离群点:如果某一批离群点扎堆,也是批次效应的信号。
我的小习惯:我会把多个 ADMET 指标(比如溶解度、血浆蛋白结合率、CYP 抑制)的箱线图排成一排,用 subplots 统一看。这样能快速评估批次效应是全局性的还是某个指标独有。

3.2 变异系数:量化“一致性”

箱线图靠眼力,那有没有一个数字能直接告诉我批次内的一致性好不好?有。用变异系数(CV)。

公式很简单:CV = (标准差 / 均值) × 100%。CV 越大,说明该批数据的相对波动越大。不同批次的 CV 如果差距悬殊,那批次效应就跑不掉了。

为什么用 CV 而不用标准差?因为不同指标的尺度不一样。比如 Caco-2 渗透性可能是 1-10 ×10⁻⁶ cm/s,而 LogD 可能是 0-5。直接比标准差没意义,用 CV 就把尺度归一化了。

看代码实现:

def calc_cv_by_batch(df, metric_col, batch_col='batch'):
    """按批次计算变异系数"""
    cv_dict = {}
    for batch in df[batch_col].unique():
        batch_data = df[df[batch_col] == batch][metric_col]
        cv = (batch_data.std() / batch_data.mean()) * 100
        cv_dict[batch] = round(cv, 2)
    return cv_dict

# 计算 Caco-2 渗透性各批次的 CV
cv_result = calc_cv_by_batch(df, 'caco2_permeability')
print(cv_result)
# 输出示例:{'Batch1': 12.34, 'Batch2': 11.87, 'Batch3': 28.56}

你想想看,如果 Batch3 的 CV 是 28.56%,而前两批只有 12% 左右,这到底意味着什么?意味着 Batch3 的数据内部一致性差了将近 2.5 倍。用它去做模型训练,模型对这批数据的泛化能力肯定打折扣。

避坑指南:我曾经碰到过一个坑——某个 ADMET 指标的均值非常接近 0(比如某些化合物的 LogD 接近 0)。这时候 CV 会变得极大甚至无穷大,因为分母太小。处理方法:对于均值低于检测限的指标,不要用 CV,改用“批次内中位数绝对偏差(MAD)”来评估。

一般来说,CV 的判定标准可以这样参考:

CV 范围 一致性等级 建议动作
< 10% 优秀 不需要处理
10% - 20% 良好 留意观察,暂不处理
20% - 30% 临界 建议排查实验条件差异
> 30% 差 必须进行批次校正或剔除异常批次

3.3 批次相关性矩阵:看清“亲疏关系”

箱线图看分布,CV 看波动,那怎么判断不同批次的样本在 ADMET 空间里到底“像不像”?相关性矩阵最直观。

我会把每个批次的数据当成一个“整体”,计算批次之间的皮尔逊相关系数。如果同一化合物在不同批次之间的 ADMET 剖面高度相关(比如 r > 0.9),说明批次间一致性不错。如果相关系数只有 0.6、0.7,那就说明存在明显的批次效应。

代码写起来也不复杂:

import numpy as np

# 按批次和化合物 ID 重新整理数据
pivot_df = df.pivot_table(index='compound_id', 
                          columns='batch', 
                          values='caco2_permeability')

# 计算批次间相关性矩阵
corr_matrix = pivot_df.corr(method='pearson')
print(corr_matrix)

# 用热图可视化
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', vmin=0.5, vmax=1.0)
plt.title('批次间相关性矩阵')
plt.show()

怎么读这个矩阵?我一般按三步走:

  1. 看对角线:必须都是 1.0(同一批跟自己完全相关)。
  2. 看非对角线:值越高越好,一般要求 > 0.85。
  3. 找“离群批次”:比如 Batch1 和 Batch2 的相关系数是 0.95,但 Batch1 和 Batch3 只有 0.72。那 Batch3 就是“有问题”的那一个。
实战经验:有一次我给某合作方做数据审计,发现他们的 Batch5 和所有其他批次的相关系数都低于 0.6。一查实验 SOP,原来 Batch5 换了新的 LC-MS 柱子。相关性矩阵直接在报告里成了“铁证”。

3.4 三种方法联用:三步判定法

好,现在你有三个工具了。不要一个一个孤立地用,要串联起来形成一个快速判定流程。我总结了一个“三步走”套路:

下面这张图把流程串起来,你可以对着它用:

批次效应识别三步判定流程 Step 1: 箱线图扫视 Step 2: CV 量化 Step 3: 相关性确认 中位数 差异 >20%? CV >20% ? r <0.85 ? 批次效应倾向高 需进一步排查 一致性可接受

你拿着这张图,对着数据跑一遍流程:

  • 箱线图发现中位数差异大 → 进入第 2 步
  • CV 也偏高(>20%)→ 进入第 3 步
  • 相关性矩阵确认(r < 0.85)→ 判定批次效应存在
实际场景建议:如果你手上有 500 个化合物、5 个 ADMET 指标、3 个批次,建议写个自动化脚本每天跑一遍 CV 和相关性矩阵,输出“批次健康报告”。我当年就是这么干的——每天早上花 2 分钟看一眼报告,有没有批次效应苗头一清二楚。

好了,三种方法讲完了。方法论有了,下一件事就是怎么从数据层面去“修正”这些效应,但那是后面章节的事了。先安心把这三种识别工具用起来,你手头的数据质量就能有一个清晰的诊断。