明日の資料の元ネタあたりをまとめてみた

創薬のためのメトリクスとかアジャイル開発手法の応用みたいな内容を話しますが、資料の中ではコンテクストや背景や動機なんかを端折ってるために、全体像がつかめないかもしれないので、参考になりそうなエントリをまとめておきます。

アジャイル開発とか継続的インテグレーション(CI)

アジャイルとはなんなのか?

モチベーションマネジメント、ゲーミフィケーション

成果主義を例に出すまでもなく、組織を活性化するためには賃金による報奨は必ずしもいいとは限らない。承認欲求に訴えるためにはどうしたらいいか?

加えて暗黙知を形式知になりやすくするようなカルチャーをつくるにはどうしたら良いか?といったこともシステム設計として考えていく必要がある。

リーンとかトヨタ生産方式とか

生産のアナロジーが使えそうな仕事または労働時間と生産性が比例するような仕事。参考書籍は沢山出ているので乱読すればよいでしょう。

リポジトリとしての化合物データベース

ELNは単に紙の実験ノートを電子化すればいいってもんじゃなくて、もっとリポジトリへのアクセス端末としての設計を考えなきゃいけないんじゃないの?とか思う。 認証だとかPart11対応だとかはバックエンドのデータベースの問題であってそんなのイノベーションとは関係無いだろ?と思っているのでELN界とは距離を置いてる。

pygamessの便利なところ

懇親会でpygamessじゃなくてもFacioで良かろうと言われたのだけど、対話環境よりも、スクリプトでやれたほうがいい場面が多いので書いておく。ちなみに僕はAvogadroユーザーです。

適当なsdfが手元になかったので

C
CC
CCC

というSMILESのリストを

babel -ismi carbons.smi -osdf carbons.sdf

とやってsdfにしたものを使いました。

sdfの分子群に対して計算したい場合、対話環境だとチマチマと作業しないといけないが、スクリプトだとforループを回せばよいし、結果がmoleculeオブジェクトで返ってくるので、そのあとのケモインフォマティクス的な取り回しが楽です。Gamessのアウトプット睨みながらコピペとか苦行だし、アウトプットの処理用のパーサー書くならinputから全てスクリプトで処理したほうがいいんじゃないかなと。

>>> import pybel
>>> from pygamess import Gamess
>>> g = Gamess()
>>> mols = pybel.readfile('sdf', 'carbons.sdf')
>>> for mol in mols:
...   nmol = g.run(mol)
...   print nmol, nmol.energy
... 
C   
 -39.7265813363
CC  
 -78.3052918313
CCC 
 -116.885136991

ケミストのように化合物を個別のものとして一つ一つ丁寧に対応していくのか、情報論的にまとめて取り扱いたいのかとかそういうあたりで使い分ければいいのだと思う。オービタル見たい場合にはGUI必須だけど、エネルギーみたいな数字だけに関心があるのであればGUIは要らないかな。

結合を切断しながらMatched Molecular Pair(MMP)を求める

論文読んでたら、コンベンショナルなMMPの求め方が載ってたので、似たようなやり方を考えてみた。

要するに網羅的に結合を切断していって

{Scaffold; [fragment, fragment, fragment]}

っていうハッシュを作っていく。fragmentは共通のScaffoldを持つので配列中の任意のフラグメントのペアがMMPということになる。

僕の切断ルール

僕の切断ルール

  • シングルボンド
  • 環の一部ではない
  • ボンドのどちらかの端がCであること

CHEMBL327743の結果

('CC[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1CC(=O)*)/C=C/c1ccccc1)C', 'COc1cccc(c1*)OC', 'CHEMBL327743')
('CC[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1C*)/C=C/c1ccccc1)C', 'COc1cccc(c1C(=O)*)OC', 'CHEMBL327743')
('CC[C@@H]([C@@H](/C=C/c1ccccc1)N*)C', 'COc1cccc(c1C(=O)CN1CCCC[C@H]1C(=O)*)OC', 'CHEMBL327743')
('CC[C@@H]([C@H](NC(=O)*)/C=C/c1ccccc1)C', 'COc1cccc(c1C(=O)CN1CCCCC1*)OC', 'CHEMBL327743')
('CC[C@@H](C(NC(=O)[C@@H]1CCCCN1CC(=O)c1c(OC)cccc1OC)*)C', '*/C=C/c1ccccc1', 'CHEMBL327743')
('COc1cccc(c1C(=O)CN1CCCC[C@H]1C(=O)N[C@H](/C=C/c1ccccc1)*)OC', 'CC[C@H](C)*', 'CHEMBL327743')
('*c1ccccc1', '*/C=C\\[C@H]([C@H](CC)C)NC(=O)[C@@H]1CCCCN1CC(=O)c1c(OC)cccc1OC', 'CHEMBL327743')
('*OC', 'CC[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1CC(=O)c1c(*)cccc1OC)/C=C/c1ccccc1)C', 'CHEMBL327743')
('*OC', 'CC[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1CC(=O)c1c(*)cccc1OC)/C=C/c1ccccc1)C', 'CHEMBL327743')
('*CC', 'COc1cccc(c1C(=O)CN1CCCC[C@H]1C(=O)N[C@@H](C(C)*)/C=C/c1ccccc1)OC', 'CHEMBL327743')
('CC[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1CC(=O)c1c(OC)cccc1OC)/C=C/c1ccccc1)*', 'C*', 'CHEMBL327743')
('*C[C@@H]([C@H](NC(=O)[C@@H]1CCCCN1CC(=O)c1c(OC)cccc1OC)/C=C/c1ccccc1)C', 'C*', 'CHEMBL327743')

