pychembldbでつくるChEMBLウェブサービス

Flaskpychembldbを使えばChEMBLウェブサービスみたいなのは簡単に作れるよと、朝の30分くらいを使ってちょっとやってみた。

pychembldbはSQLAlchemyのラッパーなので、Flaskのほうではルーティングを設定して、ハンドラ関数用意すればいいだけ。特にFlaskはJSON化する関数が用意されているのでJSONで返すのはラク。

@app.route("/chemblws/compounds/<chembl_id>")
def compound_by_ChEMBLID(chembl_id):
    compound = chembldb.query(Molecule).filter_by(chembl_id=chembl_id).one()
    result = {...}
    return jsonify(result)

という感じでDictionaryを用意してxmlかjsonに変換して返せばいいので、とりあえずChEMBLIDを与えると対応する化合物情報を返すAPIを実装してExamplesに用意してみた。

自前でサービスを用意することのメリットは

  • 外部に情報が流れない
  • レスポンスが速い
  • 沢山投げても怒られない

ということの他に

  • 自分たちの用途に合わせて 拡張できる
  • データベースのスキーマをきちんと理解できる

という部分もあるかなと思います。例えばChEMBLウェブサービスにはジャーナルのdoiから構造リストを返すというAPIは存在しないけど、project毎にジャーナルをまとめていたりするときにはそういうAPIが用意されていると便利かもしれませんよね?

最初、ウェブサービスが返す情報は固定なのかなと思い、決め打ちで用意したのだけど、CHEMBL1CHEMBL2で返ってくるjsonのキーが違うので、valueが存在するのものをすべて返しているのかな。

もう少しちゃんと出来たらきちんとテストを書こう。

CHEMBL1

{
    "compound": {
        "acdLogd": 7.67, 
        "acdLogp": 7.67, 
        "alogp": 3.63, 
        "chemblId": "CHEMBL1", 
        "knownDrug": "No", 
        "medChemFriendly": "Yes", 
        "molecularFormula": "C32H32O8", 
        "molecularWeight": 544.59, 
        "numRo5Violations": 1, 
        "passesRuleOfThree": "No", 
        "rotatableBonds": 2, 
        "smiles": "COc1ccc2[C@@H]3[C@H](COc2c1)C(C)(C)OC4=C3C(=O)C(=O)C5=C4OC(C)(C)[C@@H]6COc7cc(OC)ccc7[C@H]56", 
        "stdInChiKey": "GHBOEFUAGSHXPO-XZOTUCIWSA-N"
    }
}

CHEMBL2

{
    "compound": {
        "acdBasicPka": 6.52, 
        "acdLogd": 2.09, 
        "acdLogp": 2.14, 
        "alogp": 2.11, 
        "chemblId": "CHEMBL2", 
        "knownDrug": "Yes", 
        "medChemFriendly": "Yes", 
        "molecularFormula": "C19H21N5O4", 
        "molecularWeight": 383.4, 
        "numRo5Violations": 0, 
        "passesRuleOfThree": "No", 
        "preferredCompoundName": "PRAZOSIN", 
        "rotatableBonds": 4, 
        "smiles": "COc1cc2nc(nc(N)c2cc1OC)N3CCN(CC3)C(=O)c4occc4", 
        "species": "NEUTRAL", 
        "stdInChiKey": "IENZQIKPVFGBNW-UHFFFAOYSA-N", 
        "synonyms": "CP-12299,Minipress,Minizide,PRAZOSIN,Prazosin"
    }
}

今週末は第3回入門機械学習読書会です

優先トレイと回帰分析の章をやります。

懇親会はどこにするかは決めていないですが、それほど人が多くなければPhotoに寄って、ソビって帰るというプランが(自分のなかで)固まりつつある。

ProductName 入門 機械学習
Drew Conway
オライリージャパン / 3360円 ( 2012-12-22 )


ちなみに、scikit-learnでも回帰分析してみました。

MMPで発表してみたら結構レスポンスが多くて楽しかった

