分子生成中常见分子描述符介绍及代码实现

一、背景介绍

        本文针对计算机辅助药物设计 (CADD)中的分子生成方向进行一下总结,主要内容是根据模型生成的分子理化性质检验,包括多种性质的定义、计算公式、评价指标及实现代码。

        计算机辅助药物设计 (CADD) 提高了药物发现的速度。在不同的方法中,DL方法一般通过神经网络训练大量的样本数据来学习样本的分子结构。不同于传统的相似配体(ligand)搜索,DL模型在训练过程中通过学习先验知识获取特征信息和一般规则。在深度学习领域,生成模型可以有效地生成具有所需特性的新化合物,这将降低药物发现的成本。深度学习在药物发现领域的应用带来了分子生成模型的发展和扩展,同时也带来了该领域的新挑战。从头分子生成的挑战之一是如何产生具有所需药理、理化化性质的新的合理分子。

        生成模型用于生成具有分子活性的结构特征相似的新分子,并通过使用无监督学习来学习数据的分布特征。生成模型通常使用基于ASCII字符的SMILES串和基于Graph的分子图来描述,通过深度学习算法学习分子特征,从而从头生成(de novo drug generation)新的、未被发现的药物分子SMILES,但生成分子后,需要进行多步筛选,以筛选出符合条件、易于合成、具有目标理化性质的有研究价值分子,这些药物分子还需要做进一步的生物化学实验,通过大量实验数据分析才有可能成为新药。下面将主要介绍(X)种常见的分子性质检验指标。

二、配置环境

        化合物(compounds)药物是一种主要的药物类型,通常进行相关研究时通常需要对其结构进行操作,展示,及分子量、化学描述符等计算。这里需要用到一个开源工具RDkit

        RDKit是一个用于化学信息学的开源工具包,基于对化合物2D和3D分子操作,利用机器学习方法进行化合物描述符生成,fingerprint生成,化合物结构相似性计算,2D和3D分子展示等。基于python语言进行调取使用。

        首先安装RDkit,这里建议用conda进行安装,其余方法都容易出现不同程度的报错。这里默认已经安装conda。

官方网站、pythonAPI、github链接如下:

RDKithttp://www.rdkit.org/https://github.com/rdkit/rdkithttps://github.com/rdkit/rdkithttps://rdkit.org/docs/index.htmlhttps://rdkit.org/docs/index.htmlPython API Reference — The RDKit 2021.09.1 documentationhttp://www.rdkit.org/docs/api-docs.html

先创建一个自己的虚拟环境,主要为避免不同项目之间包的冲突

conda create -n my_env python=3.7 #'my_env'自己起名,python版本按需安装

激活环境

conda env list #查看环境 
conda activate my_env #激活环境

  安装RDkit

conda install -c conda-forge rdkit

检验一下是否安装成功 

import rdkit

导入后续需要包

import csv
import os
import pandas as pd
import numpy as np
from rdkit.Chem import Crippen
from rdkit.Chem import rdMolDescriptors
from rdkit.Chem import QED
from rdkit import rdBase, Chem
from rdkit.Chem import Draw, Descriptors
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit import DataStructs

三、分子性质检验指标

1、合成可行性分数(SA SCORE)

介绍:2009年诺华公司的研究员Ertl和Schuffenhauer在化学信息学杂志上发表了名为SAscore的Rdkit插件用于快速评估药物分子的合成难易程度。

评价指标:其将小分子合成难易程度用1到10区间数值进行评价,越靠近1表明越容易合成,越靠近10表明合成越困难。

计算公式:其计算权重为化合物的片段贡献减去复杂程度(SAscore =fragmentScore − complexityPenalty),其中片段贡献值根据PubChem数据库中上百万分子计算共性进行计算,复杂度则考虑分子中非标准结构特征的占比,例如大环、非标准环的合并、立体异构和分子量大小等方面。

