doiからアブストラクトを取ってくる

今、職場で使っている文献管理システムはpubmedのXMLサービスを使ってpubmedのIDで管理しているので、合成反応系の雑誌が収録されてなくてケミストには不評だったり、ASAPとpubmedのタイムラグがあってちょっと待たないと登録できなかったりと製薬系の研究所内では使いにくさが目立ってきている。(自分も量子化学系の論文とか管理できなくて困ったりしたのだけど、3年以上放置してきた)

ふと、やる気を出した。

from pyquery import PyQuery as pq
import re

r = re.compile('10.(\d+)/')

def get_contents(doi):
    DX_DOI = "http://dx.doi.org/"
    doi_url = DX_DOI + doi
    d = None
    try:
        d = pq(doi_url)
    except:
        pass

    return _extract(d,doi)

def _extract(d,doi):
    registrant_prefix = r.search(doi).group(1)    
    if registrant_prefix == '1021':
        return _extractACS(d,doi)
    else:
        return "no extractor"

#    SQLAlchemy
#    title            = Column(String(256), unique=True)
#    pubmed_id        = Column(Integer, unique=True)
#    doi              = Column(String(128), unique=True)
#    abstract         = Column(Text())

def _extractACS(d,doi):
    title = d("h1.articleTitle").text()
    abstract = d("p.articleBody_abstractText").text()
    return {'doi': doi, 'title': title, 'abstract': abstract}

これでACS系はいけるはず。

J.C.I.Mはもちろん

>>> doi.get_contents('10.1021/ci900416a')
{'abstract': u'We introduce SARANEA, ... freely available under the GNU \
General Public License.', 'doi': '10.1021/ci900416a', 'title': u'SARANEA:\
A Freely Available Program To Mine Structure\u2212Activity and \
Structure\u2212Selectivity Relationship Information in Compound Data Sets'}

J.O.Cもいけるで。

>>> doi.get_contents('10.1021/jo1025157')
{'abstract': u'The stereoselectivity of glycosylation reactions is affected \
by many factors. Synthesis of 1,2- cis glycosidic ... effects of the acetyl \
groups.', 'doi': '10.1021/jo1025157', 'title': 'Study of the \
Stereoselectivity of 2-Azido-2-deoxygalactosyl Donors: Remote Protecting \
Group Effects and Temperature Dependency'}

あとはスクレイプできる出版社を増やしていけばpubmedのXMLサービス使わんでもいいね。

  • ASAPも大丈夫
  • doiからたどれる出版社の文献を管理できる

あとちっと頑張れば一通り動くようなものが出来上がる予定。

pyqueryも、Web::QueryもjQueryっぽくスクレイピング出来て便利なので、結局jQueryを覚えておけば色々使い回しが効いて良いと思う。

ProductName jQueryクックブック
jQuery Community Experts
オライリージャパン / 3780円 ( 2010-08-18 )


選曲ネットワークをCytoscapeで

ゴールデンウィークを利用して250ノード、220エッジくらいまで選曲データを溜めたので、Cytoscapeで描画してみた。SIFフォーマットにして読み込んでみた。

全体像はだいたいこんな感じ。たまにacid jazzとかかけてたので、それが孤立したネットワークとして左下に表示されてるが、基本はドラムンベース。

djutil1

中心部を拡大してみた。

djutil1

sebaはかなり好きななんだが、Audioもよく聴いている。

ところで、文字化けしてんのはどうやったらいいんだろうか?

Flaskでone-to-manyのデータの数を表示する

FlaskというよりはSQLAlchemy+Jinja2の話なのだけど。

one-to-manyのスキーマがあったとして(例えばあるレビューに付いたコメント数)、countメソッド使うと怒られるんだろうなぁと思いつつ使ってみる

{{ bookmark.comments.count() }}

これはやはり怒られた。

TypeError: count() takes exactly one argument (0 given)

で、Jinja2でlenが使えるかなとふと思ったのでやってみたら使えなかったが、builtin filtersにlengthがあるそうなので、それで解決。

onetomany

ところで、サービスは一気に動くところまで仕上げないと、だれるっていうかモチベーションが下がってきて生産性が落ちるなぁ。みんなはどうやってやる気を保ち続けているんだろうか?こつが知りたい。

pubmedのidをdoiに変換する

pubmedのidからdoiを調べたい。

BeautifulSoupでXMLをパースするのが良いのだが、ソース見たらげんなりした(preってなんやねん)。というわけで、MEDLINE形式のデータから正規表現でdoiを抜き出してます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python

import urllib2
import re,sys

