OpenBabelでC++プログラミング ~超入門 その2~

今回はSDファイル

 今回はSDファイルを取り扱ってみます。SDファイルとは化合物構造とそれに付随する様々な情報を取り扱うファイル形式として広く使われています。構造式を記述する部分の形式はMOL形式と同じで、複数の分子を取り扱えることや様々な情報を取り扱えるように仕様が拡張されています。より詳しい情報は以下のwebサイトを確認してみて下さい。

 OpenBabelでSDファイルを読み書きするにはOBConversionクラスのSetInFormatメソッドで”SDF”を指定すれば良いだけです。SD形式がMOL形式やSMILESと異なるのは色々なプロパティ(情報)が入っている点で、このプロパティを扱わないとSD形式を利用する意味がありません。したがって、SDファイル中のプロパティを扱う方法を紹介します。

PubChemのSDファイル

 PubChemから例として扱えるSDファイルをダウンロードして用意してみました。ファイルの中身を少し見てみると以下のようになっています。”ダウンロード”をクリックしてSDファイルをダウンロードしてみてください。

9549299
  -OEChem-07121920292D

 48 51  0     0  0  0  0  0  0999 V2000
    2.3660   -1.2010    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
    2.0000   -2.5670    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
    3.3660   -2.9330    0.0000 F   0  0  0  0  0  0  0  0  0  0  0  0
   12.3923    2.4330    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
   11.5263    0.9330    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
...
...
 28 47  1  0  0  0  0
 30 48  1  0  0  0  0
M  END
> <PUBCHEM_COMPOUND_CID>
9549299

> <PUBCHEM_COMPOUND_CANONICALIZED>
1

> <PUBCHEM_CACTVS_COMPLEXITY>
586

...
...

 最初から”M END”までが構造式の情報で、MOL形式と同じです。そこから下の”> <PUBCHEM_COMPOUND_CID>”がプロパティのタイトルで、それに対応する内容が直下に記述されている”9549299″となります。そしてこのSDファイルには11個の分子の情報が記述されており、1分子毎に、情報の終わりとして必ず”$$$$”という記号で終了します。

 先ずSDファイル中の”PUBCHEM_MOLECULAR_WEIGHT”と”PUBCHEM_XLOGP3_AA”という2個のプロパティ(分子量とlogP)をプログラムで読み取ってみましょう。プログラムは”readSDF1.cpp“です。

  1 #include <iostream>
  2 #include <fstream>
  3 #include <string>
  4 
  5 #include <openbabel/mol.h>
  6 #include <openbabel/obconversion.h>
  7 
  8 using namespace std;
  9 using namespace OpenBabel;
 10 
 11 int main(int argc, char* argv[]) {
 12   ifstream ifs("EGFR_lig.sdf");
 13 
 14   OBConversion conv;
 15   conv.SetInFormat("SDF");
 16 
 17   OBMol mol;
 18   vector<OBMol> lib;
 19   while (conv.Read(&mol, &ifs)) {
 20     lib.push_back(mol);
 21   }
 22   ifs.close();
 23 
 24   for (auto ii = lib.begin(); ii != lib.end(); ii++) {
 25     string s_MW = ii->GetData("PUBCHEM_MOLECULAR_WEIGHT")->GetValue();
 26     double MW = stof(s_MW);
 27     string s_logP = ii->GetData("PUBCHEM_XLOGP3_AA")->GetValue();
 28     double logP = stof(s_logP);
 29     cout << ii->GetTitle() << "\t" << MW << "\t" << logP << endl;
 30   }
 31 }

以下のようにコンパイルします

g++ -std=c++11 readSDF1.cpp -I/home/username/app/include/openbabel-2.0 -L/home/username/app/lib -lopenbabel

 ポイントは25行目と27行目の”GetData”メソッドです。ここの引数に取得したいプロパティ名を指定します。そして”GetValue()”でその値が文字列となって取得できます。取得する変数の型は文字列なので、分子量のような実数の場合は”stof”関数で実数化します。プログラムを実行すると以下のような出力になります。

9549299 413.4   4.5
78358313        445.6   3.3
9843206 387.4   4.8
6711154 365.3   4
11566580        355.8   3.6
132117821       490     3.1
127264586       490     3.1
118797290       397.6   4.6
126496758       487.5   3.4
129627019       436.5   4.7
91668194        415.4   1.2

プロパティの作成と削除

 次は独自プロパティを追加してみましょう。あと、ついでに何か1つのプロパティを削除もしてみます。とりあえずプログラム”readSDF2.cpp“をご覧ください。少しコードが長くなってきたので、ポイントとなる部分だけ載せます。

 24   conv.SetOutFormat("CAN");
 25   for (auto ii = lib.begin(); ii != lib.end(); ii++) {
 26     // SMILESを取得する
 27     string smiles = conv.WriteString(&*ii);
 28     smiles.erase(smiles.find('\t'));
 29 
 30     // プロパティとしてSMILES
 31     OBPairData* p = new OBPairData;
 32     p->SetAttribute("OpenBabel_SMILES");
 33     p->SetValue(smiles);
 34     ii->SetData(p);
 35 
 36     // プロパティを1つ削除する
 37     ii->DeleteData("PUBCHEM_IUPAC_INCHI");
 38   }

 このプログラムではOpenBabelの機能で構造のSMILESを作成して”OpenBabel_SMILES”という新しいプロパティ名でそのSMILESをSDファイルに追加しています。あと”PUBCHEM_IUPAC_INCHI”というプロパティを削除しています。”readSDF2.cpp”をコンパイルしてプログラム実行すると、”New_EGFR_lig.sdf”というファイルが作成されます。エディタなどで前述したようなプロパティが出来ているかどうか中身を確認してみて下さい。

 プロパティの追加はnew演算子でOBPairDataのオブジェクトを作成します(31行目)。そのオブジェクトに対して”SetAttribute”, “SetValue”でプロパティ名とその値を追加して、最後に分子に対して”SetData”で追加します。あと、プロパティの削除は”DeleteData”メソッドを利用します。

 注意点として、プロパティを追加したら絶対にdelete演算子でオブジェクトを消さないで下さい。大抵のC++の入門書などではnew演算子を使ったら必ずdeleteしなさいと書いてありますが、この場合はOBMolクラスのデストラクタがちゃんと消してくれるので大丈夫です。こういう部分に気を使わないと行けないのがC++の面倒くさい所ですよね。計算を専門とする我々が扱う分子数は数百万〜数千万というオーダーになることがあります。したがって少しでもメモリリークすると分子数が多いので大変なことになるのです。