fingerprintの由来を知りたい

openbabelのFP4フィンガープリントは部分構造由来のビット列なので、どのパターンに由来するビットが立っているのか知りたいときがよくあります。(MACCSなんかも同様に)

pybelを使えば簡単にサーチできるようです。

import pybel

def readsmartsfile(filename="/usr/local/share/openbabel/2.2.0/SMARTS_InteLigand.txt"):
    patterns = []
    inputfile = open(filename, "r")
    for line in inputfile:
        line = line.strip()
        if line and line[0]!="#":
            colon = line.find(":")
            name = line[:colon]
            smarts = line[colon+1:].strip()
            patterns.append([pybel.Smarts(smarts), name])
    return patterns
        
if __name__=="__main__":
    import sys
    patterns = readsmartsfile()
    file = sys.argv[1]
    
    for molecule in pybel.readfile("sdf", file):
        print "Looking at molecule %s" % molecule.title
        print "It has the following functional groups:",
        for smarts, name in patterns:
            if smarts.findall(molecule):
                print name,
        print "\n"

出典:Problem when using Fingerprint 4

openbabelのfingerprintをビット列にする

openbabelではxhオプションをつけることでフィンガープリントを16進表記で書き出すことができます。

babel -ismi test.smi -ofpt test.fpt -xh -xfFP2
less test.fpt
>test1   18 bits set 
00002000 00000000 00000100 00010200 00000000 00000000 
00000000 00000000 00000000 00000840 00000000 40008000 
00000000 00000000 00000000 00000000 00020000 02000000 
04000000 00000000 00000000 08000000 10000000 00400000 
00000000 80000000 00000000 00000040 00004000 00020000 
00000000 00000000 
>test2   Tanimoto from test1 = 0.027027
00010000 01000000 00000000 00000000 00080000 00000000 

ただしこれだと、解析するときに不便なのでビット列に直したい時があります。なのでpythonでビット列に変換するコードを書いてみました

def hex2bin(fingerprint):
   bf = ""
   h2b = {"0":"0000","1":"0001","2":"0010","3":"0011",
          "4":"0100","5":"0101","6":"0110","7":"0111",
          "8":"1000","9":"1001","a":"1010","b":"1011",
          "c":"1100","d":"1101","e":"1110","f":"1111",
          }

   for l in fingerprint:
       for c in l:
           b = h2b.get(c)
           if b: bf += b
   return bf

def convert(file):
   result = ""
   for data in open(file,"r").read().split("\n>"):
       fp = ""
       for list in data.split("\n")[1:]:
           fp += hex2bin(list)
       result += data.split("\n")[0].split(" ")[0] + " " + fp + "\n"
   return result

if __name__ == "__main__":
   import sys
   file = sys.argv[1]
   sys.stdout.write(convert(file))

フィンガープリントをさらにsplitしてcsvで書き出せばRであれとかこれとかQSAR的な解析が出来るようになりますね。

RECAPルールで構造を切断したい

molblasterのように構造をランダムに切断するのではなくて、もう少し合成を考慮した方法としてRECAPルールが使えます。ルールは全部で11あります。

RECAPを簡単に使える方法がなかったので、Chemistry::RECAPというPerlのモジュールを作ってみました。PerlMolとUNIVERSAL::requireが必要です。

use Chemistry::RECAP;
use Perl6::Say;
my $recap = Chemistry::RECAP->new();
my @frags = $recap->cleave("CC(=O)NCCn1cccc1-c2ccccc2");
say join ",", @frags;

実行結果

$ perl recap.pl 
CC=O,CCN,n1cccc1,c1ccccc1

RECAPは切断部位のみで切断後の構造に関しては特に指定してないのでこのモジュールは切断した位置には全て水素を付加しています。実際にフラグメントを反応させたい場合には単に水素を埋めるだけではなく、酸クロライドみたいな試薬としても適切な形でキャッピングするようにしたほうがよいでしょう。