def pmid2doi(pmid):
   url = "http://www.ncbi.nlm.nih.gov/pubmed/%s?dopt=MEDLINE" % pmid
   r = re.compile('AID - (10.\d+/.+?) \[doi\]')
   response = urllib2.urlopen(url)
   if response.code == 200:
       s = response.read()
       m = r.search(s)
       return m.group(1)
   else:
       return "error: %d" % response.code

if __name__ == '__main__':
   if len(sys.argv) == 2:
       print pmid2doi(sys.argv[1])
   else:
       print "usage: %s [pmid]" % sys.argv[0]

コマンドラインから使う場合には

$ pmid2doi.py 20053000
10.1021/ci900416a

ProductName Bioinformatics Programming Using Python
Mitchell L. Model
Oreilly & Associates Inc / 5119円 ( 2009-12-23 )


jQuery Form Pluginを使えば簡単にAjaxなformを導入できる

今、欲しい物はこういうモノです。

Real World Haskellのように、文書のパラグラフ毎にコメントが入れられるようなシステム。それをSphinxでやりたい。つまりSphinxで文書を書いてmake htmlをするとパラグラフ毎にtwitterのOAuthかGoogleの認証使ってコメントが入れられるようなHTMLが出力されるナイスなドキュメント

実際にReal World Haskellのソースを追いかけてみると、jQuery Form Pluginを使っていてAjax化が簡単にできそうなので、別の題材で試してみる。もちろんflaskr

  • statisticsにjqueryとjquery.form.jsを配置
  • show_entries.htmlのformにid属性をつける(id='flaskr_form'っていう属性をつけた)
  • layout.htmlにjsの設定を追加

layout.html

<script type="text/javascript" src="{{ url_for('static', filename='jquery-1.6.min.js') }}"></script> 
<script type="text/javascript" src="{{ url_for('static', filename='jquery.form.js') }}"></script> 
<script type="text/javascript"> 
        // wait for the DOM to be loaded 
        $(document).ready(function() { 
            // bind 'myForm' and provide a simple callback function 
            $('#flaskr_form').ajaxForm(function() { 
            $(document.body).load("/");
                alert("Thank you for your comment!"); 
            }); 
        }); 
    </script>

これでsubmitしたときにアラート画面が出るようになります。今回書いたコードはここからダウンロードできます。

flaskr_form

というわけで、flaskかGAEでパラグラフのidをクエリとしてコメントをjsonで返すサービスを用意しておいて、SphinxにjQuery埋め込んでコメントサービスと連携させれば望みのものはできそうな気がするんだけど、Sphinxのパラグラフに固有のIDを付加する方法がわからない。

どうやるのがいいのかなぁ

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


サケグリッチ

日本酒のラベルでグリッチ。幾つか出してみてヨサゲな画像にgimpでポスタリゼーションとトーンカーブかけた。

joukigen

import base64,re,Image
from random import randint

def glitch(infile,outfile):
    jpg_text = base64.encodestring(open(infile,"rb").read())
    glitched_text = ""
    width,height = Image.open(infile).size
    num = width * height / 10000
    for c in jpg_text:
        if c == '0':
            if randint(0,num) == 0:
                count += 1
                glitched_text += str(randint(0,9))
            else:
                glitched_text += c
        else:
            glitched_text += c
    glitched_jpg = base64.decodestring(glitched_text)
    open(outfile,"wb").write(glitched_jpg)
    return ""

もうちょっとノイズを加えたほうがいいかな。

ProductName Glitch: Designing Imperfection

Mark Batty Publisher / 2508円 ( 2009-09-16 )


曲のつなぎを可視化

選曲をログるようにしているので、可視化してみた。まだ60チェーンくらいしかないのでcytoscape使うまでもなくnetworkxで充分。

djutil

seba,b-complex,sporあたりがお気に入りなのがわかるというか、自分はつなぐときに耽美でダークな感じの曲から展開していくのが好きだということがはっきりするわけだ。

from sqlalchemy import *
import networkx as nx
import matplotlib.pyplot as plt

G=nx.Graph()

db = create_engine('sqlite:////Users/kzfm/python/DJUtil/djutil.db')
metadata = MetaData(bind=db, reflect=True)
music_table = metadata.tables['music']
graph_table = metadata.tables['graph']

music = [m.title for m in music_table.select().execute()]

stmt = graph_table.select()
result = stmt.execute()
for row in result:
    G.add_edge(music[row.head-1], music[row.tail-1])

pos=nx.spring_layout(G)
ith_labels=False)
nx.draw(G,pos,node_color='#A0CBE2',width=1,node_size=200,alpha=0.4,edge_cmap=plt.cm.Blues,font_size=8)
plt.savefig("path.png")

ファイルのデータから画像を作ってみる

ファイルの内容を16進数で読みながらrgbのデータとして画像出力してみる。普通に出力すると単なるノイズになってしまうのでheightを繰り返してみた。

