packages feed

foldl-incremental 0.1.1.0 → 0.2.0.0

raw patch · 8 files changed

+664/−55 lines, 8 filesdep +QuickCheckdep +containersdep +deepseqdep −hastachedep ~foldldep ~foldl-incrementalPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, containers, deepseq, histogram-fill, mwc-random, pipes, vector

Dependencies removed: hastache

Dependency ranges changed: foldl, foldl-incremental

API changes (from Hackage documentation)

- Control.Foldl.Incremental: incAbs :: Double -> Fold Double Double
- Control.Foldl.Incremental: incMa :: Double -> Fold Double Double
- Control.Foldl.Incremental: incSq :: Double -> Fold Double Double
- Control.Foldl.Incremental: incStd :: Double -> Fold Double Double
+ Control.Foldl.Incremental: absma :: Double -> Fold Double Double
+ Control.Foldl.Incremental: alpha :: Double -> Fold (Double, Double) Double
+ Control.Foldl.Incremental: autocorr :: Double -> Double -> Fold Double Double
+ Control.Foldl.Incremental: beta :: Double -> Fold (Double, Double) Double
+ Control.Foldl.Incremental: corr :: Double -> Fold (Double, Double) Double
+ Control.Foldl.Incremental: cov :: Double -> Fold (Double, Double) Double
+ Control.Foldl.Incremental: length :: Double -> Fold a Double
+ Control.Foldl.Incremental: ma :: Double -> Fold Double Double
+ Control.Foldl.Incremental: sqma :: Double -> Fold Double Double
+ Control.Foldl.Incremental: std :: Double -> Fold Double Double
+ Control.Foldl.Incremental.Histogram: incAdaptiveHist :: Double -> Double -> Int -> Double -> Fold Double (Histogram BinDU Double)
+ Control.Foldl.Incremental.Histogram: incHist :: BinD -> Double -> Fold Double (Histogram BinD Double)
+ Control.Foldl.Incremental.Histogram: incHist2D :: Bin2D BinD BinD -> Double -> Fold (Double, Double) (Histogram (Bin2D BinD BinD) Double)
+ Control.Foldl.Incremental.Histogram: incrementalizeHist :: BinD -> (Double -> Double) -> Double -> Fold Double (Histogram BinD Double)
+ Control.Foldl.Incremental.Histogram: incrementalizeHist2D :: Bin2D BinD BinD -> ((Double, Double) -> Double) -> Double -> Fold (Double, Double) (Histogram (Bin2D BinD BinD) Double)
+ Control.Foldl.Incremental.Histogram: instance Show IncrementHist2D
+ Control.Foldl.Incremental.Simple: incrementalizeSimple :: (a -> Double) -> Int -> Fold a Double
+ Control.Foldl.Incremental.Simple: sabsma :: Int -> Fold Double Double
+ Control.Foldl.Incremental.Simple: sma :: Int -> Fold Double Double
+ Control.Foldl.Incremental.Simple: ssqma :: Int -> Fold Double Double
+ Control.Foldl.Incremental.Simple: sstd :: Int -> Fold Double Double
+ Data.Histogram.Adaptable: insertAt :: Histogram BinDU Double -> Double -> Histogram BinDU Double
+ Data.Histogram.Adaptable: mergeAtCut :: HistogramDU -> Int -> HistogramDU
+ Data.Histogram.Adaptable: mergeSmallest :: HistogramDU -> HistogramDU
+ Data.Histogram.Adaptable: mergeSmallestSingle :: HistogramDU -> HistogramDU
+ Data.Histogram.Adaptable: sliceAt :: Histogram BinDU Double -> Double -> Histogram BinDU Double
+ Data.Histogram.Adaptable: smallestCutContiguous :: (Bin b, Unbox a, Ord a, Num a) => Histogram b a -> Int
+ Data.Histogram.Adaptable: smallestCutSingle :: (Bin b, Unbox a, Ord a, Num a) => Histogram b a -> Int
+ Data.Histogram.Adaptable: type HistogramDU = Histogram BinDU Double
+ Data.Histogram.Bin.BinDU: BinDU :: !(Vector Double) -> BinDU
+ Data.Histogram.Bin.BinDU: addCut :: AdaptableBin b => b -> Double -> b
+ Data.Histogram.Bin.BinDU: binDU :: Vector Double -> BinDU
+ Data.Histogram.Bin.BinDU: class Bin b => AdaptableBin b
+ Data.Histogram.Bin.BinDU: cuts :: BinDU -> Vector Double
+ Data.Histogram.Bin.BinDU: data BinDU
+ Data.Histogram.Bin.BinDU: deleteCut :: AdaptableBin b => b -> Int -> b
+ Data.Histogram.Bin.BinDU: instance AdaptableBin BinDU
+ Data.Histogram.Bin.BinDU: instance Bin BinDU
+ Data.Histogram.Bin.BinDU: instance Bin1D BinDU
+ Data.Histogram.Bin.BinDU: instance BinEq BinDU
+ Data.Histogram.Bin.BinDU: instance Data BinDU
+ Data.Histogram.Bin.BinDU: instance Eq BinDU
+ Data.Histogram.Bin.BinDU: instance IntervalBin BinDU
+ Data.Histogram.Bin.BinDU: instance NFData BinDU
+ Data.Histogram.Bin.BinDU: instance Show BinDU
+ Data.Histogram.Bin.BinDU: instance SliceableBin BinDU
+ Data.Histogram.Bin.BinDU: instance Typeable BinDU
+ Data.Histogram.Bin.BinDU: instance VariableBin BinDU
+ Data.Histogram.Bin.BinDU: unsafeBinDU :: Vector Double -> BinDU

