Haskell プログラム集


Haskell で基本統計量の計算

基本統計量の計算

Haskell で基本統計量の計算をしてみた。最初は ghci で対話的に計算してみた。

Prelude> let raw_data = [1,2,3,4,5,6]
Prelude> raw_data
[1,2,3,4,5,6]
Prelude> maximum raw_data
6
Prelude> minimum raw_data
1
Prelude> let n = length raw_data
Prelude> n
6
Prelude> let mean = (sum raw_data) / fromIntegral n
Prelude> mean
3.5
Prelude> let variance = (sum $ map (\x -> (x - m) * (x - m)) raw_data) /
 fromIntegral n
Prelude> variance
2.9166666666666665
Prelude> let sd = sqrt variance
Prelude> sd
1.707825127659933

基本統計計算プログラム

動くことが確認できたのでプログラムをファイルに作成した。

module Statistics where

statistics xs =
  let
    n = length xs
    mean = (sum xs) / fromIntegral n
    variance = (sum $ map (\x -> (x - mean) * (x - mean)) xs) / 
fromIntegral n
    sd = sqrt variance
  in
    [("n", fromIntegral n), ("mean", mean), 
("variance", variance), ("sd", sd)]

戻り値で Int の n をわざわざ fromIntegral で double にしているが、データ種別とデータ値のペアの型を揃えるためだ。そうしておくと、これらをリストにまとめることができる。リストでは lookup 関数でデータの検索ができる。プログラムを ghci で動作検証した。

Prelude> :l Statistics.hs
[1 of 1] Compiling Statistics ( Statistics.hs, interpreted )
Ok, modules loaded: Statistics.
*Statistics> let result = statistics [1,2,3,4,5,6]
*Statistics> result
[("n",6.0),("mean",3.5),("variance",2.9166666666666665),
("sd",1.707825127659933)]
*Statistics> lookup "variance" result
Just 2.9166666666666665

Data.Maybe モジュールを追加すれば、fromJust 関数で Just コンテナのデータを取り出すことができる。

*Statistics> :m +Data.Maybe
*Statistics Data.Maybe> fromJust $ lookup "variance" result
2.9166666666666665

let ~ in 構文を利用すれば、do 記法に頼らなくても手続き型プログラムに似たプログラムが書ける。上のプログラムは IO モナドではなく、純粋関数プログラムだが、let と in に囲まれた部分を見るとまるで手続き型のプログラムで記述されたかのように見える。

Haskell の名前付きフィールド

Haskell では代数的データ型で名前付きフィールドが使える。上の基本統計量の計算プログラムを名前付きフィールドを使うように変更したのが次のプログラムだ。

module Statistics where

data Stat = Stat {n :: Int, mean :: Double, variance :: Double, 
sd :: Double}
  deriving Show

statistics xs =
  let
    n = length xs
    mean = (sum xs) / fromIntegral n
    variance = (sum $ map (\x -> (x - mean) * (x - mean)) xs) / 
fromIntegral n
    sd = sqrt variance
  in
    Stat n mean variance sd

上の data 宣言で名前付きフィールドが使えるようにしている。名前付きフィールドの名前の部分は Stat コンテナからその名前のデータを取り出すための関数名になる。したがって、フィールド名の名前空間はトップレベルになるので名前の衝突に注意が必要だ。

ghci での実行例は次のようになる。

Prelude> :l Statistics
[1 of 1] Compiling Statistics ( Statistics.hs, interpreted )
Ok, modules loaded: Statistics.
*Statistics> let result = statistics [1,2,3,4,5,6]
*Statistics> result
Stat {n = 6, mean = 3.5, variance = 2.9166666666666665, 
sd = 1.707825127659933}
*Statistics> variance result
2.9166666666666665