以前、こんなエントリを書きました。
Clojure/lucene-kuromojiでテキストマイニング入門 〜形態素解析からワードカウントまで〜
http://d.hatena.ne.jp/Kazuhira/20130911/1378914422
なんですけど、ぶっちゃけこの時は元の参照エントリで使っていたIncanterの意味がわからず、単純に持ってきただけでした。出力されていたグラフの内容はさすがにわかりますが…。
で、このIncanter、覚えておくと後々便利かな〜と思い、最近少し興味があったので試してみることにしました。
Incanter
http://incanter.org/
Incanterは、Clojureで書かれた統計解析ツールらしいです。入力したデータを元に、表やグラフを表示したり図やPDFとして保存が可能な模様。
で、始めるにあたってドキュメントページを見て…
http://data-sorcery.org/contents/
Getting Startedはあるものの、以下のPDFに沿ってやった方がよい感じでした。
Introduction to datasets and charts
http://incanter.org/docs/data-sorcery-new.pdf
環境設定等は、こちらで。
Building Incanter applications with Leiningen
http://data-sorcery.org/2009/11/20/leiningen-clojars/
あとは、適宜APIを見ながら。
名前空間一覧
http://liebke.github.io/incanter/
API for core
http://liebke.github.io/incanter/core-api.html
API for charts
http://liebke.github.io/incanter/charts-api.html
API for stats
http://liebke.github.io/incanter/stats-api.html
で、今回はlein-exec形式で実行する前提とするので、用意するClojureスクリプトの冒頭にこんな感じで定義。
(require '[leiningen.exec :as exec]) (exec/deps '[[incanter "1.5.4"]]) (require '[incanter.core :as c] '[incanter.stats :as s] '[incanter.charts :as ch])
datasets名前空間は、サンプルデータが入っているそうなのですが、最終的に要らないので外しました。
ちょっと関数の挙動とかを見る時に、手軽にデータを用意できる点としてはグッドです。
あと、Clojureでのコードの書き方として、useよりもrequireの方がいいみたいな話があったかと思うんですけど、どうなんでしょうね?ドキュメントに合わせた方がいいのかな?と思いつつも、今回はrequireにしました。
dataset
こちらのドキュメントによると、まずはdatasetというものを作成するのが基本になるようです。
Introduction to datasets and charts
http://incanter.org/docs/data-sorcery-new.pdf
core/dataset関数にキーとなるベクタ、値のベクタのベクタを渡し、それをそのままviewで表示します。
(c/view (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6]]))
なるほど。
もうひとつ。Mapからも作成できるみたいです。
(c/view (c/to-dataset [{"x1" 1 "x2" 2 "x3" 3} {"x1" 4 "x2" 5 "x3" 6} {"x1" 7 "x2" 8 "x3" 9}]))
列や行の指定からも、datasetは作成できるみたいです。
core.conj-cols
(c/view (c/conj-cols [1 2 3] [4 5 6] [7 8 9]))
core.conj-rows
(c/view (c/conj-rows [1 2 3] [4 5 6] [7 8 9]))
core.conj-rowsその2
(c/view (c/conj-rows [{:a 1 :b 2 :c 3}] [[4 5 6] [7 8 9] [10 11 12]]))
演算やフィルタリング
中央値と標準偏差。
(let [[m s] (c/with-data (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]]) [(s/mean (c/$ :x1)) (s/sd (c/$ :x1))])] (assert (= m 4.0)) ;; 中央値 (assert (= s 3.0))) ;; 標準偏差
core.$で、列を指定可能みたいです。
列や行の絞り込み。
(c/with-data (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]]) (c/view (c/$ [:x1 :x2])) ;; x1とx2を表示 (c/view (c/$ [:not :x1 :x2])) ;; x1とx2以外を表示 (c/view (c/$ 0 [:not :x1 :x2])) ;; x1とx2以外の1行目を表示 (c/view (c/$ [0 2] [:not :x1 :x2]))) ;; x1とx2以外の1行目と3行目を表示
絞り込み
core.$whereを使って、データの絞り込みが可能。
(c/with-data (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]]) (c/view (c/$where {"x1" {:gt 1}}))) ;; $whereで使える演算子は、query-datasetを見るべし
コメントにも書いていますが、$whereで使用できる演算子は、core.query-datasetを参照してください。
比較演算子や、関数指定が可能なようです。
:eq
(c/view (c/$where {"x1" {:eq 4}} (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
:in。引数は、セットで指定します。
(c/view (c/$where {"x1" {:in #{1 7}}} (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
組み合わせ。
(c/view (c/$where {"x1" {:gt 2, :lt 10} "x2" {:eq 5}} (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
関数で条件を指定。
(c/view (c/$where (fn [row] (or (= (row "x1") 1) (= (row "x3") 9))) (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
ソート
ソートは、core.$orderで。
(c/view (c/$order :x3 :desc (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
scatter-plot
ここからは、グラフ?の描画へ。
chart/scatter-plotで、datasetの値のプロットが可能です。以下は、指定されたdetasetから、x1を横、x2を縦にプロットします。
(c/view (ch/scatter-plot :x1 :x2 :data (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
データが少ないので、寂しい感じになっているのはご愛嬌。
Group Byも可能みたいです。
(c/view (ch/scatter-plot :x1 :x2 :data (c/dataset ["x1" "x2" "x3"] [[1 4 "c1"] [2 8 "c2"] [3 6 "c1"] [4 7 "c2"]]) :group-by :x3))
bar-chart
続いて、棒グラフ。
以前使ったのは、この形式でdatasetを作成して渡すのではなく、ベクタを直接指定していたようです。
(c/view (ch/bar-chart [:x1 :x2] [10 5]))
datasetを使用する例。
(c/view (ch/bar-chart "x2" "x1" :data (c/dataset ["x1" "x2" "x3"] [[1 2 3] [4 5 6] [7 8 9]])))
先ほどまでのdatasetの例では使いませんでしたが、datasetに対してcore.$rollupを使うことで集合演算ができるようです。
(c/view (ch/bar-chart :x2 :x1 :data (c/$rollup :mean :x1 :x2 (c/dataset ["x1" "x2"] [[1 "c1"] [4 "c2"] [7 "c1"] [10 "c2"]]))))
今回は、x2でグループ化して、x1の中央値を集計。