久しぶりの更新ですが今日は Ruby のアプリケーションフレームワーク Sinatra を利用してデータを表示するアプリケーションを作る話です。
データ分析処理とウェブアプリケーションを分離する
データ分析には、複雑な数理計算、特定目的の科学計算、機械学習などの高度な計算を駆使することもあります。このような分野には科学技術計算に向いた R や Python などのプログラム言語を用いることが通例ですが、必ずしもウェブアプリケーションの部分までこれらを利用する必要はないでしょう。どう可視化するよりも何を可視化するかのほうがずっと大切だからです。プレゼンテーション層としてのウェブアプリケーション部分と、ビジネスロジック層としてのデータ分析処理部分は、それぞれ分かれていても良いはずです。むしろウェブアプリケーションで統計などの処理まで担うのは、ある意味ビューにロジックを書いてしまうようなものと考えることもできるかもしれません。このように割り切るならば、ウェブの部分は Ruby などの手軽な言語を用いて実装しても良いですし、あるいは D3.js などによってインタラクションを実装しても良いでしょう。
インタラクションの向き不向き
探索的データ分析ではインタラクションによって多角的なデータの探索を可能にします。インタラクションはポインティングデバイスなどによる操作で対話的にデータを閲覧することになります。これは対話的にデータを読み解いていこうという場面では威力を発揮します。反面、一目で全体像を把握したい、あるいはチェックする項目が決まっていてそこだけ迅速に確認したい、といったニーズにはやや不向きな面があります。たとえば数十社、数百社の経営情報を閲覧したいというニーズがあったとします。このとき、一社ごとの詳細情報表示画面でいちいちポインティングデバイスを利用して情報を追っていたのでは、全社分を閲覧するのに時間と労力がかかってしまうことになります。また全体の概況をインタラクションで表示するにしても、見た瞬間に全体がわかるという迅速性のほうが重視されることになるでしょう。インタラクションは見た目がスマートで見栄えがするのですが、どのような場面にも向いているというわけではないことに注意が必要です。
どんなデータの可視化にも使えるシンプルな可視化
たとえば経営情報の分析とその可視化を考えてみます。この場合では、全体の概況一覧と個別の財務諸表の詳細を可視化するような形になるでしょう。あるいは業界のトレンドを可視化するなら業界全体の動向と各社の戦略についての詳細を用意するでしょう。このように、トップに全体の一覧や要約の画面を用意し、そこから各個別の詳細画面に飛ぶといったスタイルは、可視化の中でも定番ともいえる見せ方です。ここでは一覧と詳細画面から構成されるシンプルなウェブアプリケーションを用意することを考えてみましょう。
Sinatra でシンプルな可視化アプリケーションを実装する
Sinatra の最低限のルーティングと Haml を利用して上記の要件を満たすサイトが手軽に作れます。
ルーティングを実装する
get '/' do
@data = open_file(index_file)
haml :index
end
get '/:param' do
@data = open_file(@params[:param])
redirect '/' if @data.length == 0
haml :detail
end
いかがでしょう。記述はきわめて簡潔です。それぞれ、 / にアクセスしたらインデックスを構築するためのサマリーとなるデータファイルを、各詳細画面に飛ぼうとしたらそれに対応したデータファイルを開くという処理をしているだけにすぎません。
上の例では一様に同じ詳細画面に飛ばしていますが
haml @params[:param].to_sym
というふうに分岐先を個別の画面にすることもできます。
データファイルを開くための関数を用意する
次に上で登場するデータファイルを開くメソッドを用意しておきます。
def open_file(filename, &block)
open(filename) do |file|
file.each_line do |line|
block.call(line)
end
end
end
これでファイル名とブロックを渡せばファイルを開けるようになります。
あとはデータファイルがどういう形式かにあわせてブロックを用意します。いわゆる Key と Value 形式で Value に JSON オブジェクトが入っているなら
parse_json = lambda {|line|
hash = {}
# キーとバリューに分割
hash['key'], hash['tag'], json = line.strip.split("\t")
# バリューを JSON パーサーで処理する
begin
hash['value'] = JSON.parse(json)
rescue JSON::ParserError
hash['value'] = json
end
array << hash
}
などのようにブロックを用意しておいて
open_file(filename, &parse_json) if File.exist?(filename) if /\.json\Z/ =~ filename
のようにブロックを渡します。
データ分析処理を実装する
あとは好きなように分析をします。これは上述したように R や Python で深夜などに定期実行するといった形で良いでしょう。
まとめ
今回のようにシンプルなウェブアプリケーションを用意しておけばなにかと応用が効くので便利です。