先週はgWT絡みで、今週はMMP絡みの発表をしてきた。それぞれ独立している仕事ではあるのだけど、プロジェクトの大いなる流れというかストリームというかそんなやつをきちんとコントロールしたいというのがそもそもの動機だったりするのでそういう意味ではつながっていると思います。

あと、いくつか質問をうけたので答えられるものはここに記しておきます。

sdfからgspのコンバートってどうすんの?

openbabelのPythonラッパーを入れていればここに書いてるスクリプトで動くと思います。結合情報をアトム情報を吐き出せばいいだけなので他のツールでも同様にやればいいはずです。速度が気になる場合はsdfパーサーを書かなければいけないので面倒かも。

個人的にはpygwtというsdf2gsp gsp2sdfを後ろで面倒見てくれるモジュールを作りかけなので、出来たらgithubにあげておくかもしれません。

pychembldb

chembldbのpythonラッパーです。pypiにもあげてあるのでeasy_installとかpipでインストール出来ます。16には対応していませんが、そのうち対応します。

イントラでrestfulなウェブサービスを作る場合(といってもGETに対応させるだけでしょうが)にはFlaskを使うのがお手軽かなと思います。chemblのAPIに対応させるのはそんなに難しくないですね。

そういえば、創薬系の中でPythonでウェブサービスつくるハンズオンでもやりつつ沼津のタップルームで地ビールがぶ飲みしたいなとかそういう話をしていたことを思いだした。

Shizuoka.py #2をやります

7/6@静岡です。

snake

前半はフロントエンドな話、後半はサーバーサイド(サーバー管理的な)の話になっています。

前半は、みんな大好きパターン認識から、Pythonで書いてjavascriptにコンパイルするっていう話と、Pythonでスマホアプリを作っていくという夢が広がる系の話ですね。

後半はサーバーの管理を楽にする構成管理ツールの使い方という実務的な内容になる(のかな?)個人的にansible,fabric(+cuisine), envassertの使い分けをいまいち理解していないので楽しみなトコロ。

タイムテーブルに若干余裕があるので、入門的な話を入れたいのだけど、もし聞きたい話があればATNDのコメント欄にでも書いておいて貰えれば。もちろん「発表したい」も歓迎です。

python-pptxで表をつくってみた

python-pptxがテーブル対応していたので早速使ってみた。これはヤヴァイ、スクリプトで自動化したら快適になりそうな予感がする。

が、いまのとこ6行以上を指定するとファイルが出力できない模様。

test_table

from pptx import Presentation
from pptx.util import Inches
from pychembldb import *

prs = Presentation()
title_only_slidelayout = prs.slidelayouts[5]
slide = prs.slides.add_slide(title_only_slidelayout)
shapes = slide.shapes

shapes.title.text = "10.1016/S0960-894X(01)80693-4"

rows = 6
cols = 6
left = Inches(0.5)
top = Inches(1.5)
width = Inches(8.0)
height = Inches(0.8)

tbl = shapes.add_table(rows, cols, left, top, width, height)

tbl.columns[0].width = Inches(2.0)

tbl.cell(0, 0).text = 'inchi_key'
tbl.cell(0, 1).text = 'activity'
tbl.cell(0, 2).text = 'HBD'
tbl.cell(0, 3).text = 'HBA'
tbl.cell(0, 4).text = 'PSA'
tbl.cell(0, 5).text = 'MWT'

for journal in chembldb.query(Doc).filter_by(doi="10.1016/S0960-894X(01)80693-4"):
    for assay in journal.assays[:1]:
        for i, activity in enumerate(assay.activities[:5], 1):
            tbl.cell(i, 0).text = activity.compound.molecule.structure.standard_inchi_key
            tbl.cell(i, 1).text = str(activity.published_value)
            tbl.cell(i, 2).text = str(activity.compound.molecule.property.hbd)
            tbl.cell(i, 3).text = str(activity.compound.molecule.property.hba)
            tbl.cell(i, 4).text = str(activity.compound.molecule.property.psa)
            tbl.cell(i, 5).text = str(activity.compound.molecule.property.mw_freebase)

