りんごがでている

何か役に立つことを書きます

Joneswareの世界

Julia Advent Calendar 2日目の記事です。

JoneswareというのはJulia界の重要人物のひとりであるdcjonesさんの作ったパッケージをさしています。 実は私のJulia Summer of Codeプロジェクトのメンターになってくれた方で、今年のJuliaConでお会いしたこともあります。

彼の書くプログラムは非常に洗練されていて、その設計やスタイルは非常に学ぶところが多いです。 その上、ハイパフォーマンスなコードばかりで、Juliaで技術計算を始める方は是非参考にしてみると良いと思います。

グラフィックスなど

Gadfly.jl

最初は皆さんご存知のGadfly.jlです。 これはggplot2のような"Grammar of Graphics"のアイディアに則ったプロットのためのパッケージです。 具体例はマニュアルを見ていただけると分かるのですが、簡潔なAPIで目的のプロットを行うことができます。

plot(x=rand(10), y=rand(10), Geom.point, Geom.line)

f:id:bicycle1885:20151201165328p:plain

Compose.jl

Compose.jlベクター画像を作るためのパッケージです。 Gadfly.jlはこのCompose.jlの上に作られています。 Compose.jlそれ自体でかなり有用なパッケージですので、ここでもご紹介しましょう。 Jupyter NotebookIJulia.jlを使うと、実際に以下のプロットを試すことができます。

Compose.jlでは、図形を組み合わせて、ベクター画像を作ります。 たとえば、polygon([(1, 1), (0, 1), (1/2, 0)]は3つの頂点 (1, 1), (0, 1), (1/2, 0) を持つ多角形を作ります。 そして、context()でその図形が置かれる座標系を指定でき、compose()で座標系と図形を組み合わせます: f:id:bicycle1885:20151201171505p:plain

context(x0, y0, width, height)のように座標系を指定できるため、以下のようにcontext(0, 1/2, 1/2, 1/2)とするとy軸方向に1/2ずらした、幅と高さが1/2の座標系となります: f:id:bicycle1885:20151201172045p:plain

複数の図形を組み合わせるのも簡単で、composeの呼び出しをネストするだけです: f:id:bicycle1885:20151201172224p:plain

再帰呼び出しと組み合わせて、シェルピンスキーの三角形のようなフラクタル図形も描けます: f:id:bicycle1885:20151201172341p:plain

fillなどで色などの属性も指定できます: f:id:bicycle1885:20151201172638p:plain

このように複雑な図形を単純な要素の組み合わせで表現できるため、ベクター画像で何かを可視化したい時はこのパッケージを使うと良いでしょう。 Jupyter NotebookではレンダリングのためSVGのバックエンドを使っていますが、Cairo.jlなどを通してPNGなどの画像ファイルを出力することもできます。 また、このパッケージに触発された、3D版のパッケージ Compose3D.jlもあります。

その他

他にもLocal regression (loess) というノンパラメトリックな回帰のパッケージLoess.jlや、YAMLのパーサYAML.jlC言語のようなswitch文を可能にするSwitch.jlも彼のパッケージです。

バイオ関係

Jonesさんはバイオインフォマティクスを研究している博士課程の学生(のはず)なので、バイオ関係のパッケージもいくつも作っています。 ここではそれらのパッケージの紹介をしていきます。

Bio.jl

バイオインフォマティクスのライブラリ、Bio.jlも私にとっては重要です。Jonesさんはこのパッケージの非常に重要な部分を作っています。 このパッケージの紹介はまた今度書きましょう。

JuliaConでのトークは、YouTubeで公開されています。

www.youtube.com

IntervalTrees.jl

IntervalTrees.jlは区間木の実装です。バイオインフォマティクスでは、ゲノムや染色体のある区間を扱いたいことがよくあります。 例えば、ヒトの遺伝子とエキソン領域のアノテーション情報をもとに、見つかった変異がどの遺伝子の何番目のエキソン領域にあるかを知りたい場合などです。

オーバーラップしている区間を取り出すにはintersectを使います:

julia> using IntervalTrees

julia> im = IntervalMap{Int,ASCIIString}()
IntervalTrees.IntervalBTree{Int64,IntervalTrees.IntervalValue{Int64,ASCIIString},64}

julia> im[(1, 100)] = "foo"
"foo"

julia> im[(50, 100)] = "bar"
"bar"

julia> im[(101, 100)] = "baz"
"baz"

julia> collect(intersect(im, (10, 20)))
1-element Array{Any,1}:
 IntervalTrees.IntervalValue{Int64,ASCIIString}
(1,100) => foo

julia> collect(intersect(im, (10, 80)))
2-element Array{Any,1}:
 IntervalTrees.IntervalValue{Int64,ASCIIString}
(1,100) => foo
 IntervalTrees.IntervalValue{Int64,ASCIIString}
(50,100) => bar

このパッケージはBio.jlでもゲノムのアノテーション情報などを保持するのに使われています。

BufferedStreams.jl

BufferedStreams.jlはIOのバッファリングをより積極的に行い、IOを高速化します。 次に述べるLibz.jlやRagelで使われています。

Libz.jl

Libz.jlはzlibへのバインディングですが、速度に非常に重点を置いています。 インターフェースも良い感じで、例えば圧縮されたテキストファイルを一行ずつ扱うには以下のようにできます:

using Libz

let
    filepath = shift!(ARGS)
    open(filepath) do s
        s′ = ZlibInflateInputStream(s)
        for line in eachline(s′)
            @show line
        end
    end
end

Ragel

Ragelは正規言語からパーサを生成するコンパイラです。 と言っても、別に正規文法しか使えないわけではなく、有限オートマトンの状態遷移の間にホスト言語での操作を挟めるため、わりと色々パースできると思います。

このコンパイラ自体はAdrian D. Thurston氏のプロダクトなのですが、そのJulia版サポートを書いたのがJonesさんなわけです。彼はJuliaをRagelに対応させるためにgoto文までJuliaに実装しています。 今のところ、このレポジトリからJulia対応版を使うことができますが、次のRagelのリリースではJuliaが正式にサポートされる予定です。

例えば、FASTA形式のファイルをパースする場合は、以下のように記述することができます:

    action count_line  { state.linenum += 1 }
    action mark        { Ragel.@anchor! }
    action identifier  { Ragel.@copy_from_anchor!(output.name) }
    action description { Ragel.@copy_from_anchor!(output.metadata.description) }
    action letters     { Ragel.@append_from_anchor!(input.seqbuf) }

    newline     = '\r'? '\n'     >count_line;
    hspace      = [ \t\v];
    whitespace  = space | newline;

    identifier  = (any - space)+            >mark  %identifier;
    description = ((any - hspace) [^\r\n]*) >mark  %description;
    letters     = (any - space - '>')+      >mark  %letters;
    sequence    = whitespace* letters? (whitespace+ letters)*;
    fasta_entry = '>' identifier (hspace+ description)? newline sequence whitespace*;

    main := whitespace* (fasta_entry %finish_match)**;

fasta.rl

Ragelを使えば、pure Juliaで高速なパーサを生成することができるため、様々なフォーマットのファイルを扱うバイオインフォマティクスでは大変意義のあるツールです。 正式版が出たら、また解説を書くかもしれません。