Files

CHANGELOG.markdown view
@@ -1,3 +1,7 @@ 0.1.1 --- * Removed Increment from API++0.2.0+---+* inclusion of Histogram folds
foldl-incremental.cabal view
@@ -1,5 +1,5 @@ Name:                     foldl-incremental-Version:                  0.1.1.0+Version:                  0.2.0.0 Author:                   Tony Day Maintainer:               tonyday567@gmail.com License:                  MIT@@ -15,13 +15,13 @@   README.markdown   CHANGELOG.markdown Synopsis:                 incremental folds-Description: This library provides incremental statistical folds based upon the -  foldl libray.  An incremental statistical fold can be thought of as +Description: Incremental statistical folds based upon the +  <https://hackage.haskell.org/package/foldl foldl> libray.  An incremental statistical fold can be thought of as    exponentially-weighting statistics designed to be efficient computations over    a Foldable.    It supplies "incrementalize" which turns any unary function into a -  "Fold".  As a reference, \"incrementalize id\" is an exponentially-weighted moving average.+  "Fold".  As a reference, `incrementalize id` is an exponentially-weighted moving average. Homepage:                 https://github.com/tonyday567/foldl-incremental Bug-Reports:              https://github.com/tonyday567/foldl-incremental/issues Tested-With:              GHC==7.6.3@@ -31,10 +31,18 @@  Library   Exposed-Modules:        Control.Foldl.Incremental+                        , Control.Foldl.Incremental.Histogram+                        , Control.Foldl.Incremental.Simple+                        , Data.Histogram.Adaptable+                        , Data.Histogram.Bin.BinDU -  Build-Depends:          base >= 4 && < 5,-                          foldl >= 1.0.3 && < 2-                          +  Build-Depends:          base >= 4 && < 5+                        , containers+                        , deepseq+                        , foldl+                        , histogram-fill+                        , vector+                    Default-Language:       Haskell2010   HS-Source-Dirs:         src @@ -43,22 +51,20 @@   Main-Is:                test.hs   Default-Language:       Haskell2010   HS-Source-Dirs:         src, test-  Build-Depends:          base >= 4 && < 5,-                          bytestring >= 0.10.0.2,-                          foldl >= 1.0.3 && < 2,-                          tasty >= 0.7 && < 1,-                          tasty-golden >= 2.2.0.2 && < 3,-                          tasty-quickcheck >= 0.8,-                          tasty-hunit >= 0.4.1 && < 5,-                          foldl-incremental >= 0.1.0.1+  Build-Depends:          base >= 4 && < 5, tasty >= 0.7 && < 1, tasty-golden >= 2.2.0.2 && < 3, tasty-quickcheck >= 0.8, tasty-hunit >= 0.4.1 && < 5, foldl-incremental >= 0.2.0, QuickCheck >= 2.7.5+                        , bytestring >= 0.10.0.2+                        , containers >= 0.5.0.0+                        , foldl >= 1.0.3 && < 2+                        , histogram-fill >= 0.8.4.1+                        , mwc-random >= 0.13.1.1+                        , pipes >= 4.1.1+                        , vector >= 0.10.0.1  Benchmark bench   Type:                   exitcode-stdio-1.0   Main-Is:                bench.hs   Default-Language:       Haskell2010   HS-Source-Dirs:         test-  Build-Depends:          base >= 4 && < 5,-                          foldl >= 1.0.3 && < 2,-                          hastache == 0.5.1,-                          criterion >= 0.8.0.1,-                          foldl-incremental >= 0.1.0.1+  Build-Depends:          base >= 4 && < 5, criterion >= 0.8.0.1, foldl-incremental >= 0.2.0+                        , containers >= 0.5.0.0+                        , foldl >= 1.0.3 && < 2
src/Control/Foldl/Incremental.hs view
@@ -23,28 +23,48 @@  The folds represent incremental statistics such as moving averages`. -The stream of moving averages with a `rate` of 0.1 is:+The stream of moving averages with a forgetting `rate` of 0.9 is: ->>> L.scan (incMa 0.1) [1..    10]+>>> L.scan (ma 0.9) [1..10]+[NaN,1.0,1.5263157894736843,2.070110701107011,2.6312881651642916,3.2097140484969837,3.805217699371904,4.4175932632947745,5.046601250122929,5.691970329383086,6.3533993278762955]  or if you just want the moving average at the end. ->>> L.fold (incMa 0.1) [1..10]+>>> L.fold (ma 0.9) [1..10]+6.3533993278762955 +The simple average is obtained via a decay rate of 1.0 (ie no decay)++>>> L.fold (ma 1.0) [1..10]+5.5+++further reading:++<http://queue.acm.org/detail.cfm?id=2534976 Online Algorithms in High-frequency Trading>+<http://www.google.com.au/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCwQFjAA&url=http%3A%2F%2Fwww-personal.umich.edu%2F~annastef%2Fpapers_Long_ctrl%2FJournalPaperMassGrade_Final.pdf&ei=kq1ZU76IFIPGkAXtqoHACQ&usg=AFQjCNHG7gVfpVoh1g9gjWUcK3Hb22JIlg&sig2=_YxAjKZFo_UEXoFWJafUsw&bvm=bv.65397613,d.dGI  recursive least squares with exponential forgetting>+ -}  module Control.Foldl.Incremental (     -- * incrementalize     incrementalize     -- * common incremental folds-  , incMa-  , incAbs-  , incSq-  , incStd+  , ma+  , absma+  , sqma+  , std+  , cov+  , corr+  , length+  , beta+  , alpha+  , autocorr   ) where -import           Control.Applicative ((<$>), (<*>))-import           Control.Foldl (Fold(..))+import Control.Applicative ((<$>), (<*>))+import Control.Foldl (Fold(..),premap)+import Prelude hiding (length)  -- | An Increment is the incremental state within an exponential moving average fold. data Increment = Increment@@ -53,7 +73,7 @@    , _rate    :: {-# UNPACK #-} !Double    } deriving (Show) -{-| Incrementalize takes a function and turns it into a `Control.Foldl.Fold` where the step is an Increment iso to the typical step in an exponential moving average calculation.+{-| Incrementalize takes a function and turns it into a `Control.Foldl.Fold` where the step is an Increment similar to the typical step in an exponential moving average calculation.  >>> incrementalize id @@ -67,20 +87,36 @@  >>> std r = (\s ss -> sqrt (ss - s**2)) <$> incrementalize id r <*> incrementalize (*2) r -The rate is the parameter regulating the discount of current state and the introduction of the current value.+incrementalize works with any function that produces a double.  A correlation fold of a tuple is quite intuitive: +>>> cov r = (\xy xbar ybar -> xy - xbar * ybar) <$> incrementalize (uncurry (*)) r <*> incrementalize fst r <*> incrementalize snd r+>>> corr r = (\cov' stdx stdy -> cov' / (stdx * stdy)) <$> cov r <*> L.premap fst (std r) <*> L.premap snd (std r)++The rate is the parameter regulating the discount (or forgetting) of current state and the introduction of the current value.+ >>> incrementalize id 1 -tracks the sum/average of an entire Foldable.+tracks the sum/average of an entire Foldable. In other words, prior values are never forgotten.  >>> incrementalize id 0 -produces the latest value (ie current state is discounted (or decays) to zero)+produces the latest value (ie current state is discounted (or decays) to zero).  In other words, prior values are immediately forgotten. -A exponential moving average with a duration of 10 (the average lag of the values effecting the calculation) is+A exponential moving average with an exponetially-weighted length (duration if its a time series) of 10 (the average lag of the values effecting the calculation) is ->>> incrementalize id (1/10)+>>> incrementalize id (1 - 1/10) +>>> L.fold (length 0.9) [1..100]+9.999734386011127++There is no particular reason for different parts to have the same rate.  A standard deviation where mean is expected to be static (eg equal to the unconditional sample average) would be:++>>> std' r = (\s ss -> sqrt (ss - s**2)) <$> incrementalize id 1 <*> incrementalize (*2) r++and a standard deviation with a prior for the mean (eg ignoring sample averges) would be:++>>> std'' mean r = incrementalize (\x -> x*2 - mean**2) r+ -} incrementalize :: (a -> Double) -> Double -> Fold a Double incrementalize f r =  Fold step (Increment 0 0 r) (\(Increment a c _) -> a / c)@@ -88,22 +124,76 @@     step (Increment n d r') n' = Increment (r' * n + f n') (r' * d + 1) r' {-# INLINABLE incrementalize #-} --- | moving average fold-incMa :: Double -> Fold Double Double-incMa = incrementalize id-{-# INLINABLE incMa #-}+-- | incremental average+ma :: Double -> Fold Double Double+ma = incrementalize id+{-# INLINABLE ma #-} --- | moving absolute average-incAbs :: Double -> Fold Double Double-incAbs = incrementalize abs-{-# INLINABLE incAbs #-}+-- | incremental absolute average+absma :: Double -> Fold Double Double+absma = incrementalize abs+{-# INLINABLE absma #-} --- | moving average square-incSq :: Double -> Fold Double Double-incSq = incrementalize (\x -> x*x)-{-# INLINABLE incSq #-}+-- | incremental average square+sqma :: Double -> Fold Double Double+sqma = incrementalize (\x -> x*x)+{-# INLINABLE sqma #-} --- | moving standard deviation-incStd :: Double -> Fold Double Double-incStd rate = (\s ss -> sqrt (ss - s**2)) <$> incMa rate <*> incSq rate-{-# INLINABLE incStd #-}+-- | incremental standard deviation+std :: Double -> Fold Double Double+std rate = (\s ss -> sqrt (ss - s**2)) <$> ma rate <*> sqma rate+{-# INLINABLE std #-}++-- | incremental covariance+cov :: Double -> Fold (Double, Double) Double+cov r = (\xy xbar ybar -> xy - xbar * ybar) <$> incrementalize (uncurry (*)) r <*> incrementalize fst r <*> incrementalize snd r+{-# INLINABLE cov #-}++-- | incremental corelation+corr :: Double -> Fold (Double, Double) Double+corr r = (\cov' stdx stdy -> cov' / (stdx * stdy)) <$> cov r <*> premap fst (std r) <*> premap snd (std r)+{-# INLINABLE corr #-}++-- | the exponentially weighted length of a rate, which is 1/(1-rate) at infinity+length :: Double -> Fold a Double+length r =  Fold step (Increment 0 0 r) (\(Increment _ d _) -> d)+  where+    step (Increment _ d r') _ = Increment 1 (r' * d + 1) r'+{-# INLINABLE length #-}++-- | the beta in a simple linear regression of `snd` on `fst`+beta :: Double -> Fold (Double, Double) Double+beta r = (/) <$> cov r <*> premap snd (std r)+{-# INLINABLE beta #-}++-- | the alpha in a simple linear regression of `snd` on `fst`+alpha :: Double -> Fold (Double, Double) Double+alpha r = (\y b x -> y - b * x) <$> premap fst (ma r) <*> beta r <*> premap snd (ma r)+{-# INLINABLE alpha #-}++{-| autocorrelation is a slippery concept.  This method starts with the concept that there is an underlying random error process (e), and autocorrelation is a process on top of that ie for a one-step correlation relationship.++value@t = e@t + k * e@t-1++where k is the autocorrelation.++There are thus two decay rates needed: one for the average being considered to be the dependent variable, and one for the decay of the correlation calculation between the most recent value and the moving average. ++>>> L.fold (autoCorr 0 1)++Would estimate the one-step autocorrelation relationship of the previous value and the current value over the entire sample set. ++-}+autocorr :: Double -> Double -> Fold Double Double+autocorr maR corrR = +    case ma maR of+        (Fold maStep maBegin maDone) ->+            case corr corrR of+                (Fold corrStep corrBegin corrDone) ->+                    let begin = (maBegin, corrBegin)+                        step (maAcc,corrAcc) a = (maStep maAcc a,+                            if isNaN (maDone maAcc)+                            then corrAcc+                            else corrStep corrAcc (maDone maAcc, a)) +                        done = corrDone . snd in+                    Fold step begin done
+ src/Control/Foldl/Incremental/Histogram.hs view
@@ -0,0 +1,198 @@+{-# OPTIONS_GHC -fno-warn-type-defaults #-}++{-| incremental folds of 'Histogram's from the <https://hackage.haskell.org/package/histogram-fill histogram-fill> library.++-}++module Control.Foldl.Incremental.Histogram  (+    -- * Incrementalize+    incrementalizeHist+  , incrementalizeHist2D+    -- * Common Histogram Folds+  , incHist+  , incHist2D+  , incAdaptiveHist+  ) where++import           Control.Foldl (Fold(..))+import qualified Control.Foldl as L+import           Data.Histogram+import           Data.Histogram.Adaptable+import           Data.Histogram.Bin.BinDU+import           Data.Histogram.Fill+import           Data.Vector.Unboxed ((//))+import qualified Data.Vector.Unboxed as V+import           GHC.Float (double2Int)++-- | FIXME: make Increment a class and IncrementHist an instance.+data IncrementHist a = IncrementHist+   { _adder   :: Histogram a Double+   , _counter :: {-# UNPACK #-} !Double+   , _rate    :: {-# UNPACK #-} !Double+   }++{-| incrementalizeHist takes a function governing the input to the histogram.++>>> incrementalizeHist (const 1)++is the usual boiler-plate meaning of histogram.++-}+incrementalizeHist :: BinD -> (Double -> Double) -> Double -> L.Fold Double (Histogram BinD Double)+incrementalizeHist b f r =  L.Fold step begin done+  where+    step (IncrementHist x' d r') a = IncrementHist+        (Data.Histogram.zip (\x0 y0 -> x0 * r' + y0) x' (mkOne b f a))+        (d * r' + 1)+        r'+    begin = IncrementHist (fillBuilderVec (mkSimple b) V.empty) 0 r+    done (IncrementHist a c _) =+      if c /= 0+         then Data.Histogram.map (/c) a+         else a+    mkOne :: BinD -> (Double -> Double) -> Double -> Histogram BinD Double+    mkOne b' f' a = histogram b' d+      where+        i  = toIndex b a+        d = V.replicate (nBins b) 0 // [(i,f' a)]++-- 2D histogram folding+data IncrementHist2D = IncrementHist2D+   { _adder2D   :: Histogram (Bin2D BinD BinD) Double+   , _counter2D :: {-# UNPACK #-} !Double+   , _rate2D    :: {-# UNPACK #-} !Double+   } deriving (Show)++-- | 2D version+incrementalizeHist2D :: Bin2D BinD BinD -> ((Double,Double) -> Double) -> Double -> L.Fold (Double,Double) (Histogram (Bin2D BinD BinD) Double)+incrementalizeHist2D b f r =  L.Fold step begin done+  where+    step (IncrementHist2D x' d r') a = IncrementHist2D+        (Data.Histogram.zip (\x0 y0 -> x0 * r' + y0) x' (mkOne2D b f a))+        (d * r' + 1)+        r'+    begin = IncrementHist2D (fillBuilderVec (mkSimple b) V.empty) 0 r+    done (IncrementHist2D a c _) =+      if c /= 0+         then Data.Histogram.map (/c) a+         else a+    mkOne2D :: Bin2D BinD BinD -> ((Double,Double) -> Double) -> (Double,Double) -> Histogram (Bin2D BinD BinD) Double+    mkOne2D b' f' a = histogram b' d+      where+        i  = toIndex b a+        d = V.replicate (nBins b) 0 // [(i,f' a)]++{-| incremental histogram with pre-defined bins++>>> import Control.Foldl.Incremental+>>> import qualified Control.Foldl as L++>>> let b = binDn 0 2 12+>>> L.fold (incHist b 0.9) [1..10]+-}+incHist :: BinD -> Double -> Fold Double (Histogram BinD Double)+incHist b = incrementalizeHist b (const 1)+{-# INLINABLE incHist #-}++-- | incremental 2D histogram+incHist2D :: Bin2D BinD BinD -> Double -> Fold (Double,Double) (Histogram (Bin2D BinD BinD) Double)+incHist2D b = incrementalizeHist2D b (const 1)++{-| adaptable histogram fold++TODO: integrate Histogram.Adaptable upstream++incHist requires a pre-specified bin, which in turn requires an initial pass over the stream to determine the data ranges.++For a one pass histogram fold, we require an incremental approach to bin creation, which, in turn, requires some way of creating a histogram from scratch.++'Data.Histogram.Adaptable' and 'Data.Histogram.Bin.BinDU' is a draft solution to enable a one-pass at histogram creation.+++This function takes++- a maximum frequency (thresh) for a bin, which, when triggered causes a bin to be split (at the moment using a uniform distribution assumption which is pretty bad).+- a minimum bin size (grain). bins are further constrained to be multiples of this.++- a maximum number of bins, which, when triggered, causes bins to be merged.++>>> L.fold (incAdaptiveHist 0.2 1.0 10 1.0) [1..1000]++provides a histogram with no bin more than 20% frequency size, with a minimum bin size of 1, with at most 10 bins, and a decay rate of 1.0++-}+incAdaptiveHist :: Double -> Double -> Int -> Double -> Fold Double (Histogram BinDU Double)+incAdaptiveHist thresh grain maxBins rate = Fold step begin done+  where+    step (IncrementHist h d _) a =+        let h' = Data.Histogram.map (*rate) (step' h a) in+        IncrementHist h' ((d+1)*rate) rate+    step' x a =+        checkMaxBins maxBins+        (maybeSlice thresh grain (checkMaxBins maxBins (addOne grain x a)) a)+      where+        checkMaxBins max' x' =+            if nBins (bins x') > max'+            then checkMaxBins max' (mergeSmallest x')+            else x'+    begin = IncrementHist (histogram (unsafeBinDU V.empty) V.empty) 0 rate+    done (IncrementHist h c _) =+      if c /= 0+         then Data.Histogram.map (/c) h +         else h++-- helpers+addOne :: Double -> Histogram BinDU Double -> Double -> Histogram BinDU Double+addOne grain x a+    | V.length (cuts (bins x)) == 0 = addFirst grain a+    | a < lowerLimit (bins x) = addLower grain x a+    | a >= upperLimit (bins x) = addUpper grain x a+    | otherwise = addMiddle x a++addMiddle :: (Bin bin, V.Unbox a, Num a) => Histogram bin a -> BinValue bin -> Histogram bin a+addMiddle h a = histogram (bins h) (histData h // [(i, 1 + h `atI` i)])+      where+        i = toIndex (bins h) a++addFirst :: Double -> Double -> Histogram BinDU Double+addFirst grain a = histogram bin (V.fromList [1.0])+      where+        a' = roundD grain a+        bin = binDU (V.fromList [a' - grain/2, a' + grain/2])++addLower :: Double -> Histogram BinDU Double-> Double -> Histogram BinDU Double+addLower grain x a = x1+      where+        a' = roundD grain a+        x0 = sliceAt x (a' - grain/2)+        x1 = addMiddle x0 a++addUpper :: Double -> Histogram BinDU Double-> Double -> Histogram BinDU Double+addUpper grain x a = x1+      where+        a' = roundD grain a+        x0 = sliceAt x (a' + grain/2)+        x1 = addMiddle x0 a++maybeSlice :: Double-> Double-> Histogram BinDU Double-> BinValue BinDU-> Histogram BinDU Double+maybeSlice thresh' grain' x' a' =+    let freq = x' `atV` a'+        i = toIndex (bins x') a'+        (l,u) = binInterval (bins x') i+        numGrains = round ((u-l) / grain') :: Int+        center = l + grain' * fromIntegral (round (fromIntegral numGrains / 2)) +    in+    if binSizeN (bins x') i > grain' + 3e-11 &&+       freq / V.sum (histData x') > thresh'+    then sliceAt x' center+    else x'++roundD :: Double -> Double -> Double+roundD grain x = if frac > 0.5 then whole + grain else whole+    where+      whole = floorDD+      frac = (x - floorDD) / grain+      floorDD = fromIntegral (floorD (x / grain)) * grain+      floorD x' | x' < 0     = double2Int x' - 1+                | otherwise = double2Int x'+{-# INLINE roundD #-}
+ src/Control/Foldl/Incremental/Simple.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++{-| Simple moving average style folds -}++module Control.Foldl.Incremental.Simple (+    -- * incrementalize+    incrementalizeSimple+    -- * common simple folds+  , sma+  , sabsma+  , ssqma+  , sstd+  ) where++import           Control.Applicative+import           Control.Foldl as L+import           Data.Sequence (ViewR(EmptyR, (:>)), (<|))+import qualified Data.Sequence as Seq++(</>) :: (Fractional c, Applicative f) => f c -> f c -> f c+(</>) = liftA2 (/) ++(<+>) :: (Fractional c, Applicative f) => f c -> f c -> f c+(<+>) = liftA2 (+) ++maybeSum :: Fold (Maybe Double) (Maybe Double)+maybeSum = Fold (<+>) (Just 0) id++{-|+Incrementalize takes a function and turns it into a `Control.Foldl.Fold` where the step is an Increment similar to the typical step in a simple moving average calculation.+-}+incrementalizeSimple :: (a -> Double) -> Int -> Fold a Double+incrementalizeSimple f n = L.Fold step begin done+  where+    begin = Seq.replicate n Nothing+    av x = L.fold maybeSum x </> pure (fromIntegral n)+    done x = case av x of+        Nothing -> 0/0+        Just x' -> x'+    step x a = Just (f a) <| pop x+    pop x = case Seq.viewr x of+        EmptyR   -> x+        x'' :> _ -> x''++-- | a simple moving average+sma :: Int -> L.Fold Double Double+sma = incrementalizeSimple id++-- | simple squared moving average+ssqma :: Int -> L.Fold Double Double+ssqma = incrementalizeSimple (**2)++-- | simple absolute moving average+sabsma :: Int -> L.Fold Double Double+sabsma = incrementalizeSimple abs++-- | simple standard deviation+sstd :: Int -> Fold Double Double+sstd n = (\s ss -> sqrt (ss - s**2)) <$> sma n <*> ssqma n+
+ src/Data/Histogram/Adaptable.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE FlexibleContexts   #-}+{-# LANGUAGE TypeSynonymInstances   #-}+{-# LANGUAGE FlexibleInstances   #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE MultiWayIf #-}++-- Immutable histogram specialised to Unboxed Double, that can add and delete bins+module Data.Histogram.Adaptable (+    -- * Immutable adaptable histograms+    HistogramDU+    , sliceAt+    , insertAt+    , mergeAtCut+    , smallestCutContiguous+    , smallestCutSingle+    , mergeSmallest+    , mergeSmallestSingle+  ) where++import qualified Data.Vector.Unboxed as V+import Data.Vector.Unboxed ((!),(++))++import Data.Histogram+import Data.Histogram.Bin.BinDU++import Prelude hiding ((++))++-- | Immutable Adaptable histogram.+type HistogramDU = Histogram BinDU Double++sliceAt :: Histogram BinDU Double -> Double -> Histogram BinDU Double+sliceAt h x+    | V.length (cuts (bins h)) == 0 = addFirst+    | x < lowerLimit (bins h) = addLower+    | x > upperLimit (bins h) = addUpper+    | otherwise = addMiddle+  where+    b = bins h+    n = nBins b+    b' = addCut b x+    v = histData h+    i = toIndex b x+    freq = h `atV` x+    r = binInterval b i+    size = binSizeN b i+    slice0 = freq * (x - fst r)/size+    slice1 = freq * (snd r - x)/size+    addFirst  = histogram (unsafeBinDU (V.fromList [x])) V.empty+    addLower  = histogram b' (V.singleton 0 ++ v)+    addUpper  = histogram b' (v ++ V.singleton 0)+    addMiddle = histogram b' (V.concat+                              [ V.take i v+                              , V.fromList [slice0,slice1]+                              , V.drop (min (i+1) (n+1)) v+                              ])+++-- | dont interpolate the bin values+insertAt :: Histogram BinDU Double -> Double -> Histogram BinDU Double+insertAt h x+    | V.length (cuts (bins h)) == 0 = addFirst+    | x < lowerLimit (bins h) = addLower+    | x > upperLimit (bins h) = addUpper+    | otherwise = addMiddle+  where+    b = bins h+    n = nBins b+    b' = addCut b x+    v = histData h+    i = toIndex b x+    addFirst  = histogram (unsafeBinDU (V.fromList [x])) V.empty+    addLower  = histogram b' (V.singleton 0 ++ v)+    addUpper  = histogram b' (v ++ V.singleton 0)+    addMiddle = histogram b' (V.concat+                              [ V.take (i+1) v+                              , V.singleton 0+                              , V.drop (min (i+1) (n+1)) v+                              ])++mergeAtCut :: HistogramDU -> Int -> HistogramDU+mergeAtCut h i+       | i <0 || i>n = error "Data.Histogram.HistogramA': outside index range"+       | i == 0 = case h `atI` 0 of+             0 -> histogram (deleteCut (bins h) i) (V.drop 1 v) +             _ -> error "Data.Histogram.HistogramA': can't delete outer bin with non-zero frequency"+       | i == n = case h `atI` (i-1) of+             0 -> histogram (deleteCut (bins h) i) (V.init v) +             _ -> error "Data.Histogram.HistogramA': can't delete outer bin with non-zero frequency"+       | otherwise = histogram b' v'+  where+    n  = nBins (bins h)+    b' = deleteCut (bins h) i+    v  = histData h+    v' = V.concat+         [ V.take (max 0 (i-1)) v+         , V.singleton (v ! (i - 1) + v ! i)+         , V.drop (min (i+1) n) v+         ]++smallestCutContiguous :: (Bin b, V.Unbox a, Ord a, Num a) => Histogram b a -> Int+smallestCutContiguous h+    | v ! 0 == 0 = 0+    | v ! n == 0 = n+1+    | otherwise = 1 + V.minIndex (V.zipWith (+) (V.init v) (V.tail v))+  where+    v = histData h+    n = nBins (bins h) - 1++smallestCutSingle :: (Bin b, V.Unbox a, Ord a, Num a) => Histogram b a -> Int+smallestCutSingle h+    | v ! 0 == 0 = 0+    | v ! n == 0 = n+1+    | mini == 0 = 1+    | mini == n = n+    | v!(mini-1) < v!(mini+1) = mini+    | otherwise = mini+1+  where+    v = histData h+    n = nBins (bins h) - 1+    mini = V.minIndex v++mergeSmallest :: HistogramDU -> HistogramDU+mergeSmallest h = mergeAtCut h (smallestCutContiguous h)++mergeSmallestSingle :: HistogramDU -> HistogramDU+mergeSmallestSingle h = mergeAtCut h (smallestCutSingle h)
+ src/Data/Histogram/Bin/BinDU.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE FlexibleContexts   #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeFamilies #-}++{-+λ: let bin = binDU (V.fromList [0,1,2])++λ: bin+# BinDU cuts+0.0	1.0	2.0	++λ: addCut bin 3+# BinDU cuts+0.0	1.0	2.0	3.0	++λ: deleteCut bin 0+# BinDU cuts+1.0	2.0++-}++module Data.Histogram.Bin.BinDU (+    -- * Specialized to Double, Unboxed Vectors+    BinDU(..)+  , binDU+  , cuts+  , unsafeBinDU+  , AdaptableBin(..)+  ) where++import Control.DeepSeq (NFData(..))+import Data.Data       (Data,Typeable)+import Data.Vector.Unboxed  (Vector,(!))+import qualified Data.Vector.Unboxed as VU+import Data.Maybe++import Data.Histogram.Bin.Classes++-- | Double bins of unequal sizes.+--   Bins are defined by a vector of cuts marking bounadries between bins (The entire range is continuous.  There are n+1 cuts for n bins+--   Cuts are assumed to be in ascending order+--   Specialized on Data.Vector.Unboxed+--   TODO: Generic Vector type.+--   Type paramter:+--+--   [@v@] type of vector used to define bin cuts++data BinDU = BinDU !(Vector Double) -- vector of cuts+            deriving (Data,Typeable,Eq)++-- | Create bins unsafely+unsafeBinDU :: Vector Double -- ^ cuts+     -> BinDU+unsafeBinDU = BinDU++binDU :: Vector Double -- ^ cuts+     -> BinDU+binDU c+    | VU.length c < 2 = error "Data.Histogram.Bin.BinDU.binDU': nonpositive number of bins"+    | VU.any (uncurry (>)) (VU.zip (VU.init c) (VU.drop 1 c)) = error "Data.Histogram.Bin.BinDU.binDU': cuts not in ascending order"+    | otherwise = BinDU c++cuts :: BinDU -> Vector Double+cuts (BinDU c) = c++instance Bin BinDU where+  type BinValue BinDU = Double+  toIndex   (BinDU c) !x = case VU.findIndex (>x) c of+      Nothing -> error "Data.Histogram.Bin.BinDU.toIndex: above range"+      Just i  -> case i of+          0 -> error "Data.Histogram.Bin.BinDU.toIndex: below range"+          _ -> i-1++  fromIndex (BinDU c) !i+      | i >= VU.length c - 1 = +            error "Data.Histogram.Bin.BinDU.fromIndex: above range"+      | otherwise = ((c ! i) + (c ! (i+1)))/2++  nBins (BinDU c) = if VU.length c < 2 then 0 else VU.length c - 1+  {-# INLINE toIndex #-}++instance IntervalBin BinDU where+  binInterval (BinDU c) i = (c ! i, c ! (i+1))++instance Bin1D BinDU where+  lowerLimit (BinDU c) = VU.head c+  upperLimit (BinDU c) = VU.last c++instance SliceableBin BinDU where+  unsafeSliceBin i j (BinDU c) = BinDU (VU.drop i $ VU.take (j-i) c)++instance VariableBin BinDU where+  binSizeN (BinDU c) !i = c ! (i+1) - c ! i++-- | Equality is up to 3e-11 (2/3th of digits)+instance BinEq BinDU where+  binEq (BinDU c) (BinDU c')+    =  isNothing (VU.find (\(d,d') -> d - d' > eps * abs d) $ VU.zip c c')+    where+      eps = 3e-11++instance Show BinDU where+  show (BinDU c) = "# BinDU cuts\n" ++ concat (fmap showCut $ VU.toList c) ++ "\n\n"+    where+      showCut x = show x ++ "\t"++instance NFData BinDU++-- | Binning algorithms which support adaption.+class Bin b => AdaptableBin b where+  -- | delete a bin+  deleteCut :: b -> Int -> b+  -- | add a new bin+  addCut :: b -> Double -> b++instance AdaptableBin BinDU where+    deleteCut (BinDU c) !i+        | VU.length c <= 2 = +            error "Data.Histogram.Bin.BinDU.deletBin: deleting single bin"+        | otherwise = BinDU (VU.take i c VU.++ VU.drop (i+1) c)++    addCut (BinDU c) !x = BinDU (VU.concat [VU.take i c, VU.singleton x, VU.drop i c])+      where+        i = fromMaybe (VU.length c) (VU.findIndex (> x) c)
test/test.hs view
@@ -5,9 +5,6 @@ main = defaultMain tests  tests :: TestTree-tests = testGroup "Tests" [tests']--tests' :: TestTree-tests' = testGroup "tests"+tests = testGroup "incremental tests"   [ TestIncremental.quickTests   ]