prs.save('test.pptx')

追記 130605

というわけでSHizuoka.py #2も楽しいよと、ちょいと宣伝しておきます。

入門機械学習の線形回帰をscikit-learnでやってみる

5.2のウェブアクセス数予測の節でRのコードは以下のようによくあるもの

> lm.fit <- lm(log(PageViews) ~ log(UniqueVisitors), data = top.1000.sites)
> summary(lm.fit)

Call:
lm(formula = log(PageViews) ~ log(UniqueVisitors), data = top.1000.sites)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.1825 -0.7986 -0.0741  0.6467  5.1549

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)         -2.83441    0.75201  -3.769 0.000173 ***
log(UniqueVisitors)  1.33628    0.04568  29.251  < 2e-16 ***
---

これをpandas+scikit-learnでやってみる。プロットするのでipython -pylabで起動しておく。

from sklearn import linear_model
import pandas as pd
import numpy as np
top_1k_sites = pd.read_csv("top_1000_sites.tsv", sep="\t")
scatter(np.log(top_1k_sites["PageViews"]), np.log(top_1k_sites["UniqueVisitors"]))
clf = linear_model.LinearRegression()
clf.fit(np.log(top_1k_sites[["UniqueVisitors"]]), np.log(top_1k_sites["PageViews"]))
clf.coef_        # array([ 1.33627803])
clf.intercept_ # -2.834409473735672

個人的にRの表記の仕方(y ~ Xみたいな)のほうに慣れているのでscikit-learnのfit(X, y)という書き方は最初ちょっと戸惑った。

以下はscatter(np.log(top_1k_sites["PageViews"]), np.log(top_1k_sites["UniqueVisitors"]))で表示される散布図

plot

ProductName 入門 機械学習
Drew Conway
オライリージャパン / 3360円 ( 2012-12-22 )


PythonScriptでHandlebarsを扱う

PythonScriptでhtmlテンプレートを使いたいなぁと思ったのでHandlebarsを使ってみた。

pythonscript with handlebars

index.jade

!!! 5
html
  head
    meta(charset='utf-8')
    meta(name='viewport', content='width=device-width')
    title Handlebars demo
    link(rel='stylesheet', href='stylesheets/app.css')
    script(src='javascripts/vendor/custom.modernizr.js')
  body
    .row
      .large-12
        h2 PythonScript with Handlebars
        hr
    script(src='javascripts/jquery.js')
    script(src='javascripts/handlebars.js')
    script(src='javascripts/pythonscript.js')
    script(src='javascripts/jquery.py.js')
    script(src='javascripts/app.py.js')
    script(src='javascripts/foundation/foundation.js')
    script
      $(document).foundation();

app.py

テンプレートをhtmlのscriptから取れなかったのでベタ書きした。あと",'の扱いが0.7.4でおかしくなってる。

source = "<h3>{{title}}</h3><div>{{body}}</div>"

template =  JS("Handlebars.compile(source)")
title = "ひげひげひげ"
body = "セクシーコマンドー外伝"
data = JS("template({title:title,body:body})")
J("body .row ").append(data)

ちょっと試した感じではラップすればjavascriptを意識せずに使えそう。scriptタグでテンプレートが読み込めない問題は困るが。

ProductName セクシーコマンド外伝 すごいよ!!マサルさん(1) [DVD]

バンダイビジュアル / 3243円 ( 2001-01-25 )


Hakuin: PythonのフロントエンドM(VC)フレームワーク

ぼくも日本酒にちなんだフレームワークをつくりたいなーと思っていたので、Zen of PythonScriptでSpineインスパイアなHakuinというPythonのフロントエンドM(VC)フレームワークを作って遊んでいる(韻を踏むかは個人的に超重要)。

hakuin

ちなみに0.6.1では動くけれど0.7.4では動かないので、これから対応していく。今のとこ

  • クラスメソッドがうまくうごかない
  • シングルトンの作り方がわからないのでグローバル
  • e.preventdefault()が使えない
  • importのやり方がわからないので、とりあえずベタ書き