コード

どこで切断されたかどうかを明確にするために切断されたボンドにダミーアトム(atomicnumが0のアトム)をおいた。

canonicalSMILESで出力して.の文字でsplitした文字列をそのままキーに使っているが、openbabelのcanonicalizationアルゴリズムはMorgan Algorithmを改変したものを使っているのでおそらく大丈夫。

下のコードでは標準出力に書きだしたが、実際はデータベース化することになる。多分Mongo使う。

smi_string,id = clone.write(format="can")[:-1].split('\t')
frag1, frag2 = smi_string.split('.')
dbh.mmp.update({"core": frag1}, {"$push": {"frags": (frag2, id)}}, True)
dbh.mmp.update({"core": frag2}, {"$push": {"frags": (frag1, id)}}, True)

こんな感じだろうか。

以下MMPを求めるのに使ったコード

import pybel
import openbabel
import sys

input = sys.argv[1]

def mkclone(mol):
    obc = openbabel.OBConversion()
    obc.SetInAndOutFormats("mol", "mol")
    molstring = obc.WriteString(mol)
    new_mol = openbabel.OBMol()
    obc.ReadString(new_mol, molstring)
    return new_mol

def ring_member(rings, atom):
    for i, ring in enumerate(rings):
        if ring.IsMember(atom):
            return i
    return -1

def is_same_ring_member(rings, a1, a2):
    a1_idx = ring_member(rings, a1)
    a2_idx = ring_member(rings, a2)
    if a1_idx == -1 or a2_idx == -1:
        return False
    return a1_idx == a2_idx

def detect_del_bonds(mol):
    rings = mol.OBMol.GetSSSR()
    del_bonds = []
    for a in mol:
        if a.atomicnum == 6:
            for na in mol:
                n_a = na.OBAtom
                b = a.OBAtom.GetBond(n_a)
                if b != None and b.GetBondOrder() == 1 and not is_same_ring_member(rings, a.OBAtom, n_a):
                    if a.OBAtom.GetIndex() < n_a.GetIndex():
                        del_bonds.append((a.OBAtom.GetIndex(), n_a.GetIndex()))
    return del_bonds

for mol in pybel.readfile("sdf", input):
    chembl_id = mol.data["CHEMBL ID"]
    print chembl_id
    del_bonds = detect_del_bonds(mol)
    for a1, a2 in del_bonds:
        clone = pybel.Molecule(mkclone(mol.OBMol))
        # !!! index starts 0 but GetAtom starts 1 !!!
        ob_a1 = clone.OBMol.GetAtom(a1 + 1)
        ob_a2 = clone.OBMol.GetAtom(a2 + 1)
        bond = ob_a1.GetBond(ob_a2)
        clone.OBMol.DeleteBond(bond)

        new_atom1 = clone.OBMol.NewAtom()
        new_atom1.SetAtomicNum(0)
        new_bond1 = clone.OBMol.NewBond()
        new_bond1.SetBegin(ob_a1)
        new_bond1.SetEnd(new_atom1)
        new_bond1.SetBondOrder(1)

        new_atom2 = clone.OBMol.NewAtom()
        new_atom2.SetAtomicNum(0)
        new_bond2 = clone.OBMol.NewBond()
        new_bond2.SetBegin(ob_a2)
        new_bond2.SetEnd(new_atom2)
        new_bond2.SetBondOrder(1)

        smi_string, id = clone.write(format="can")[:-1].split('\t')
        frag1, frag2 = smi_string.split('.')
        #print (frag1, chembl_id)
        #print (frag2, chembl_id)
        print (frag1, frag2, chembl_id)

MCSベースでMatched Molecular Pair(MMP)を求める

