magattacaのブログ

日付以外誤報

Molecular Fragments 〜RDKit 直訳 Day20〜

(12/30追記)試訳をまとめたテストサイトを作成しました。よろしければご参照ください。

こちらはRDKit直訳 Advent Calendar 2018 - Adventar 20日目の記事です。基本的な進め方は1日目の記事をご覧ください。

本日の訳出に困った用語
bit ranking functionality: ビットランキング機能
artificial example: モデルケース

以下、訳

分子フラグメント(Molecular Fragments)

[Link] https://www.rdkit.org/docs/GettingStartedInPython.html#molecular-fragments

RDKitには分子のフラグメント化を行うツールと、生成したフラグメントを扱うためのツールが揃っています。フラグメントは互いに結合した原子団から成るものと定義されていて、原子団が付随する官能基を持つ場合もあります。説明するよりもやってみせたほうがわかりやすいでしょう:

>>> fName=os.path.join(RDConfig.RDDataDir,'FunctionalGroups.txt')
>>> from rdkit.Chem import FragmentCatalog
>>> fparams = FragmentCatalog.FragCatParams(1,6,fName)
>>> fparams.GetNumFuncGroups()
39
>>> fcat=FragmentCatalog.FragCatalog(fparams)
>>> fcgen=FragmentCatalog.FragCatGenerator()
>>> m = Chem.MolFromSmiles('OCC=CC(=O)O')
>>> fcgen.AddFragsFromMol(m,fcat)
3
>>> fcat.GetEntryDescription(0)
'C<-O>C'
>>> fcat.GetEntryDescription(1)
'C=C<-C(=O)O>'
>>> fcat.GetEntryDescription(2)
'C<-C(=O)O>=CC<-O>'

フラグメントはrdkit.Chem.rdfragcatalog.FragCatalogにエントリーとして格納されています。エントリーの記述が山括弧で囲まれた部分(例えば'<'と'>'の間)を含むことに注意してください。この部分はフラグメントに結合する官能基を表します。例えば、上記の例の場合、カタログエントリー0はアルコールが一方の炭素に結合したエチル基のフラグメントに相当し、エントリー1はカルボン酸が一方の炭素に結合したエチレンに相当します。官能基についての詳細な情報は、官能基のIDをフラグメントに問い合わせ、そのIDをrdkit.Chem.rdfragcatalog.FragCatParamsオブジェクトの中から探し出すことで得られます。

[link] rdkit.Chem.rdfragcatalog.FragCatalog

[link] rdkit.Chem.rdfragcatalog.FragCatParams

>>> list(fcat.GetEntryFuncGroupIds(2))
[34, 1]
>>> fparams.GetFuncGroup(1)
<rdkit.Chem.rdchem.Mol object at 0x...>
>>> Chem.MolToSmarts(fparams.GetFuncGroup(1))
'*-C(=O)[O&D1]'
>>> Chem.MolToSmarts(fparams.GetFuncGroup(34))
'*-[O&D1]'
>>> fparams.GetFuncGroup(1).GetProp('_Name')
'-C(=O)O'
>>> fparams.GetFuncGroup(34).GetProp('_Name')
'-O'

カタログは階層構造になっており、小さなフラグメントが合わさってより大きなフラグメントを構成します。rdkit.Chem.rdfragcatalog.FragCatalog.GetEntryDownIds()メソッドを使って、小さなフラグメントから、そのフラグメントが寄与するより大きなフラグメントを見つけることができます。

[link] rdkit.Chem.rdfragcatalog.FragCatalog.GetEntryDownIds()

>>> fcat=FragmentCatalog.FragCatalog(fparams)
>>> m = Chem.MolFromSmiles('OCC(NC1CC1)CCC')
>>> fcgen.AddFragsFromMol(m,fcat)
15
>>> fcat.GetEntryDescription(0)
'C<-O>C'
>>> fcat.GetEntryDescription(1)
'CN<-cPropyl>'
>>> list(fcat.GetEntryDownIds(0))
[3, 4]
>>> fcat.GetEntryDescription(3)
'C<-O>CC'
>>> fcat.GetEntryDescription(4)
'C<-O>CN<-cPropyl>'