というあたりの制約があるため、ほぼ100%トイプログラミングなのだけどtodoくらいなら動く。それから上記1番目の理由でSpineインスパイアなのにCollectionモデルを取り入れている。

次回のShizuoka.pyで話す予定

以下の日本語注釈付きソースは参考になった。backbone.jsのほうは次回の「TalkNote × Frontrend(フロントレンド)」の演者の人が管理されているようなので、興味があれば参加するといいと思う。個人的にはSASS/Compassのセッションが超気になるんだけど、吉原祇園祭と被っているのでかなり参加は厳しい。

そして白隠正宗ではなく臥龍梅を飲み始めた。この純米吟醸まじ美味い。緑のラベルの超辛口ってのもあったんだけど、酒屋の奥さんのすすめの通り赤いラベルが正解だな。(さらに奥さん情報によると袋しぼりがメチャウマだったらしいんだけど、売り切れたらしい。)

1369817478

そのうちこんや銘酒館で日本酒を飲みながら、日本酒フレームワークハッカソンでもやりたい。

PythonScriptでjQueryを扱う

次回のShizuoka.pyはサーバーサイド寄りの演題が多めなので、カウンターとしてフロントエンド寄りのネタでもやろうかなと思いPythonScriptをいじっている。

PythonScriptでjQueryを使うのは簡単。jQueryとラッパーを呼び出しておけばJでアクセスできる。

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>PythonScript & jQuery</title>
    </head>
    <body>
      <div id="mydiv">click      
      </div>
      <script src="pythonscript.js"></script>
      <script src="jquery.js"></script>
      <script src="jquery.py.js"></script>
      <script src="app.py.js"></script>
    </body>
</html>

app.py

J()でjQueryのラッパーオブジェクトを作れるが、jQueryオブジェクトはj属性で参照されるので注意。

class Model():
    def __init__(self, tag):
        self.el = J(tag)
        self.el.bind("click", self.el, self.say)

    def say(self):
        d = J('<p>:-)</p>')
        self.el.append(d.j)
        print "hello PythonScript"

m = Model('#mydiv')

pythonscript & jQuery

PythonScriptのクラスシステムを読んでみた

PythonScriptではクラスがどういうjavascriptにトランスレートされるのか気になったので。

class C():
    def __init__(self, x, y):
        self.x = x
        self.y = y

c1 = C(1, 2)
c2 = C(3, 4)

print c1.x, c2.y

これは以下のjavascriptに変換される

C = {};
parents = create_array();
var C____init__ = function(args, kwargs) {
    var signature = {"kwargs": {}, "args": create_array("self", "x", "y")};
    var arguments = get_arguments(signature, args, kwargs);
    var self = arguments["self"];
    var x = arguments["x"];
    var y = arguments["y"];
    set_attribute(self, "x", x);
    set_attribute(self, "y", y);
}

C.__init__ = C____init__;
C = create_class("C", parents, C);
c1 = get_attribute(C, "__call__")(create_array(1, 2), {});
c2 = get_attribute(C, "__call__")(create_array(3, 4), {});
console.log(get_attribute(c1, "x"), get_attribute(c2, "y"));

create_array, get_arguments, create_class, get_attribute, set_attributeはpythonscript.jsのほうに定義されている。create_classは次のように定義されている

var create_class = function(class_name, parents, attrs) {
    if(attrs.__metaclass__) {
        var metaclass;
        metaclass = attrs.__metaclass__;
        attrs.__metaclass__ = undefined;
        return metaclass([class_name, parents, attrs]);
    }
    else {

    }

    var klass;
    klass = {};
    klass.bases = parents;
    klass.__name__ = class_name;
    klass.__dict__ = attrs;
    var __call__ = function() {
        var object;
        object = {};
        object.__class__ = klass;
        object.__dict__ = {};
        var init;
        init = get_attribute(object, "__init__");
        if(init) {
            init.apply(undefined, arguments);
        }
        else {

        }

        return object;
    }

    klass.__call__ = __call__;
    return klass;
}