Haskellでデータ処理がしたい!
この記事はIIJの@kazu_yamamotoさんの下で、アルバイトとして書いています。 最終的な目的はHaskellでの統計処理やデータ処理のツールを整備しようというものですが、その下調べをした内容をこの記事にまとめています。 ご意見などコメントいただけるとありがたいです。
今から3年前の2011年のICFPに、Emily G. Mitchell 氏のHaskellを使ったシミュレーションに関するExperience Reportが掲載されました。
[PDF] Functional Programming through Deep Time - Modeling the first complex ecosystems on Earth
このレポートでは筆者が行ったエディアカラ紀の生態系シミュレーションの実装に関してプログラミング言語の選択や、使った言語にどのような利点・欠点があったかが述べられています。 初めはRを用いていたようですが、実行速度とシミュレーションのコード修正をするのに問題があってHaskellを選択した経緯とそのときに感じたHaskellでデータ処理をする際の問題点はとても興味深いものでした。 筆者は元々Haskellの使い手だったわけではないようですが、夫があのNeil Michell氏であることもあってか、シュミレーションにはHaskellを使い、プロットなどを含むデータ処理にはRを使ったようです。
その中でRと比較したHaskellの利点として以下のようなことが挙げられています。
しかし、Haskellまわりのツールには不満点もあるようで、
- 異なるパッケージの似たようなデータ型で互いにデータを変換するのが困難であること
- Haskellパッケージのインストールが難しいこと (RはGUIでパッケージをインストールすることもできる)
- 標準ライブラリで統計量の計算やプロットができないこと
などが挙げられています。
現状Haskellの統計パッケージあたりがどういうのがあるか調べてみると、Haskell Wikiにあるのは、
の3つだけです。(hstatsは2008年で止まっている)
プロットに関しては、
といった感じです。正直、充実しているとは言い難い状況ですね。
現状の機能では大変なことを実感するには、簡単な具体例を考えてみれば分かりやすいでしょう。以下の例は、2012年のHaskell cafeにあったスレッドの一部を翻訳したものです。
練習として、GHCiを使って適当な浮動小数点数の表になっているCSVファイルを読み込んで、ある列と別の列の間で線形回帰をして、回帰直線と一緒に散布図をプロットしてみてください。残差の正規性もチェックしてみるといいかも。大きなデータも扱えるようにするには、bytestringとかattoparsecなんかも使う必要があるでしょう。x86_64のGHCiからhmatrix/gslの関数を使うと、セグメンテーションフォルトとかバスエラーになる既知のバグがあることにも気をつけましょう*1。 何か当たり前なことを見落としてるかもしれませんが、(データの)コンテナ・永続化・パース・統計・プロットをするのにどのパッケージを選ぶのにすっっごく時間がかかるでしょう。
[Haskell-cafe] Mathematics and Statistics libraries
確かにこれをHaskellでやるのはちょっと考えてみただけどもやりたくない感じですね。 まずCSVのパースなのでcassavaあたりを使うといいのでしょうか。cassavaのData.CSVモジュールを見るとdecodeというのがあるのでこれを使えば良さそうですね。でも引数がByteString型なのでbytestringパッケージのData.ByteStringモジュールをインポートしてファイルを読む必要がありますね。返り値はVector型なのでそれから必要な列を取り出して線形回帰すればいいのですが、Haskellで線形回帰をするライブラリを探してぐぐってみるとstatistics-linregがありますね。よし、これをcabalでインストールしてとかやってられません。 しかしRやPythonなら、慣れている人なら5分とかからずにやってしまうでしょう。 試しにRでやってみるとたった6行です。
library(ggplot2) # ggplot2ライブラリを読み込む data = read.csv("data.csv") # ヘッダーつきCSVファイルを読み込む data.lm = lm(y ~ x, data) # 線形回帰 plot(data.lm) # 残差の正規性をQ-Qプロットでチェック ggplot(data, aes(x=x, y=y)) + geom_point() + stat_smooth(method="lm") # 試しにプロット ggsave("data.png") # プロットを画像ファイルにセーブ
入力のCSVファイル:
id,x,y
0,0.125,2.140
1,0.065.567
2,-0.079,3.115
3,-0.193,1.765
...
プロット:
この簡潔さを可能にしているRの特徴として挙げられるのは、
- 表データを扱う組み込みのデータ構造 (データフレーム)
- データ処理のための組み込み関数 (
read.csv()
,lm()
など) - プロットのためのライブラリ (ggplot2, latticeなど)
などでしょう。 Pythonでも組み込みの機能はRほどではないにしろ、データフレームにはpandas、データ処理にはnumpy/scipy/scikit-learn、プロットにはmatplotlibがあり、IPythonを使えばRと同様インタラクティブに処理可能になります。
これを真似て処理するデータを収めるコンテナを統一的に扱えるようにし、データ処理によく使う関数をひとつのモジュールをインポートするだけで使えるようにすれば、結構Haskellでもいけるようになるのではないでしょうか。 加えて、快適に使うために欲しいものとしては、ドキュメントがサクサク読めて、データを眺めたりプロットをはめ込めるRStudioやIPython notebookのような統合的対話環境があると嬉しいです。
これらの機能を整備したとして、Haskellを使う利点は何でしょうか。 先の論文の要旨も踏まえて私の考えを3点述べると、
- 静的な型付けによる安全性とメンテナンス性の向上
- データ並列による処理の高速化
- 入出力の抽象化とストリーミング処理
これらはRやPythonではそう簡単には実現できない特徴ですので、うまくデータ処理のニーズと合致すればとても魅力的なツールになるはずです。 最後に、データ解析者やHaskellerの方々の目線から、このような機能は必須だとか、Haskellのこのライブラリを使うといいなどの意見をコメントいただけるととてもありがたいです。
*1:GHCの7.8.xでは修正されているようです。 https://twitter.com/kazu_yamamoto/status/454741146053255168