hmt-base-0.20: Music/Theory/Math/Histogram.hs
-- | c.f. Statistics.Sample.Histogram (this is much slower but doesn't require any libraries)
module Music.Theory.Math.Histogram where
import Music.Theory.List {- hmt-base -}
import Music.Theory.Math.Constant {- hmt-base -}
{- | Calculate histogram on numBins places. Returns the range of each bin and the number of elements in each.
> map (snd . bHistogram 10) [[1 .. 10],[1,1,1,2,2,3,10]] == [[1,1,1,1,1,1,1,1,1,1],[3,2,1,0,0,0,0,0,0,1]]
-}
bHistogram :: Int -> [Double] -> ([(Double, Double)], [Int])
bHistogram numBins xs =
let (lo, hi) = bHistogramRange numBins xs
d = (hi - lo) / fromIntegral numBins
step i = lo + d * fromIntegral i
lhs_seq = map step [0 .. numBins - 1]
rng_seq = map (\n -> (n, n + d)) lhs_seq
cnt_seq = map (\rng -> length (filterInRange rng xs)) rng_seq
in (rng_seq, cnt_seq)
{- | Calculate range.
> bHistogramRange 10 (replicate 10 1) == (0.9, 1.1)
> bHistogramRange 10 (replicate 10 0) == (-1, 1)
> bHistogramRange 10 [1 .. 10] == (0.5, 10.5)
> bHistogramRange 25 [1 .. 10] == (0.8125,10.1875)
-}
bHistogramRange :: Int -> [Double] -> (Double, Double)
bHistogramRange numBins xs =
let d = if numBins == 1 then 0 else (hi - lo) / ((fromIntegral numBins - 1) * 2)
(lo, hi) = minmax xs
in if numBins < 1 || null xs
then error "bHistogramRange: empty sample"
else if lo == hi
then let a = abs lo / 10
in if a < smallestNormalizedValue then (-1,1) else (lo - a, lo + a)
else (lo-d, hi+d)