monoid-statistics 0.1 → 0.2
raw patch · 3 files changed
+287/−99 lines, 3 files
Files
- Data/Monoid/Statistics.hs +7/−98
- Data/Monoid/Statistics/Numeric.hs +278/−0
- monoid-statistics.cabal +2/−1
Data/Monoid/Statistics.hs view
@@ -11,20 +11,17 @@ module Data.Monoid.Statistics ( StatMonoid(..) , evalStatistic -- * Statistic monoids- , Count(..)- , Mean(..)+ , TwoStats(..) -- * Additional information -- $info ) where -import Data.Int (Int8, Int16, Int32, Int64)-import Data.Word (Word8,Word16,Word32,Word64,Word) import Data.Monoid import qualified Data.Foldable as F -import GHC.Float (float2Double) + -- | Monoid which corresponds to some stattics. In order to do so it -- must be commutative. In many cases it's not practical to -- construct monoids for each element so 'papennd' was added.@@ -38,6 +35,9 @@ -- -- > pappend x (pappend y mempty) == pappend x mempty `mappend` pappend y mempty -- > mappend x y == mappend y x+--+-- It is very similar to Reducer type class from monoids package but+-- require commutative monoids class Monoid m => StatMonoid m a where -- | Add one element to monoid accumulator. P stands for point in -- analogy for Pointed.@@ -47,106 +47,15 @@ -- foldl'. evalStatistic :: (F.Foldable d, StatMonoid m a) => d a -> m evalStatistic = F.foldl' (flip pappend) mempty---------------------------------------------------------------------- Data types--------------------------------------------------------------------- | Simplest statistics. Number of elements in the sample-newtype Count a = Count { calcCount :: a }- deriving Show--instance Integral a => Monoid (Count a) where- mempty = Count 0- (Count i) `mappend` (Count j) = Count (i + j)- {-# INLINE mempty #-}- {-# INLINE mappend #-} -instance (Integral a) => StatMonoid (Count a) b where- pappend _ !(Count n) = Count (n + 1)- {-# INLINE pappend #-}- - - --- | Mean of sample. Samples of Double,Float and bui;t-in integral--- types are supported------ Numeric stability of 'mappend' is not proven.-data Mean = Mean { calcMean :: Double -- ^ Current mean- , calcCountMean :: Int -- ^ Number of entries- }- deriving Show -instance Monoid Mean where- mempty = Mean 0 0- mappend !(Mean x n) !(Mean y k) = Mean ((x*n' + y*k') / (n' + k')) (n + k)- where- n' = fromIntegral n- k' = fromIntegral k- {-# INLINE mempty #-}- {-# INLINE mappend #-}---- Add one sample elemnt to Mean-addValueToMean :: (a -> Double) -> a -> Mean -> Mean-addValueToMean f !x !(Mean m n) = Mean (m + (f x - m) / fromIntegral n') n' where n' = n+1-{-# INLINE addValueToMean #-}---- Floating point-instance StatMonoid Mean Double where- pappend = addValueToMean id- {-# INLINE pappend #-}-instance StatMonoid Mean Float where- pappend = addValueToMean float2Double- {-# INLINE pappend #-}---- Basic integrals-instance StatMonoid Mean Integer where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Int where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Word where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}---- Fixed size ints-instance StatMonoid Mean Int8 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Int16 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Int32 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Int64 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}---- Fixed size Words-instance StatMonoid Mean Word8 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Word16 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Word32 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-instance StatMonoid Mean Word64 where- pappend = addValueToMean fromIntegral- {-# INLINE pappend #-}-- ---------------------------------------------------------------- -- Generic monoids ---------------------------------------------------------------- -- | Monoid which allows to calculate two statistics in parralel-data TwoStats a b = TwoStats { calcStat1 :: a- , calcStat2 :: b+data TwoStats a b = TwoStats { calcStat1 :: !a+ , calcStat2 :: !b } instance (Monoid a, Monoid b) => Monoid (TwoStats a b) where
+ Data/Monoid/Statistics/Numeric.hs view
@@ -0,0 +1,278 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Data.Monoid.Statistics.Numeric ( + -- * Mean and variance+ Count(..)+ , asCount+ , Mean(..)+ , asMean+ , Variance(..)+ , asVariance+ -- ** Ad-hoc accessors+ , CalcCount(..)+ , CalcMean(..)+ , CalcVariance(..)+ , calcStddev+ , calcStddevUnbiased+ -- * Maximum and minimum+ , Max(..)+ , Min(..)+ -- * Conversion to Double+ , ConvertibleToDouble(..)+ ) where++import Data.Int (Int8, Int16, Int32, Int64)+import Data.Word (Word8,Word16,Word32,Word64,Word)+import GHC.Float (float2Double)++import Data.Monoid+import Data.Monoid.Statistics+++----------------------------------------------------------------+-- Statistical monoids+----------------------------------------------------------------++-- | Simplest statistics. Number of elements in the sample+newtype Count a = Count { calcCountI :: a }+ deriving Show++-- | Fix type of monoid+asCount :: Count a -> Count a+asCount = id+{-# INLINE asCount #-}++instance Integral a => Monoid (Count a) where+ mempty = Count 0+ (Count i) `mappend` (Count j) = Count (i + j)+ {-# INLINE mempty #-}+ {-# INLINE mappend #-}+ +instance (Integral a) => StatMonoid (Count a) b where+ pappend _ !(Count n) = Count (n + 1)+ {-# INLINE pappend #-}++instance CalcCount (Count Int) where+ calcCount = calcCountI+ {-# INLINE calcCount #-}+++++-- | Mean of sample. Samples of Double,Float and bui;t-in integral+-- types are supported+--+-- Numeric stability of 'mappend' is not proven.+data Mean = Mean {-# UNPACK #-} !Int -- Number of entries+ {-# UNPACK #-} !Double -- Current mean+ deriving Show++-- | Fix type of monoid+asMean :: Mean -> Mean+asMean = id+{-# INLINE asMean #-}++instance Monoid Mean where+ mempty = Mean 0 0+ mappend !(Mean n x) !(Mean k y) = Mean (n + k) ((x*n' + y*k') / (n' + k')) + where+ n' = fromIntegral n+ k' = fromIntegral k+ {-# INLINE mempty #-}+ {-# INLINE mappend #-}++instance ConvertibleToDouble a => StatMonoid Mean a where+ pappend !x !(Mean n m) = Mean n' (m + (toDouble x - m) / fromIntegral n') where n' = n+1+ {-# INLINE pappend #-}++instance CalcCount Mean where+ calcCount (Mean n _) = n+ {-# INLINE calcCount #-}+instance CalcMean Mean where+ calcMean (Mean _ m) = m+ {-# INLINE calcMean #-}+++++-- | Intermediate quantities to calculate the standard deviation.+data Variance = Variance {-# UNPACK #-} !Int -- Number of elements in the sample+ {-# UNPACK #-} !Double -- Current sum of elements of sample+ {-# UNPACK #-} !Double -- Current sum of squares of deviations from current mean+ deriving Show++-- | Fix type of monoid+asVariance :: Variance -> Variance+asVariance = id+{-# INLINE asVariance #-}++-- | Using parallel algorithm from:+-- +-- Chan, Tony F.; Golub, Gene H.; LeVeque, Randall J. (1979),+-- Updating Formulae and a Pairwise Algorithm for Computing Sample+-- Variances., Technical Report STAN-CS-79-773, Department of+-- Computer Science, Stanford University. Page 4.+-- +-- <ftp://reports.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf>+--+instance Monoid Variance where+ mempty = Variance 0 0 0+ mappend !(Variance n1 ta sa) !(Variance n2 tb sb) = Variance (n1+n2) (ta+tb) sumsq+ where+ na = fromIntegral n1+ nb = fromIntegral n2+ nom = sqr (ta * nb - tb * na)+ sumsq+ | n1 == 0 || n2 == 0 = sa + sb -- because either sa or sb should be 0+ | otherwise = sa + sb + nom / ((na + nb) * na * nb)+ {-# INLINE mempty #-}+ {-# INLINE mappend #-}++instance ConvertibleToDouble a => StatMonoid Variance a where+ -- Can be implemented directly as in Welford-Knuth algorithm.+ pappend !x !s = s `mappend` (Variance 1 (toDouble x) 0)+ {-# INLINE pappend #-}++instance CalcCount Variance where+ calcCount (Variance n _ _) = n+ {-# INLINE calcCount #-}+instance CalcMean Variance where+ calcMean (Variance n t _) = t / fromIntegral n+ {-# INLINE calcMean #-}+instance CalcVariance Variance where+ calcVariance (Variance n _ s) = s / fromIntegral n+ calcVarianceUnbiased (Variance n _ s) = s / fromIntegral (n-1)+ {-# INLINE calcVariance #-}+ {-# INLINE calcVarianceUnbiased #-}++++++-- | Calculate minimum of sample. For empty sample returns NaN. Any+-- NaN encountedred will be ignored. +newtype Min = Min { calcMin :: Double }+ deriving Show++-- N.B. forall (x :: Double) (x <= NaN) == False+instance Monoid Min where+ mempty = Min (0/0)+ mappend !(Min x) !(Min y) = Min $ if x <= y then x else y+ {-# INLINE mempty #-}+ {-# INLINE mappend #-} ++instance StatMonoid Min Double where+ pappend !x m = mappend (Min x) m+ {-# INLINE pappend #-}+++++-- | Calculate maximum of sample. For empty sample returns NaN. Any+-- NaN encountedred will be ignored. +newtype Max = Max { calcMax :: Double }+ deriving Show++instance Monoid Max where+ mempty = Max (0/0)+ mappend !(Max x) !(Max y) = Max $ if x >= y then x else y+ {-# INLINE mempty #-}+ {-# INLINE mappend #-} ++instance StatMonoid Max Double where+ pappend !x m = mappend (Max x) m+ {-# INLINE pappend #-}+++++----------------------------------------------------------------+-- Ad-hoc type class+----------------------------------------------------------------+ +class CalcCount m where+ -- | Number of elements in sample+ calcCount :: m -> Int++class CalcMean m where+ -- | Calculate esimate of mean of a sample+ calcMean :: m -> Double+ +class CalcVariance m where+ -- | Calculate biased estimate of variance+ calcVariance :: m -> Double+ -- | Calculate unbiased estimate of the variance, where the+ -- denominator is $n-1$.+ calcVarianceUnbiased :: m -> Double++-- | Calculate sample standard deviation (biased estimator, $s$, where+-- the denominator is $n-1$).+calcStddev :: CalcVariance m => m -> Double+calcStddev = sqrt . calcVariance+{-# INLINE calcStddev #-}++-- | Calculate standard deviation of the sample+-- (unbiased estimator, $\sigma$, where the denominator is $n$).+calcStddevUnbiased :: CalcVariance m => m -> Double+calcStddevUnbiased = sqrt . calcVarianceUnbiased+{-# INLINE calcStddevUnbiased #-}++++----------------------------------------------------------------+-- Conversion to Double+----------------------------------------------------------------++-- | Data type which could be convered to Double+class ConvertibleToDouble a where+ toDouble :: a -> Double+ +-- Floating point+instance ConvertibleToDouble Double where+ toDouble = id+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Float where+ toDouble = float2Double+ {-# INLINE toDouble #-}+-- Basic integral types+instance ConvertibleToDouble Integer where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Int where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Word where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+-- Integral types with fixed size+instance ConvertibleToDouble Int8 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Int16 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Int32 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Int64 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Word8 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Word16 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Word32 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}+instance ConvertibleToDouble Word64 where+ toDouble = fromIntegral+ {-# INLINE toDouble #-}++ +sqr :: Double -> Double+sqr x = x * x+{-# INLINE sqr #-}
monoid-statistics.cabal view
@@ -1,5 +1,5 @@ Name: monoid-statistics-Version: 0.1+Version: 0.2 Cabal-Version: >= 1.6 License: BSD3 License-File: LICENSE@@ -23,3 +23,4 @@ Library Build-Depends: base >=3 && <5 Exposed-modules: Data.Monoid.Statistics+ Data.Monoid.Statistics.Numeric