研究员将40个化合物给化学家进行经验性评估其合成难易程度,并与SAscore得分进行比较发现与化学家给出的合成难易程度评分的相关性R2高达0.89,表明其在识别可合成难易程度上的可靠性较高。

代码:需要用到一个文件,在这里下载fpscores.pkl.gz,运行下面的脚本即可,主函数可以改成自己想要的分子SMILES

rdkit/Contrib/SA_Score at master · rdkit/rdkit · GitHubThe official sources for the RDKit library. Contribute to rdkit/rdkit development by creating an account on GitHub.https://github.com/rdkit/rdkit/tree/master/Contrib/SA_Score

import math
import pickle
from rdkit import Chem
from rdkit.Chem import rdMolDescriptors
import os
import os.path as op

#get_sa_score start
_fscores = None


def readFragmentScores(name='fpscores'):
    import gzip
    global _fscores
    # generate the full path filename:
    if name == "fpscores":
        name = op.join(os.getcwd(), name)
        # name = op.join(op.dirname(__file__), name)
    data = pickle.load(gzip.open('%s.pkl.gz' % name))
    outDict = {}
    for i in data:
        for j in range(1, len(i)):
            outDict[i[j]] = float(i[0])
    _fscores = outDict


def numBridgeheadsAndSpiro(mol, ri=None):
    nSpiro = rdMolDescriptors.CalcNumSpiroAtoms(mol)
    nBridgehead = rdMolDescriptors.CalcNumBridgeheadAtoms(mol)
    return nBridgehead, nSpiro


def calculateScore(m):
    if _fscores is None:
        readFragmentScores()

    # fragment score
    fp = rdMolDescriptors.GetMorganFingerprint(m,
                                            2)  # <- 2 is the *radius* of the circular fingerprint
    fps = fp.GetNonzeroElements()
    score1 = 0.
    nf = 0
    for bitId, v in fps.items():
        nf += v
        sfp = bitId
        score1 += _fscores.get(sfp, -4) * v
    score1 /= nf

    # features score
    nAtoms = m.GetNumAtoms()
    nChiralCenters = len(Chem.FindMolChiralCenters(m, includeUnassigned=True))
    ri = m.GetRingInfo()
    nBridgeheads, nSpiro = numBridgeheadsAndSpiro(m, ri)
    nMacrocycles = 0
    for x in ri.AtomRings():
        if len(x) > 8:
            nMacrocycles += 1

    sizePenalty = nAtoms**1.005 - nAtoms
    stereoPenalty = math.log10(nChiralCenters + 1)
    spiroPenalty = math.log10(nSpiro + 1)
    bridgePenalty = math.log10(nBridgeheads + 1)
    macrocyclePenalty = 0.
    # ---------------------------------------
    # This differs from the paper, which defines:
    # macrocyclePenalty = math.log10(nMacrocycles+1)
    # This form generates better results when 2 or more macrocycles are present
    if nMacrocycles > 0:
        macrocyclePenalty = math.log10(2)

    score2 = 0. - sizePenalty - stereoPenalty - spiroPenalty - bridgePenalty - macrocyclePenalty

    # correction for the fingerprint density
    # not in the original publication, added in version 1.1
    # to make highly symmetrical molecules easier to synthetise
    score3 = 0.
    if nAtoms > len(fps):
        score3 = math.log(float(nAtoms) / len(fps)) * .5

    sascore = score1 + score2 + score3

    # need to transform "raw" value into scale between 1 and 10
    min = -4.0
    max = 2.5
    sascore = 11. - (sascore - min + 1) / (max - min) * 9.
    # smooth the 10-end
    if sascore > 8.:
        sascore = 8. + math.log(sascore + 1. - 9.)
    if sascore > 10.:
        sascore = 10.0
    elif sascore < 1.:
        sascore = 1.0

    return sascore
def my_score(mols:list):
    readFragmentScores("fpscores")
    print('smiles\tsa_score')
    for m in mols:
        s = calculateScore(m)
        smiles = Chem.MolToSmiles(m)
        print(smiles + "\t" + "\t%3f" % s)
    
