人気ブログランキング | 話題のタグを見る

Haskell でヒストグラムを作る。

Haskell でヒストグラムを作るにはどうすればいいかを考えてみた。

まずはデータの作成から。サイコロを2つ振った時の出た目の合計のリスト scores を作った。

Prelude> let scores = [x + y | x <- [1..6], y <- [1..6]]
Prelude> scores
[2,3,4,5,6,7,3,4,5,6,7,8,4,5,6,7,8,9,5,6,7,8,9,10,6,7,8,9,10,11,7,8,9,
10,11,12]

scores の最小値と最大値を求めた。

Prelude> let smin = minimum scores
Prelude> smin
2
Prelude> let smax = maximum scores
Prelude> smax
12

ヒストグラムの区間の幅を 1 にした。

Prelude> let binWidth = 1

データの最小値と最大値が収まるような区間を求めた。

Prelude> let bins = [smin, (smin + binWidth)..(smax + binWidth)]
Prelude> bins
[2,3,4,5,6,7,8,9,10,11,12,13]

区間の境界値の組を作った。

Prelude> let intervals = zipWith (\x y -> (x,y)) (init bins) (tail bins)
Prelude> intervals
[(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10),(10,11),(11,12),(12,13)]

x が区間 u 内に入っているかどうかを判別する関数 within を作った。

Prelude> let within u x = x >= fst u && x < snd u
Prelude> within (3,4) 3
True

scores の中で (5,6) の中に入っている値を取り出した。

Prelude> filter (within (5,6)) scores
[5,5,5,5]

全ての区間で上と同じ事をやった。

Prelude> map (\u -> filter (within u) scores) intervals
[[2],[3,3],[4,4,4],[5,5,5,5],[6,6,6,6,6],[7,7,7,7,7,7],[8,8,8,8,8],[9,9,9,9],[10,10,10],[11,11],[12]]

出現頻度を数えた。

Prelude> map length $ map (\u -> filter (within u) scores) intervals
[1,2,3,4,5,6,5,4,3,2,1]

区間とペアにした。

Prelude> zip intervals $ map length $ map (\u -> filter (within u) scores) intervals
[((2,3),1),((3,4),2),((4,5),3),((5,6),4),((6,7),5),((7,8),6),((8,9),5),((9,10),4),((10,11),3),((11,12),2),((12,13),1)]

ghci で試行錯誤しながら作れるので、頭が疲れない。

そこで、これらをまとめて関数 histogram を作ってみた。開発はボトムアップで行ったが、それをもとに histogram はトップダウンで開発する。Haskell では関数はどの順序で記述してもいいので余計な配慮が要らないのが楽だ。ボトムアップで開発してトップダウンで記述するといっても、関数の定義を単に並べ替えるだけでプログラミングができてしまう。やってみると分かるが、すごくうれしい。

ファイル名: test.hs

histogram leftmost binwidth rawdata =
  zip intervals $ map length $ map (\u -> filter (within u) rawdata) intervals
  where
    intervals = zipWith (\x y -> (x,y)) (init bins) (tail bins)
    bins = [leftmost, (leftmost + binwidth)..(smax + binwidth)]
    smax = maximum rawdata
    within u x = x >= fst u && x < snd u

実行例:

*Main> let scores = [x + y | x <- [1..6], y <- [1..6]]
*Main> histogram 2 1 scores
[((2,3),1),((3,4),2),((4,5),3),((5,6),4),((6,7),5),((7,8),6),((8,9),5),((9,10),4),((10,11),3),((11,12),2),((12,13),1)]

実用的に使うためには、数値の型にも配慮しないといけないが、それは宿題ということで。
by tnomura9 | 2012-07-04 18:52 | Haskell | Comments(0)
<< Dancing Dolls RWH の読み方(56) 第7章 >>