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