040

Scaffold 骨架:药物化学为什么重视母核

骨架是药物化学组织分子的核心概念。这篇讲清骨架的定义、提取方法与在实际工作中的用途。

骨架(scaffold)是分子的「核心结构」——去掉取代基后剩下的部分。药物化学围绕骨架组织工作:一个项目通常是「选定一个骨架,然后优化它的取代基」。理解这个概念,才能理解药物发现的实际工作方式。

为什么骨架重要

用途 说明
组织化学系列 同一骨架的分子构成一个「系列」
数据划分 骨架划分是评测的关键(见 051《Scaffold Split》
专利空间 专利通常围绕骨架撰写(Markush 结构)
骨架跃迁 换骨架保持活性(见 352《Scaffold Hopping 实战》
库设计 保证骨架多样性(见 053《分子多样性评估》
SAR 分析 固定骨架,比较取代基(见 042《R-group Decomposition》
去冗余 避免采购一堆同骨架的分子

骨架的不同定义层次

from rdkit import Chem
from rdkit.Chem.Scaffolds import MurckoScaffold

smiles = "CC(=O)Nc1ccc(OC(C)C)cc1"
mol = Chem.MolFromSmiles(smiles)

# ---- 第 1 层:Bemis-Murcko 骨架 ----
#   保留:所有环 + 连接环的链
#   去掉:末端的取代基
scaffold = MurckoScaffold.GetScaffoldForMol(mol)
print(Chem.MolToSmiles(scaffold))
# 【最常用的定义】

# ---- 第 2 层:通用骨架(generic)----
#   进一步把所有原子变成碳、所有键变成单键
#   → 【只保留拓扑形状】
generic = MurckoScaffold.MakeScaffoldGeneric(scaffold)
print(Chem.MolToSmiles(generic))
# 苯环与吡啶环 → 【视为同一个骨架】
# 【用于更严格的划分或更宽松的分组】

# ---- 第 3 层:只保留环系 ----
#   去掉连接链,只留独立的环
def ring_systems(mol):
    ri = mol.GetRingInfo()
    systems = []
    for ring in ri.AtomRings():
        systems.append(set(ring))
    # 合并共享原子的环(稠环)
    merged = []
    for s in systems:
        found = False
        for m in merged:
            if s & m:
                m |= s
                found = True
                break
        if not found:
            merged.append(set(s))
    return merged

# 【选择哪一层?】
#   数据划分 → Bemis-Murcko(标准)或 generic(更严格)
#   SAR 分析 → Bemis-Murcko
#   多样性评估 → generic(关注拓扑多样性)
#   骨架跃迁 → 需要跨越 generic 骨架才算真正的跃迁

实际使用中的问题

# 【问题一:线性分子没有骨架】
mol = Chem.MolFromSmiles("CCCCCCCC")
scaf = MurckoScaffold.MurckoScaffoldSmiles(mol=mol)
print(repr(scaf))     # ''(空字符串)
#
# 【后果】:
#   骨架划分时,所有无环分子被归为同一「骨架」
#   → 【如果数据集中无环分子多,这会造成问题】
#   → 应对:单独处理,或用其它划分方式

# 【问题二:骨架可能过于笼统】
#   很多不同的分子共享同一个简单骨架(如苯环)
#   → 【骨架划分的「严格程度」不如预期】
#
#   检查方法:
from collections import Counter

def scaffold_distribution(smiles_list):
    counter = Counter()
    for s in smiles_list:
        m = Chem.MolFromSmiles(s)
        if m is None:
            continue
        counter[MurckoScaffold.MurckoScaffoldSmiles(mol=m)] += 1
    print(f"分子数: {sum(counter.values())}")
    print(f"骨架数: {len(counter)}")
    print(f"最大骨架包含 {counter.most_common(1)[0][1]} 个分子")
    print(f"单例骨架(只有 1 个分子): "
          f"{sum(1 for c in counter.values() if c == 1)}")
    return counter

# 【判读】:
#   骨架数 / 分子数 越接近 1 → 多样性高
#   有一个骨架占了很大比例 → 数据集偏向某个系列

# 【问题三:骨架划分不等于足够严格】
#   两个不同的骨架可能仍然很相似
#   (如苯并咪唑 vs 苯并噻唑)
#   → 【更严格的做法】:
#     - 用 generic 骨架
#     - 或用聚类划分(见 050)
#     - 或检查测试集与训练集的最大相似度(见 167)

# 【问题四:立体化学】
#   默认的骨架提取会保留立体化学
#   → 通常应该去掉:
scaf = MurckoScaffold.MurckoScaffoldSmiles(
    mol=mol, includeChirality=False)

骨架在药物化学中的实际角色

# 【一个典型的项目流程】
#
# 1) 【苗头筛选】
#    找到多个不同骨架的活性分子
#    → 每个骨架是一个潜在的化学系列
#
# 2) 【系列选择】
#    评估各系列:
#      - 活性水平与可优化空间
#      - 【专利自由度】(有没有被别人占了)
#      - 【可合成性】(衍生化是否方便)
#      - 初步的成药性(见 064、070)
#      - 结构可解释性(有没有共晶)
#    → 【选 1~3 个系列推进】
#
# 3) 【先导优化】
#    在选定的骨架上系统改造取代基
#    → 建立 SAR(见 073)
#    → 【R 基团分解是标准的分析工具】(见 042)
#
# 4) 【骨架跃迁】(如果需要)
#    当原骨架遇到瓶颈时:
#      - 专利问题
#      - 成药性天花板(如代谢不稳定)
#      - 毒性与骨架相关
#    → 换一个骨架保持药效团(见 352)
#
# 【关键认识】:
#   骨架的选择是【项目早期最重要的决策之一】
#   选错骨架,可能在优化上浪费一两年
#   → 因此「多系列并行推进」是常见的风险管理策略

按骨架组织数据的实用代码

import pandas as pd
from rdkit import Chem
from rdkit.Chem.Scaffolds import MurckoScaffold

def add_scaffold_columns(df, smiles_col="smiles"):
    """给 DataFrame 加骨架列"""
    scaffolds, generics = [], []
    for s in df[smiles_col]:
        m = Chem.MolFromSmiles(s)
        if m is None:
            scaffolds.append(None)
            generics.append(None)
            continue
        scaf = MurckoScaffold.GetScaffoldForMol(m)
        scaffolds.append(Chem.MolToSmiles(scaf))
        try:
            generics.append(Chem.MolToSmiles(
                MurckoScaffold.MakeScaffoldGeneric(scaf)))
        except Exception:
            generics.append(None)
    df = df.copy()
    df["scaffold"] = scaffolds
    df["generic_scaffold"] = generics
    return df

# 【按骨架分析 SAR】
def scaffold_summary(df, activity_col="pIC50", min_count=5):
    g = df.groupby("scaffold").agg(
        n=(activity_col, "count"),
        mean=(activity_col, "mean"),
        max=(activity_col, "max"),
        std=(activity_col, "std"),
    )
    g = g[g["n"] >= min_count].sort_values("max", ascending=False)
    return g

# 【这张表能直接回答】:
#   - 哪些骨架的最好活性最高?
#   - 哪些骨架的数据最多(研究最充分)?
#   - 哪些骨架的活性范围最宽(可优化空间大)?
#   → 【这是选择化学系列的直接依据】

关键要点

  • 骨架是药物化学组织工作的核心单位——一个项目通常是「选定骨架,优化取代基」;
  • 线性分子的骨架是空字符串,会在骨架划分中被归为同一类;
  • 骨架划分不等于足够严格——不同骨架可能仍很相似,应检查最大相似度;
  • 骨架选择是项目早期最重要的决策之一,多系列并行是常见的风险管理策略

延伸资源