構造をランダムに切断したい

現在の創薬シーンにおいてはフラグメントベースのドラッグデザイン(FBDD)やそれをcomputational的に行うのは主流に近いと思います。
ランダムに結合を切る方法としてhttp://www.ncbi.nlm.nih.gov/pubmed/16995724というのがあるのでopenbabelを使って簡単に実装してみます。

molblasterのアルゴリズムは単純で、ボンドをサンプリングしてカットしていくというのを何回か繰り返してフラグメントの頻度を数えるというものです。というわけで、一回にどのくらいの数のボンドを切断するかと、何回繰り返すかで精度が変化するようです。

import openbabel as ob
from random import sample

def randomsplit(mol,cutnum=5):
    cutlist =  sample(range(mol.NumBonds()),cutnum)

    delbonds = []
    for i,bond in enumerate(ob.OBMolBondIter(mol)): 
        if i in cutlist: delbonds.append(bond)
    for b in delbonds: mol.DeleteBond(b)
        
    return mol


def molblaster(smi,iter=100):
    obc = ob.OBConversion()
    obc.SetInAndOutFormats('smi','smi')

    for i in range(iter):
        mol = ob.OBMol()       
        obc.ReadString(mol,smi)
        yield obc.WriteString(randomsplit(mol))[:-1]

if __name__ == "__main__":
    smiles = 'C(C)CCc1ccccc1Cl'

    for smi_string in molblaster(smiles):
        print smi_string

自分専用お気に入りフラグメントデータベースを持つことがモデラーとしてのキャリアの第一歩だという噂です。

脱塩したい(pybelで)

化合物の物性などを予測する時には、イオン化の状態を揃えて中性にしたり脱塩して、単一の化合物にするというルーチンワークが必須です。

openbabelにはそのためのメソッドとしてStripSaltというものが用意されています。例として以下の化合物の脱塩をしてみます。

http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=24847969&loc=ec_rcs:aspirin

import pybel
mol = pybel.readstring('smi','CCC(C)C(=O)OC1CC(C=C2C1C(C(C=C2)C)CCC(CC(CC(=O) \
[O-])O)O)O.CC(=O)OC1=CC=CC=C1C(=O)O.[Na]')
mol.OBMol.StripSalts(14)
mol.write('smi') # 'CCC(C)C(=O)OC1CC(C=C2C1C(C(C=C2)C)CCC(CC(CC(=O)[O-])O)O)O\t\n'

引数として与えた数未満のヘテロアトム数のcompoundが除かれるようです。

実際には、化合物本体よりも大きい塩があったり、R体とS体の両方が一つの化合物として入力されていたりと解析者泣かせのデータを扱うことが多いので、もう少し複雑怪奇な脱塩ルーチンが使われることが多いようですが、、、(謎)

pybelで特性を計算する

pybelはcalcdescメソッドでlogP,MR,TPSAを計算することができます

import pybel
mol = pybel.readstring('smi','COC=C')
mol.calcdesc()
#{'LogP': 0.77629999999999999,
# 'MR': 17.146000000000001,
# 'TPSA': 9.2300000000000004}
mol.OBMol.GetExactMass()
#58.041864812

さらに、OBMol属性からopenbabelのメソッドを呼ぶことができます。今回はExaxtMassを計算してみました。

perlでもOASAライブラリを利用して構造描画したい

Inline::Pythonというモジュールを使うとPythonのコードをperlに埋め込むことができます。

use Inline Python => <<'END';
import pybel
def draw_png(smiles,file):
    mymol = pybel.readstring('smi',smiles)
    mymol.draw(filename=file, show=False)
END

draw_png("CCCc1ccccc1OC","sample.png");

Inline系のモジュールは色々あります。参考までにInline::JavaでCDKなどを使った例もあげておきます。

Structure-CDKの絵は奇麗なので気に入っていますが、もっとこうアートな感じの描画ライブラリがあってもいいのになとは思います。