そのうちMMPベースのプロジェクト診断ツールでも作ろうかなと思っているので、fmcsを使ったMMP構築をやってみた。

fmcs sample.sdf --output-format complete-sdf --save-smiles-tag \
MCS_SMILES --save-atom-indices-tag MCS_ATOMS --output-all

で、MCSのSMILESと対応するatom indexをタグ付けしたsdfを出力しておいてpybelでMCSを削除する。

import pybel
import sys

input = sys.argv[1]

for mol in pybel.readfile("sdf", input):
    print mol.data["MCS_SMILES"]

    del_atom_idxs =  [int(num) for num in mol.data["MCS_ATOMS"].split()]

    del_atoms = []
    for i,atom in enumerate(mol,0):
        if i in del_atom_idxs:
            del_atoms.append(atom)

    for a in del_atoms:
        mol.OBMol.DeleteAtom(a.OBAtom)
    print mol

そうすると、MMPが作れる。

もう少し具体的な例をと思ってChEMBLからsdfでも取ってきてとか思ったけど、やり方がわからなかったのでわからんとか言ってたら@32nm教えてもらった。ありがとうございます。

というわけで、ChEMBLからカテプシンK阻害剤の化合物データからCHEMBL100616,CHEMBL101160,CHEMBL316685,CHEMBL327743,CHEMBL329731,CHEMBL419891の6化合物を引っ張ってきてやってみた

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
C(=O)(N)C

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
Cl

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
[NH+](=O)[O-]

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
Cl

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
[NH+](=O)[O-]

CCC(C)C(C=Cc1ccccc1)NC(=O)C1CCCCN1CC(=O)c1ccccc1
OC.OC

あとはアッセイ情報と付きあわせてpIC50みたいなプロパティの引き算をすればMMPを利用した解析ができる。

ある程度コントロールできてる化合物群ではうまくいくんだが、MCSだと期待している以上にスキャフォールドをアサインしまうために、結果としてフラグメントが細切れになったり(Rの環が分割されてしまったり)してMMPを構成するにはちょっとあれかな。

fmcsを使ってMaximum Common Substructure(MCS)を求める

openbabelはMCSを求められないのが難点なんだが、書けばいいよなと思いつつ実装してない。そうこうしているうちに、fmcsを見つけて、これ使えばいいかなーと思い始めた2者だけじゃなくて複数の化合物のMCSを求められるのも自分の求めていた機能だし。

fmcsを動かすにはRDKitをインストールする必要があって、「RDKitのインストールめんどいなー」とぶつくさ言ってたら@iwatobipensilicosを参考にしなはれと教えてもらった。

brewで入れられんのね。

> brew tap edc/homebrew-rdkit
> brew install rdkit

これでサクッと入った。

fmcsのほうは

hg clone https://bitbucket.org/dalke/fmcs
cd fmcs
sudo python setup.py install

で入る。

さて、MLによると、

This work has been funded by Roche, with the explicit hope that it be incorporated into RDKit.

ということなのだが、確かRSOAPはPfizerが出してたはずだし、海外はopensourceとかそういうのに理解が深いんだろうか?また、そういうのに積極的な日本の製薬企業はあるのだろうか?なんて思ったりした(自分の観測範囲ではゼロなんだが)。

そういうことも含めて貢献できるような社会がくればいいのになぁと思う。

ProductName Design and Use of Relational Databases in Chemistry
TJ O'Donnell
CRC Press / 12118円 ( 2008-11-25 )


散布図を密度の情報からグラフに変換する

複数のパラメータが相互作用しあっている状態で最適なパラメーターセットを探索する際に、あまり精度の高いフィードバックが得られないと最適解の近傍の密度が高くなり、さらに準最適解があちこちに散らばるようなことがおこる。

それはPCAとかICAで空間を適当にとってあげることが出来て、特に第一主成分と第二主成分を平面にとってプロットしてやると、散布図として表現できる。

さてこれを、グラフにしてやればCytoscapeで扱えて便利だろうと思ったことがあったのでやり方を考えてみた。

アルゴリズム

  1. 任意の点から非類似度Tの距離にある点の数をすべての点について数える
  2. 最も点を含むものが多い点をrootとし、含まれる点との間で親子関係をつくる
  3. 2の点を除いて1を行い新たな親子を決める。その際に親から非類似度T内にある既存の子のうち一番近いものを結ぶ(階層ができる)
  4. これをすべての点がなくなるまで繰り返す。

以下の様な散布図を考える。

nnplot1

総当りで近傍の数をかぞえると、次の5つの点が含まれる空間が一番密度が高いことが分かる。

nnplot2

