109

ProLIF:把相互作用变成可分析的指纹

ProLIF 把相互作用编码成指纹,可用于批量比较与轨迹分析。这篇讲清指纹的构建、MD 占有率分析与筛选应用。

ProLIF(Protein-Ligand Interaction Fingerprints)与 PLIP 的区别在于定位:PLIP 生成人读的报告,ProLIF 生成机器可分析的指纹。这个差别让 ProLIF 特别适合批量比较MD 轨迹分析

什么是相互作用指纹

# 把「配体与蛋白形成了哪些相互作用」编码成向量
#
# 结构:
#   每个「残基 × 相互作用类型」组合是一个位
#   有该相互作用 = 1,没有 = 0
#
# 例:
#   ASP86.Hydrophobic     = 0
#   ASP86.HBAcceptor      = 1
#   MET93.Hydrophobic     = 1
#   MET93.HBDonor         = 1
#   PHE80.PiStacking      = 1
#   ...
#
# 用途:
#   1) 比较两个姿势的结合模式是否相同
#      → 计算 Tanimoto 相似度
#   2) 对大量对接结果聚类
#      → 找出有几种不同的结合模式
#   3) 分析 MD 轨迹中相互作用的稳定性
#      → 每一帧算一次,得到「占有率」
#   4) 作为机器学习的特征
#
# 关键优势:
#   指纹是定长向量 → 可以直接做数值计算
#   而 PLIP 的文本报告需要解析

基本用法

pip install prolif

import prolif as plf
import MDAnalysis as mda

# 从 PDB + SDF 分析对接结果
u = mda.Universe("receptor.pdb")
protein = plf.Molecule.from_mda(u.select_atoms("protein"))

poses = list(plf.sdf_supplier("docked_poses.sdf"))

fp = plf.Fingerprint([
    "Hydrophobic", "HBDonor", "HBAcceptor",
    "PiStacking", "Anionic", "Cationic",
    "CationPi", "PiCation", "XBDonor", "MetalAcceptor",
])
fp.run_from_iterable(poses, protein)

df = fp.to_dataframe()
print(df)
# 行 = 姿势,列 = (残基, 相互作用类型) 的多级索引

# 转成位向量做相似性计算
bv = fp.to_bitvectors()
from rdkit import DataStructs
sim = DataStructs.TanimotoSimilarity(bv[0], bv[1])
print(f"姿势 0 与 1 的相互作用相似度: {sim:.3f}")

MD 轨迹分析:占有率

# 这是 ProLIF 最有价值的应用
#
# 静态结构只告诉你「某一瞬间有什么相互作用」,
# 而 MD 告诉你「这个相互作用有多稳定」

import MDAnalysis as mda
import prolif as plf

u = mda.Universe("topology.pdb", "trajectory.dcd")
ligand = u.select_atoms("resname LIG")
protein = u.select_atoms("protein and byres around 12 group lig",
                         lig=ligand)

fp = plf.Fingerprint()
fp.run(u.trajectory[::10], ligand, protein)   # 每 10 帧分析一次

df = fp.to_dataframe()

# 计算每个相互作用的占有率
occupancy = df.mean() * 100
occupancy = occupancy[occupancy > 0].sort_values(ascending=False)
print(occupancy.head(20))

# 判读:
#   占有率 > 80%   稳定的核心相互作用 → 设计中必须保住
#   占有率 40~80%  中等稳定
#   占有率 < 20%   瞬时接触 → 在静态结构中看到可能是偶然
#
# 这解决了一个真实问题:
#   共晶结构中看到的某个氢键,可能只是晶体中的
#   一个构象快照,在溶液中并不稳定
#   → 基于它做设计会失败

# 可视化
fp.plot_barcode()      # 时间轴上的相互作用条形码
fp.plot_lignetwork(poses[0], kind="frame")   # 交互式网络图

用于对接结果的筛选

# 场景:有 5000 个对接姿势,想筛出
#      「复现了关键相互作用」的那些

import prolif as plf
from rdkit import DataStructs

# 1) 先分析共晶配体,得到参考指纹
ref_pose = list(plf.sdf_supplier("crystal_ligand.sdf"))
fp_ref = plf.Fingerprint()
fp_ref.run_from_iterable(ref_pose, protein)
ref_bv = fp_ref.to_bitvectors()[0]
ref_df = fp_ref.to_dataframe()

# 2) 找出参考中的关键相互作用(如铰链区)
key_interactions = [
    ("MET793.A", "HBAcceptor"),   # 激酶铰链区的典型氢键
    ("MET793.A", "HBDonor"),
]

# 3) 批量分析对接姿势
fp_dock = plf.Fingerprint()
fp_dock.run_from_iterable(docked_poses, protein)
dock_df = fp_dock.to_dataframe()
dock_bv = fp_dock.to_bitvectors()

# 4) 两种筛选标准
#    (a) 整体相似度
sims = [DataStructs.TanimotoSimilarity(ref_bv, bv) for bv in dock_bv]

#    (b) 硬性要求:必须有某个关键相互作用
def has_key(row):
    return any(row.get(k, False) for k in key_interactions
               if k in dock_df.columns)

keep = [i for i, (s, (_, row)) in enumerate(zip(sims, dock_df.iterrows()))
        if s > 0.5 and has_key(row)]

print(f"保留 {len(keep)}/{len(dock_bv)} 个姿势")

# 这是【独立于打分函数】的筛选维度 —— 价值很高(见 096)

ProLIF 与 PLIP 的选择

PLIP ProLIF
输出 人读报告 DataFrame / 位向量
单结构详细分析 更好(报告详尽) 可以
批量比较 需自己解析 原生支持
MD 轨迹 不直接支持 原生支持
与 RDKit 集成
水桥检测 支持 需额外配置
可视化 PyMOL 会话 网络图、条形码

实践中两者常配合使用:PLIP 做单个复合物的深入解读,ProLIF 做批量与轨迹分析。

使用注意

  • 需要正确的氢原子:氢键判断依赖氢的位置,输入结构必须已加氢(见 105《PDBFixer》);
  • MDAnalysis 的选择语法要写对:选错原子组会得到空结果或错误结果,先打印选中的原子数确认
  • 轨迹分析的帧间隔:全帧分析很慢,通常每 10~50 帧取一帧就够;
  • 指纹的可比性:不同体系的指纹长度与列不同,跨体系比较需要对齐列;
  • 相互作用定义可调plf.Fingerprint 可以传入自定义参数调整距离/角度阈值。

关键要点

  • 指纹是定长向量,可直接做相似度计算、聚类与机器学习特征;
  • MD 占有率分析是它最有价值的能力——揭示共晶中看到的相互作用是否真的稳定;
  • 用「必须复现关键相互作用」筛选对接结果,是独立于打分函数的判据;
  • PLIP 做单结构深入解读,ProLIF 做批量与轨迹分析,两者配合使用。

延伸资源