多数の分子からつくったフラグメントをカタログに加えることもできます。

>>> suppl = Chem.SmilesMolSupplier('data/bzr.smi')
>>> ms = [x for x in suppl]
>>> fcat=FragmentCatalog.FragCatalog(fparams)
>>> for m in ms: nAdded=fcgen.AddFragsFromMol(m,fcat)
>>> fcat.GetNumEntries()
1169
>>> fcat.GetEntryDescription(0)
'Cc'
>>> fcat.GetEntryDescription(100)
'cc-nc(C)n

カタログ内のフラグメントは一意に定められたユニークなもので、分子をもう一度追加しても新しいエントリーは増えません:

>>> fcgen.AddFragsFromMol(ms[0],fcat)
0
>>> fcat.GetNumEntries()
1169

一旦rdkit.Chem.rdfragcatalog.FragCatalogを生成すれば、それを用いて分子のフィンガープリント化を行うこともできます。

[link] rdkit.Chem.rdfragcatalog.FragCatalog

>>> fpgen = FragmentCatalog.FragFPGenerator()
>>> fp = fpgen.GetFPForMol(ms[8],fcat)
>>> fp
<rdkit.DataStructs.cDataStructs.ExplicitBitVect object at 0x...>
>>> fp.GetNumOnBits()
189

これで、フィンガープリントに関連づけられた残りの機能をフラグメントフィンガープリントに適用することができるようになりました。例えば、二つの分子について、それぞれのフィンガープリントの積集合をとることで、共通して存在するフラグメントを簡単に見つけることができます:

>>> fp2 = fpgen.GetFPForMol(ms[7],fcat)
>>> andfp = fp&fp2
>>> obl = list(andfp.GetOnBits())
>>> fcat.GetEntryDescription(obl[-1])
'ccc(cc)NC<=O>'
>>> fcat.GetEntryDescription(obl[-5])
'c<-X>ccc(N)cc'

また、ある分子を別の分子と区別するフラグメントを見つけることもできます:

>>> combinedFp=fp&(fp^fp2) # can be more efficent than fp&(!fp2)
>>> obl = list(combinedFp.GetOnBits())
>>> fcat.GetEntryDescription(obl[-1])
'cccc(N)cc'

rdkit.ML.InfoTheory.rdInfoTheory.InfoBitRankerクラスのビットランキング機能を使って活性化合物を不活性化合物と区別するフラグメントを見つけ出すこともできます。

[link] rdkit.ML.InfoTheory.rdInfoTheory.InfoBitRanker

>>> suppl = Chem.SDMolSupplier('data/bzr.sdf')
>>> sdms = [x for x in suppl]
>>> fps = [fpgen.GetFPForMol(x,fcat) for x in sdms]
>>> from rdkit.ML.InfoTheory import InfoBitRanker
>>> ranker = InfoBitRanker(len(fps[0]),2)
>>> acts = [float(x.GetProp('ACTIVITY')) for x in sdms]
>>> for i,fp in enumerate(fps):
...   act = int(acts[i]>7)
...   ranker.AccumulateVotes(fp,act)
...
>>> top5 = ranker.GetTopN(5)
>>> for id,gain,n0,n1 in top5:
...   print(int(id),'%.3f'%gain,int(n0),int(n1))
...
702 0.081 20 17
328 0.073 23 25
341 0.073 30 43
173 0.073 30 43
1034 0.069 5 53

上記の例で、各列はbitId、infoGain、nInactive、nActiveを表します。上記モデルケースではこの手法がさほどうまくいっていないことに注意してください。

12/20/2018

このブログ記事のライセンス

以上はRDKit Documentationの試訳です。
ライセンスはCC BY-SA 4.0で、Greg Landrum氏の功績の元に作成しています。
Creative Commons — Attribution-ShareAlike 4.0 International — CC BY-SA 4.0