if __name__ == "__main__":
    a = Chem.MolFromSmiles('CN(C)CCC=C1C2=CC=CC=C2CCC2=CC=CC=C12')
    b = Chem.MolFromSmiles('[H][C@@]12CC3=CNC4=CC=CC(=C34)[C@@]1([H])C[C@H](CN2CC=C)C(=O)N(CCCN(C)C)C(=O)NCC')
    c = Chem.MolFromSmiles('CCOC1=NC(NC(=O)CC2=CC(OC)=C(Br)C=C2OC)=CC(N)=C1C#N')
    d = Chem.MolFromSmiles('OC(=O)C1=CC=CC=C1O')
    x = [a,b,c,d]
    sa_score=my_score(x)
    # print(sa_score)

结果如下:

smiles	sa_score
CN(C)CCC=C1c2ccccc2CCc2ccccc21		2.174229
C=CCN1C[C@H](C(=O)N(CCCN(C)C)C(=O)NCC)C[C@@H]2c3cccc4[nH]cc(c34)C[C@H]21		4.016443
CCOc1nc(NC(=O)Cc2cc(OC)c(Br)cc2OC)cc(N)c1C#N		2.591358
O=C(O)c1ccccc1O		1.425110

2、QED

QED(quantitative estimate of drug-likeness)是一种将药物相似性量化为介于0和1之间的数值的方法。是通过组合多个分子描述符来评估药物相似性的方法之一。

药物相似性

如Lippinski规则所示,获批药物的理化参数表明,这些化合物分布在狭窄的范围内。进入该化学空间的化合物称为“类药物 (drug-like)” 类药性不是化学结构的特征,而是由几个物理参数组合确定的指标。已经提出了几种评价药物毒性的指标,但最具影响力的是Ripinsky等人的Lippinski规则。

【注】Lipinski总结的五规则后总结为:

化合物的分子量小于500道尔顿;

化合物结构中的氢键给体 (包括羟基、氨基等)的数量不超过5个;

化合物中氢键受体的数量不超过10个;

化合物的脂水分配系数的对数值 (logP)在-2到5之间;

化合物中可旋转键的数量不超过10个。

QED的主要包括一下八种:

  • 分子量(MW)

  • logP(ALOGP)

  • 氢键供体的数量(HBDs)

  • 氢键受体的数量(HBAs)

  • 极表面积(PSA)

  • 可旋转键数目(ROTBs)

  • 芳环数目(AROMs)

  • 警报结构数目(ALERTS)

代码 

导入包

from rdkit.Chem import QED

调用

qed=QED.properties(Chem.MolFromSmiles('CN(C)CCC=C1C2=CC=CC=C2CCC2=CC=CC=C12'))
score = QED.default(Chem.MolFromSmiles('CN(C)CCC=C1C2=CC=CC=C2CCC2=CC=CC=C12'))
print(score)
print(qed)

qed1=QED.properties(Chem.MolFromSmiles('[H][C@@]12CC3=CNC4=CC=CC(=C34)[C@@]1([H])C[C@H](CN2CC=C)C(=O)N(CCCN(C)C)C(=O)NCC'))
score1 = QED.default(Chem.MolFromSmiles('[H][C@@]12CC3=CNC4=CC=CC(=C34)[C@@]1([H])C[C@H](CN2CC=C)C(=O)N(CCCN(C)C)C(=O)NCC'))
print(score1)
print(qed1)

结果

0.8136783893547587
QEDproperties(MW=277.411, ALOGP=4.168600000000003, HBA=1, HBD=0, PSA=3.24, ROTB=3, AROM=2, ALERTS=0)
0.6049384423595515
QEDproperties(MW=451.61500000000024, ALOGP=3.193900000000001, HBA=4, HBD=2, PSA=71.68, ROTB=8, AROM=2, ALERTS=1)