中心と含まれる点に対して親子関係をつくる(P1, C1-C4)。

nnplot3

これらを除いて、近傍の数をかぞえると次に密度が高いのが右側で3つの点を含んでいる。

nnplot4

これらに関しても先ほどと同様に親子関係をつくっておくが、新しくできた親に関しては既存の子とつなげられないかをチェックする。

するとC2 -> P2をつなげるのが良いことがわかる。

nnplot5

同様に行う(図の左側)。

nnplot6

これもC4 -> P3とつなげることができる。

nnplot7

最終的に全部つなげることができた。

nnplot8

実際に階層型の木を描くと以下のようになる。

nnplot

単純だけど、全体の構造を保ったままCytoscapeで眺められていいかなーと思うんだけど。

もっといい方法あったら教えてもらえると嬉しいです。

LBDDのソリューション

まぜてもらうことになりました。

多様化する分子設計用ソフトウエア

僕の思うLBDDの行き着くところは、当たり前のように量子化学計算が行われていて軌道の相互作用として結合論が語られる未来だと思っているので、まぁそんな感じの話に持って行きたいなーと思っています。

FMOをやっても、表面的には相関エネルギーっていう数値しか出てこないけど、軌道の係数とか理解出来ないとドラッグデザインに生かせないし、ケミストもMOに対する直感的な理解とか必須だと思うしね。

みたいな話を、informaticsとかSBDDと絡めてお話できればいいと思うんだけど、突然心変わりしてHaskellの文脈でLBDDを捉えてみるとかPDCAサイクルはドラムンベースでいうところのベースラインそのものであるとかそういう謎のストーリーを作るかもしれない。

pygamess+pybelで構造最適化計算

昨日は製薬業界の集まりがあって、他社のヒトと少し話す機会があって、LBDDどういう感じですかねと言われて、量子化学計算に真面目に取り組んでますよーっていう話から、ケミストは電子吸引基とか供与基とか言う割に計算して確かめようとしないんですよねー(そうですよねー)っていう流れになったので、そちらのケミストは計算するんですかねー?って聞いたら、するヒトはするし、しないヒトはしないっていう答えが返ってきた。想定通り。

もう少し知りたいのは、ちゃんと計算するケミストは、結局ただの計算マニアで結局普通のヒトなのか、それとも論理的で優秀な傾向が強いのか?ということかな。

さて、pygamessをpybelに対応させたので、より簡潔に書けるようになった。

>>> from pygamess import Gamess
>>> import pybel
>>> g = Gamess()
>>> g.run_type('optimize')
>>> mol = pybel.readstring('smi','C')
>>> mol.make3D()
>>> optimized_mol = g.run(mol)
>>> optimized_mol.energy
-37.0895866208

やっぱコンストラクタに引数をわたせるようにするべきだよなぁ。

ProductName 初めてのPython 第3版
Mark Lutz
オライリージャパン / 4830円 ( 2009-02-26 )


量子化学計算したいならこれかな。

pygamessをアップデートした

Gamessのpythonラッパーであるpygamessを0.2.0にアップデートした。

主な変更点

  • pybelに対応した
  • テストをpyvowsに移行した

pybelも使えるようになったので、よりpythonicにコードを書けるようになっていい感じ。 構造さえ用意すれば、こんなに簡単に量子化学計算ができて、量子化学計算にありがちなごちゃごちゃしたインプットファイルの作成から解放される。

>>> import pybel, pygamess
>>> g = pygamess.Gamess()
>>> mol = pybel.readfile("mol", "examples/ethane.mol").next()
>>> nmol = g.run(mol)
>>> nmol.energy
-78.30530748

それから、テストをpyvowsに変えてから快適です。自分にはBDDはあっているなぁ。

TODO

ドキュメントをきちんと書く

PyMOL v1.5をosx10.6.8に入れた

リリースしている安定版はインストールできなかったので、svnのtrunk(r3983)を入れた。それからbrewを使っているのでsetup.pyをちょっと変える必要があった。

/opt/localはmacports用の設定だと思うので/usr/X11に変更しないとGL/gl.hがないとかそんなエラーを吐くはず。

156         EXT = "/usr/X11"
157         inc_dirs=["ov/src",
158                   "layer0","layer1","layer2",
159                   "layer3","layer4","layer5",
160                   EXT+"/include",
161                   EXT+"/include/GL",
162                   EXT+"/include/freetype2",
163                   "modules/cealign/src",
164                   "modules/cealign/src/tnt",
165                   "generated/include",
166                   "generated/src",
167                   ]

それからPmwはソースをダウンロードしてきて入れた。なくても動くと思うが、import errorが出るので気持ち悪かったから後から入れておいた。