読者です 読者をやめる 読者になる 読者になる

統計解析アプリ「Incanter」入門

プログラミング Clojure incanter

 Clojure製統計解析アプリ「Incanter」は無料かつjarファイル一つでお手軽に実行できる、その上Javaの豊富なライブラリを利用したり、描画に特化したProcessingという処理系でリッチなアニメーションやインターフェイスを実装できるという面白いアプリケーションです。また、JVM上で動くため、HadoopLuceneなどにシームレスで適用できますし、GoSenなど形態素解析アプリも簡単に呼び出せるため、自然言語処理テキストマイニングにも活用できます。一番重要なことは、Clojureであるということ、つまり皆さんの愛するLispでコーディング出来るということです。もう一度言いますが、Lispで統計解析が出来るという喜b(略)。


 無料の統計解析アプリというとR(あとアプリではありませんが、Python-Scipy/Numpyなど)が挙げられると思います。正直な話、IncanterはRに比べて(いや、まず比べ物にならないのでは…)、ユーザー数や利用実績では完全に劣ります。Rでは最新の解析手法が実装されていたりしますが、Incanterは割と基本的な手法しかありませんし、パッケージの数も全く見劣りします。研究や実務で使う場合はRを用いるほうが良いでしょう。ではIncanterを使う利点はどこにあるのでしょうか?IncanterはコンパイルしてJarファイルにまとめて配布することができるので、「解析結果を渡すだけではなく、エンドユーザーが使いやすい簡単な統計アプリを作りたい。しかも色々インストールして頂くこと無く手軽に!」という用途に向いています。エンドユーザーとしても、単なる報告書渡されるだけよりも、パラメタを色々変更できたほうが楽しいですよね。また、Javaと連携できるという強みがあるので、Rでは出来ない大規模解析が可能(かもしれません、やったことないので…でも夢が広がりますよね!ですよね!)。また、JavaのSwingなどを使えるため、RよりGUIアプリをとても作り易くなります。あと一番重要なことはLispってことですね。RやScipyはLispではないので駄目ですね。もはやIncanter使うしか有りませんしIncanter知らずに今まで生きてきたことを深く恥じてください。


 さて、前置きはこれくらいにして、実際Incanterを使ってみましょう。Incanter本家からIncanter.jarを落としてきて*1、Incanterを設置したディレクトリで次のコマンドを実行します。

java -jar incanter.jar incanter.main;←Windows環境の場合
java -cp incanter.jar incanter.main;←Linux環境の場合

これでClojureのREPLという処理系が起動します。では皆さん大好きHello World!から始めましょう。

(str "Hello" " World!")

で"Hello World!"と表示されます。もうこの時点でIncanterというかLispの素晴らしさの片鱗が見えてきます。"str"は引数を標準出力する関数です。まぁそこは問題ないでしょう。しかし、上のコードを見てみると、引数が複数指定されています。試しに引数をもっと増やしてみましょう。

(str "hello" " world, " "あんちべ!")
>"hello world, あんちべ!"

引数を増やしても全く問題なく動作する…素晴らしい!このまま電卓のように利用してみましょう。足し算をします。

(+ 1 2 3 4 5)
>15

1〜5までの整数を足しあわせた結果15になりました。先程のstrと同じように、複数に引数を指定しても想定通り動きますね。


 Lisp系の基礎は、まず関数名(strや+など。+もLispでは関数です)が一番左にあり、その関数に放り込む要素を関数名の右に指定し、そしてその関数が働く範囲(スコープ)を括弧で表現するというものです。関数と要素はいいとして、スコープとは何か。足し算と掛け算を組み合わせたもので説明しましょう。

(+ 1 (* 2 3))
>7

…う、分かりづらい!!と思われるかもしれませんが、落ち着いて考えてみましょう。まず、一番左の関数は+です。引数は1と(* 2 3)です。なので、1と(* 2 3)と足しあわせたモノが一番外の括弧の処理結果になりますね。そして内側の括弧は(* 2 3) = 2 * 3 = 6なので、最終的に1 + 6になり、答えは7となります。美しい…曖昧性が一欠片もない…。しかも1~5まで加算するとき、いちいち他の言語のように1 + 2 + 3 + 4 + 5みたいな無駄に+を書く必要はありません。なんてシンプル。しかもLispと言えば目も眩む括弧の数が揶揄されますが、演算もstrの例も、別段他の言語と比べて括弧が多いわけではありません。C系の言語と比較してみるとprint ("hello world!");と(str "hello world!")で、処理に対して全く余計な;などもありません。というか;とは一体何なのか!なぜそんなモノが必要なのか!





やぁ、ここまで読んでくれたあなた!その精神的耐久力に僕は感動するし、おめでとう、これからはこんな狂気な話は出てこないから安心して欲しい:D。ギャグのようだけど、上の話は、今後どうしても必要になる前置記法に慣れてもらうためには仕方なかったんだ。実はもっともっと説明しないといけないことがあるんだけど、Lispの説明をしだしたら長くなる、ここでは単にLispへの病的な恐怖心を取り除いてもらい、「わかんないところはわかんない、とりあえずサンプルコードの値を変えるところから初めて遊ぼう」と思っていただければ幸いだ。そろそろIncanterを利用していくことにしよう。おっと、一つだけ事前にdocという関数について覚えてもらいたいな。何かわからない関数があれば(doc 関数名)とやると、その関数の説明を見ることが出来る。docは君の強力な味方、わからないことがあればとにかくdocを試してみよう!