import Image
from binascii import hexlify
from random import randint

def text2rgb(text,length):
   hex = hexlify(text)
   hex_length = len(hex)
   buf = hex
   result = []
   while len(result) < length:
       if len(buf) < 6:
           start = randint(1,hex_length)
           end   = randint(1,hex_length)
           if start > end:
               buf += hex[end:start]
           else:
               buf += hex[start:end]
       else:
           r = buf[:2]
           g = buf[2:4]
           b = buf[4:6]
           buf = buf[6:]
           #for t in range(10):
           result.append((int(r,16),int(g,16),int(b,16)))
   return result

if __name__ == '__main__':
   WIDTH = 480
   HEIGHT = 480

   text = open('/Users/kzfm/PKPD.dot','rb').read()

   rgb = text2rgb(text,WIDTH*HEIGHT)

   im = Image.new( "RGB", ( HEIGHT, WIDTH ), ( 0, 0, 0 ) )
   pix = im.load()
   for h in range(HEIGHT):
       for w in range(WIDTH):
           try:
               pix[w,h] = rgb[w]
           except IndexError:
               pass
   im.save("test.png")

text2rgb

FORM+CODEにインスパイアされた

ProductName FORM+CODE -デザイン/アート/建築における、かたちとコード

ビー・エヌ・エヌ新社 / 2520円 ( 2011-04-25 )


rcsbから結晶構造データをダウンロードするコマンドをPythonで

pdbgetというpdb-idを引数にとって構造データをまとめてダウンロードするコマンド使っていたんだけど、ちょっと前のサーバリプレースでPDBのミラリング環境が失われたので使えなくなってた。こういうの致命的だよなとは思うが今時はネットワークが太いので別にミラリングする必要性はあまりないのかな?でも遅いよなぁ。

とりあえず、自分用に。プロキシ使いたいのでFancyURLopener

import urllib
import sys
pdblist = sys.argv[1:]

proxies = {'http': 'http://[hostname]:[port]/'}
opener = urllib.FancyURLopener(proxies)

for pdbid in pdblist:
   f = opener.open('http://www.rcsb.org/pdb/files/%s.pdb.gz' % pdbid.upper())
   data = f.read()
   with open('%s.pdb.gz' % pdbid.upper(),'wb') as wf:
       wf.write(data)

ちなみに初代はperlで書いてあった。これいつ書いたんだろう?多分7,8年よりは前だと思うんだよなぁ。

use strict;
use Net::FTP;

die "usage:$0 [pdb_id] ...\n" unless @ARGV;

my $ftp = Net::FTP->new("[hostname]", Debug => 0)
    or die "Cannot connect to [hostname]: $@";
$ftp->login("pdb",'pdb')
  or die "Cannot login ", $ftp->message;

$ftp->binary();

for my $pdbid (@ARGV){
  $pdbid =~ tr/[A-Z]/[a-z]/;
  my $pdbdir = substr($pdbid,1,2);
  my $pdbfile = "pdb" . $pdbid . ".ent.gz";

  $ftp->cwd($pdbdir)
    or warn "Cannot change PDB directory ", $ftp->message;

  $ftp->get($pdbfile)
    or warn "$pdbfile : ", $ftp->message;

  $ftp->cwd('..')
    or warn "Cannot change PDB directory ", $ftp->message;
}

$ftp->quit;

ProductName Bioinformatics Programming Using Python
Mitchell L. Model
Oreilly & Associates Inc / 5119円 ( 2009-12-23 )


Sphinxの拡張(japanesesupport.py)に手を入れた

Sphinxで文書を書いていて気になるのは、謎じゃない謎の空白だ。

これはあちこちで書かれていてそのための拡張その改良版が用意されているが、自分の環境(Python2.6.6+Sphinx1.0.7)ではうごかなかった。

なんでかなーと調べてみるとtype(node.parent)が<type 'instance'>を返してたのでisinstanceで評価するようにしてみた。

def trunc_whitespace(app, doctree, docname):
    from docutils.nodes import Text, paragraph
    if not app.config.japanesesupport_trunc_whitespace:
        return
    for node in doctree.traverse(Text):
        if isinstance(node.parent, paragraph):
            newtext = node.astext()
            for c in "\n\r\t":
                newtext = newtext.replace(c, "")
            newtext = newtext.strip()
            node.parent.replace(node, Text(newtext))

def setup(app):
    app.add_config_value('japanesesupport_trunc_whitespace', True, True)
    app.connect("doctree-resolved", trunc_whitespace)

なんでかなーってのを調べるためにSphinxのソースコードを読み始めたりdocutilsのソースを読み始めたりしたけど、昨日あたりからかなり脱線し始めたのでとっとと成果物を残しておこう。

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


エキスパートPythonも読みなおそうっと