关于logp和mw RDkit也提供了单独计算方法经过验证和QED结果相同

from rdkit.Chem import Crippen
from rdkit.Chem import Descriptors

logp = Crippen.MolLogP(Chem.MolFromSmiles(mol))#计算LogP
mw = Descriptors.MolWt(Chem.MolFromSmiles(smiles))#计算MW

 3、MOSES

这是一个基准平台,分子集 (MOSES)用于支持机器学习用于药物发现的研究。MOSES 实现了几种流行的分子生成模型,并提供了一组指标来评估生成分子的质量和多样性。通过 MOSES,的目标是规范分子生成的研究,并促进新模型的共享和比较。

除了标准的唯一性和有效性指标外,MOSES 还提供其他指标来访问生成分子的整体质量。片段相似度 (Frag) 和支架相似度 (Scaff) 是生成和测试集对应的片段或支架频率向量之间的余弦距离。最近邻相似度 (SNN) 是生成的分子与测试集中最近的分子的平均相似度。内部多样性 (IntDiv) 是生成分子的平均成对相似性。Fréchet ChemNet 距离 (FCD) 测量 ChemNet 最后一层激活分布的差异。新颖性是训练集中不存在的独特有效生成分子的一小部分。

论文链接:

https://arxiv.org/abs/1811.12823https://arxiv.org/abs/1811.12823github源码(有详细的安装使用教程)

https://github.com/molecularsets/moseshttps://github.com/molecularsets/moses代码(简单以单个SMILES举例,建议至少对30000分子进行采样):

import moses
a = 'CN(C)CCC=C1C2=CC=CC=C2CCC2=CC=CC=C12'
x = [a]
metrics = moses.get_all_metrics(x)
print(metrics)
{'valid': 1.0, 'unique@1000': 1.0, 'unique@10000': 1.0, 'FCD/Test': nan, 'SNN/Test': 0.31111112236976624, 'Frag/Test': 0.013181090368167392, 'Scaf/Test': 0.0, 'FCD/TestSF': nan, 'SNN/TestSF': 0.2857142984867096, 'Frag/TestSF': 0.012627479673146369, 'Scaf/TestSF': 0.0, 'IntDiv': 0.0, 'IntDiv2': 0.0, 'Filters': 0.0, 'logP': 1.7326946315185685, 'SA': 0.39536464732487514, 'QED': 0.07334471126827363, 'weight': 34.70996032350039, 'Novelty': 1.0}

4、分子所具有的自由基电子数、价电子数

from rdkit.Chem import Descriptors
re = Descriptors.NumRadicalElectrons(smiles)#分子所具有的自由基电子数   
ve = Descriptors.NumValenceElectrons(smiles)#分子的价电子数

5、拓扑极性表面积(TPSA)

化合物的极性表面积(PSA)是分子表面极性部分的面积值的总和。极性部分通常在诸如氧或氮的杂元素周围,因此可以认为这些部分结构是加在一起的。PSA是描述分子的极性和脂溶性的描述符,例如辛醇/水分配比(logP)。在PSA和膜渗透性的实验数据之间发现了很好的相关性。

考虑到单一构象,计算出世界药品索引中所列化合物的PSA。另外,选择了43个包含氧,氮,磷和硫的部分结构,并且通过最小二乘法对每个部分结构的重量进行参数化以用于计算的PSA。

这样,将某个索引视为来自子结构的贡献之和的方法是一种在化学信息学中经常出现的概念。通过将每个原子分解成亚结构,也可以很容易地看到每个原子的贡献。

TPSA中,仅从分子中原子的键合模式(拓扑)估算PSA,而无需考虑分子的三维结构,这是拓扑极性表面积的名称的起源。

from rdkit.Chem import rdMolDescriptors
TPSA=rdMolDescriptors.CalcTPSA(Chem.MolFromSmiles(mol))

更多详细的信息可以关注“DrugAI”公众号,帮我了解了很多专有名词。

  • 15
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值