まず、Incanterの様々な統計ライブラリを利用するために、「このライブラリを使用するよ」というのを処理系に指定して教えてあげないといけません。次のコマンドをREPLに打ち込んで下さい。

(use '(incanter core stats charts io datasets))

useで処理系に引数として指定したライブラリを利用することを教えてあげる。coreはIncanterのまさにコア部分、何をするにもこれがないと始まらない。statsは統計処理、chartsはグラフ描画、ioはファイル入出力のライブラリ、datasetsはサンプルデータセットです。とりあえずこれくらい用意しておけば十分でしょう。次のようにデータセットを用意します。

(dataset ["x1" "x2" "x3"] [ [1 2 3][4 5 6] ])

これで2行3列のデータが出来ます。Excelで表示すると

x1 x2 x3
1 2 3
4 5 6

という感じになります。
これをdataという変数に束縛します。

(def data (dataset ["x1" "x2" "x3"] [ [1 2 3][4 5 6] ]))

このデータをExcelのようなインターフェイスで表示させましょう。

(view data)

簡単ですね。このように、view関数を使うとデータをGUIで表示できます。変数dataに先程の表データを束縛したので、それをファイル入出力してみましょう。

(save data "test.csv")
(read-dataset "test.csv" :header true)

なんとこれだけでCSV形式でのファイルの入出力が可能です。非常に簡単ですね。


 次に、平均と標準偏差を求めてみましょう。Incanterにはdatasetsというライブラリにサンプルデータを用意しています。datasetsは既にuseしておきましたね。サンプルデータはget-dataset関数で取得します。今回はcarsという車の速度と走行距離のデータを利用します。サンプルデータの中からcarsのデータを取得するには

(get-dataset :cars)

とします。するとspeed, distの2列50行のデータが取得出来ます。このデータのうちspeed列のデータだけ取得したい場合は$を利用します。

($ :speed (get-dataset :cars))

これをまたもやdataに束縛し、平均と標準偏差を得るには、各々mean, sd関数を用います、

(def data($ :speed (get-dataset :cars)))
(str "mean = "(mean data) " :: SD = " (sd data))
>"mean = 15.4 :: SD = 5.2876444352347844"

簡単ですね。他細々としたデータ操作について書いておきます。

($where {:speed {> 10}} data);carsデータからspeed列が10より上のデータだけ取得

(def data (get-dataset :iris));irisデータをdataに束縛
($where {:Species "setosa"} data);irisデータからSpecies列がsetosaの行だけ取得

(def data (get-dataset :hair-eye-color));髪と目の色のデータをdataに束縛
($order :count :desc data);count列を降順でソート


データの描画も簡単です。散布図を書いてみましょう。scatter-plotという関数で散布図が描けます。

(view (scatter-plot :Sepal.Length :Sepal.Width :data (get-dataset :iris)))

簡単ですが、もっとかっこいい図を書きたいですね。doc関数を利用して、オプションを調べて追加してみましょう。

(view (scatter-plot :Sepal.Length :Sepal.Width :data (get-dataset :iris)
:group-by
:Species
:title "Fisher Iris Data"
:x-label "Sepal Length (cm)"
:y-label "Sepal Width (cm)"))


棒グラフも描きましょう。

(view (bar-chart :Species :Sepal.Length :data (get-dataset :iris)))

(view (bar-chart :hair :count :group-by :eye :legend true :data (get-dataset :hair-eye-color)))


関数のグラフも簡単に描けます。

(view (function-plot sin -10 10))


綺麗なグラフが簡単にかけましたね。これを画像として保存する場合もsave関数を用います。save関数を用いるときは、次のように保存時の名前を指定します(ちなみにsave関数で画像を保存する場合はpngしか出来ないようです、多分)。

(save (function-plot sin -10 10) "func.png")


ちょっと発展的な話題として、IncanterからMongoDBを利用するのにチャレンジしてみましょう。

(use 'incanter.mongodb)
(use 'somnium.congomongo)
(mongo! :db "mydb")
(view (fetch-dataset :cars))


 お疲れ様でした!Lisp系初体験の方、如何でした?巷で言われる程難しくはなかったなー簡単に出来そうだなーという感じを持っていただければ幸いです。Lispハッカーの皆さん、如何でした?Incanterで統計解析をやる気が漲ってきませんか?簡単な計算からファイル入出力、描画、DB操作と一通りよくある操作をやってきました。皆さんがIncanterを利用し、素敵な統計解析アプリを作って下さり、社会(と重点的に僕)をもっとハッピーにしてくれることを願ってます。不明点があればご質問下さい。では、楽しいLispによる統計解析ライフを!

*1:exe版、私が試したところかなりバギーな感じなので、jar利用をお薦めします