@@ -1,13 +1,54 @@
2+-- Module : Streamly.Internal.Data.Fold.Window
3+-- Copyright : (c) 2020 Composewell Technologies
4+-- License : Apache-2.0
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
9+-- Simple incremental statistical measures over a stream of data. All
10+-- operations use numerically stable floating point arithmetic.
12+-- Measurements can be performed over the entire input stream or on a sliding
13+-- window of fixed or variable size. Where possible, measures are computed
14+-- online without buffering the input stream.
16+-- Currently there is no overflow detection.
18+-- For more advanced statistical measures see the @streamly-statistics@
1module Streamly.Internal.Data.Fold.Window
21module Streamly.Internal.Data.Fold.Window
23+ -- * Incremental Folds
24+ -- | Folds of type @Fold m (a, Maybe a) b@ are incremental sliding window
25+ -- folds. An input of type @(a, Nothing)@ indicates that the input element
26+ -- @a@ is being inserted in the window without ejecting an old value
27+ -- increasing the window size by 1. An input of type @(a, Just a)@
28+ -- indicates that the first element is being inserted in the window and the
29+ -- second element is being removed from the window, the window size remains
30+ -- the same. The window size can only increase and never decrease.
32+ -- You can compute the statistics over the entire stream using sliding
33+ -- window folds by keeping the second element of the input tuple as
13import Data.Bifunctor(bimap)
54import Data.Bifunctor(bimap)
@@ -40,14 +81,14 @@ import qualified Streamly.Data.Fold as Fold
40lmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b
81lmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b
41lmap f = Fold.lmap (bimap f (f <$>))
82lmap f = Fold.lmap (bimap f (f <$>))
43−-- | Convert a rolling fold to a normal fold using the entire input stream as a
84+-- | Convert an incremental fold to a cumulative fold using the entire input
44−-- single window.
85+-- stream as a single window.
46−-- >>> whole f = Fold.lmap (\x -> (x, Nothing)) f
87+-- >>> cumulative f = Fold.lmap (\x -> (x, Nothing)) f
48−{-# INLINE whole #-}
89+{-# INLINE cumulative #-}
49−whole :: Fold m (a, Maybe a) b -> Fold m a b
90+cumulative :: Fold m (a, Maybe a) b -> Fold m a b
50−whole = Fold.lmap (, Nothing)
91+cumulative = Fold.lmap (, Nothing)
52-------------------------------------------------------------------------------
93-------------------------------------------------------------------------------
@@ -85,6 +126,12 @@ sumInt = Fold step initial extract
86-- | Sum of all the elements in a rolling window:
127-- | Sum of all the elements in a rolling window:
129+-- \(S = \sum_{i=1}^n x_{i}\)
131+-- This is the first power sum.
133+-- >>> sum = powerSum 1
88-- Uses Kahan-Babuska-Neumaier style summation for numerical stability of
135-- Uses Kahan-Babuska-Neumaier style summation for numerical stability of
89-- floating precision arithmetic.
136-- floating precision arithmetic.
@@ -127,6 +174,10 @@ sum = Fold step initial extract
128-- | The number of elements in the rolling window.
175-- | The number of elements in the rolling window.
177+-- This is the \(0\)th power sum.
179+-- >>> length = powerSum 0
130{-# INLINE length #-}
181{-# INLINE length #-}
131length :: (Monad m, Num b) => Fold m (a, Maybe a) b
182length :: (Monad m, Num b) => Fold m (a, Maybe a) b
132length = Fold.foldl' step 0
183length = Fold.foldl' step 0
@@ -136,6 +187,28 @@ length = Fold.foldl' step 0
136 step w (_, Nothing) = w + 1
187 step w (_, Nothing) = w + 1
137 step w _ = w
188 step w _ = w
190+-- | Sum of the \(k\)th power of all the elements in a rolling window:
192+-- \(S_k = \sum_{i=1}^n x_{i}^k\)
194+-- >>> powerSum k = lmap (^ k) sum
196+-- /Space/: \(\mathcal{O}(1)\)
198+-- /Time/: \(\mathcal{O}(n)\)
199+{-# INLINE powerSum #-}
200+powerSum :: (Monad m, Num a) => Int -> Fold m (a, Maybe a) a
201+powerSum k = lmap (^ k) sum
203+-- | Like 'powerSum' but powers can be negative or fractional. This is slower
204+-- than 'powerSum' for positive intergal powers.
206+-- >>> powerSumFrac p = lmap (** p) sum
208+{-# INLINE powerSumFrac #-}
209+powerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a
210+powerSumFrac p = lmap (** p) sum
139-------------------------------------------------------------------------------
212-------------------------------------------------------------------------------
140-- Location
213-- Location
141-------------------------------------------------------------------------------
214-------------------------------------------------------------------------------
@@ -149,8 +222,8 @@ length = Fold.foldl' step 0
150-- | The minimum element in a rolling window.
223-- | The minimum element in a rolling window.
152−-- If you want to compute the minimum of the entire stream Fold.minimum from streamly
225+-- If you want to compute the minimum of the entire stream Fold.minimum from
153−-- package would be much faster.
226+-- streamly package would be much faster.
155-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
228-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
@@ -199,8 +272,8 @@ minimum = Fold step initial extract
200-- | The maximum element in a rolling window.
273-- | The maximum element in a rolling window.
202−-- If you want to compute the maximum of the entire stream Fold.maximum from streamly
275+-- If you want to compute the maximum of the entire stream Fold.maximum from
203−-- package would be much faster.
276+-- streamly package would be much faster.
205-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
278-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
@@ -246,6 +319,23 @@ maximum = Fold step initial extract
246 $ fromMaybe (0, error "max: Empty stream")
319 $ fromMaybe (0, error "max: Empty stream")
247 $ DQ.head q
320 $ DQ.head q
322+-- | Arithmetic mean of elements in a sliding window:
324+-- \(\mu = \frac{\sum_{i=1}^n x_{i}}{n}\)
326+-- This is also known as the Simple Moving Average (SMA) when used in the
327+-- sliding window and Cumulative Moving Avergae (CMA) when used on the entire
330+-- >>> mean = Fold.teeWith (/) sum length
332+-- /Space/: \(\mathcal{O}(1)\)
334+-- /Time/: \(\mathcal{O}(n)\)
336+mean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a
337+mean = Fold.teeWith (/) sum length
250-- | The difference between the maximum and minimum elements of a rolling window.
340-- | The difference between the maximum and minimum elements of a rolling window.