module Simulation.Aivika.Experiment.Histogram
(
Histogram(..),
histogram,
histogramBinSize,
histogramNumBins,
BinningStrategy(..),
binSturges,
binDoane,
binSqrt,
binScott) where
import Data.List
import Data.Monoid
import qualified Data.Map as Map
import Numeric
type Histogram = [(Double, [Int])]
type BinningStrategy = [Double] -> Int
stratFromBinWidth :: [Double] -> Double -> Int
stratFromBinWidth xs h = ceiling $ (maximum xs minimum xs) / h
binSturges :: BinningStrategy
binSturges xs = ceiling $ logBase 2 n + 1
where n = fromIntegral $ length xs
binDoane :: BinningStrategy
binDoane xs = ceiling $ 1 + log n + log (1 + a * ((n/6)**(1/2)))
where
n = fromIntegral $ length xs
a = kurtosis xs
binSqrt :: BinningStrategy
binSqrt xs = round $ sqrt n
where
n = fromIntegral $ length xs
binScott :: BinningStrategy
binScott xs = stratFromBinWidth xs $ 3.5 * stddev xs / (n**(1/3))
where
n = fromIntegral $ length xs
histogram :: BinningStrategy -> [[Double]] -> Histogram
histogram strat xs = histogramNumBins (strat $ concat xs) xs
histogramBinSize :: Double -> [[Double]] -> Histogram
histogramBinSize size = combineBins . map (histBins size . bin size)
histogramNumBins :: Int -> [[Double]] -> Histogram
histogramNumBins n xs =
histogramBinSize size xs
where
size = fromIntegral (firstdigit diff) * (10 ** fromIntegral (exponent10 diff))
diff = if diff_test > 0
then diff_test
else 1
diff_test = (maximum (map maximum xs)
minimum (map minimum xs)) / fromIntegral (max 1 n)
firstdigit dbl = floor $ dbl / (10 ** fromIntegral (exponent10 dbl))
exponent10 dbl = floor $ logBase 10 dbl
histBins :: Double -> [Double] -> [(Double, Int)]
histBins size xs = [ (head l, length l) | l <- group (sort xs) ]
bin :: Double -> [Double] -> [Double]
bin size xs @ [_] = xs
bin size xs = map (\x -> size * fromIntegral (floor (x / size)) + size / 2) xs
combineBins :: [[(Double, Int)]] -> [(Double, [Int])]
combineBins [xs] = map (\(t, n) -> (t, [n])) xs
combineBins xss = combineBins' xss
combineBins' :: [[(Double, Int)]] -> [(Double, [Int])]
combineBins' xs = map (merging . sorting) $
groupping $ ordering $ concat $ numbering xs
where numbering = zipWith (curry indexing) [1..]
indexing (i, x) = map (\(t, n) -> (i, t, n)) x
ordering = sortBy $ \(_, t1, _) (_, t2, _) -> compare t1 t2
groupping = groupBy $ \(_, t1, _) (_, t2, _) -> t1 == t2
sorting = sortBy $ \(i1, _, _) (i2, _, _) -> compare i1 i2
merging zs @ ((_, t, _) : _) = merging' zs t 1 []
merging' [] t k acc
| k <= count = merging' [] t (k + 1) (0 : acc)
| k > count = (t, reverse acc)
merging' zs @ ((i, _, n) : xs) t k acc
| i == k = merging' xs t (k + 1) (n : acc)
| i > k = merging' zs t (k + 1) (0 : acc)
count = length xs
mean :: Floating a => [a] -> a
mean x = fst $ foldl' (\(m, n) x -> (m+(xm)/(n+1),n+1)) (0,0) x
stddev :: (Floating a) => [a] -> a
stddev xs = sqrt $ var xs
var :: (Floating a) => [a] -> a
var xs = var' 0 0 0 xs / fromIntegral (length xs 1)
where
var' _ _ s [] = s
var' m n s (x:xs) = var' nm (n + 1) (s + delta * (x nm)) xs
where
delta = x m
nm = m + delta / fromIntegral (n + 1)
kurtosis :: (Floating a) => [a] -> a
kurtosis xs = ((1/n) * sum [(xx_bar)^4 | x <- xs])
/ ((1/n) * sum [(xx_bar)^2 | x <- xs])^2 3
where
n = fromIntegral $ length xs
x_bar = mean xs