@@ -1,13 +1,54 @@
1+-- |
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
7+-- Portability : GHC
8+--
9+-- Simple incremental statistical measures over a stream of data. All
10+-- operations use numerically stable floating point arithmetic.
11+--
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.
15+--
16+-- Currently there is no overflow detection.
17+--
18+-- For more advanced statistical measures see the @streamly-statistics@
19+-- package.
20+
121module Streamly.Internal.Data.Fold.Window
2−( lmap
3−, length
4−, whole
5−, sum
6−, sumInt
7−, minimum
8−, maximum
9−, range
10−)
22+ (
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.
31+ --
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
34+ -- @Nothing@.
35+ --
36+ lmap
37+ , cumulative
38+
39+ -- ** Sums
40+ , length
41+ , sum
42+ , sumInt
43+ , powerSum
44+ , powerSumFrac
45+
46+ -- ** Location
47+ , minimum
48+ , maximum
49+ , range
50+ , mean
51+ )
1152where
1253
1354import Data.Bifunctor(bimap)
@@ -40,14 +81,14 @@ import qualified Streamly.Data.Fold as Fold
4081lmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b
4182lmap f = Fold.lmap (bimap f (f <$>))
4283
43−-- | Convert a rolling fold to a normal fold using the entire input stream as a
44−-- single window.
84+-- | Convert an incremental fold to a cumulative fold using the entire input
85+-- stream as a single window.
4586--
46−-- >>> whole f = Fold.lmap (\x -> (x, Nothing)) f
87+-- >>> cumulative f = Fold.lmap (\x -> (x, Nothing)) f
4788--
48−{-# INLINE whole #-}
49−whole :: Fold m (a, Maybe a) b -> Fold m a b
50−whole = Fold.lmap (, Nothing)
89+{-# INLINE cumulative #-}
90+cumulative :: Fold m (a, Maybe a) b -> Fold m a b
91+cumulative = Fold.lmap (, Nothing)
5192
5293-------------------------------------------------------------------------------
5394-- Sum
@@ -85,6 +126,12 @@ sumInt = Fold step initial extract
85126--
86127-- | Sum of all the elements in a rolling window:
87128--
129+-- \(S = \sum_{i=1}^n x_{i}\)
130+--
131+-- This is the first power sum.
132+--
133+-- >>> sum = powerSum 1
134+--
88135-- Uses Kahan-Babuska-Neumaier style summation for numerical stability of
89136-- floating precision arithmetic.
90137--
@@ -127,6 +174,10 @@ sum = Fold step initial extract
127174
128175-- | The number of elements in the rolling window.
129176--
177+-- This is the \(0\)th power sum.
178+--
179+-- >>> length = powerSum 0
180+--
130181{-# INLINE length #-}
131182length :: (Monad m, Num b) => Fold m (a, Maybe a) b
132183length = Fold.foldl' step 0
@@ -136,6 +187,28 @@ length = Fold.foldl' step 0
136187 step w (_, Nothing) = w + 1
137188 step w _ = w
138189
190+-- | Sum of the \(k\)th power of all the elements in a rolling window:
191+--
192+-- \(S_k = \sum_{i=1}^n x_{i}^k\)
193+--
194+-- >>> powerSum k = lmap (^ k) sum
195+--
196+-- /Space/: \(\mathcal{O}(1)\)
197+--
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
202+
203+-- | Like 'powerSum' but powers can be negative or fractional. This is slower
204+-- than 'powerSum' for positive intergal powers.
205+--
206+-- >>> powerSumFrac p = lmap (** p) sum
207+--
208+{-# INLINE powerSumFrac #-}
209+powerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a
210+powerSumFrac p = lmap (** p) sum
211+
139212-------------------------------------------------------------------------------
140213-- Location
141214-------------------------------------------------------------------------------
@@ -149,8 +222,8 @@ length = Fold.foldl' step 0
149222--
150223-- | The minimum element in a rolling window.
151224--
152−-- If you want to compute the minimum of the entire stream Fold.minimum from streamly
153−-- package would be much faster.
225+-- If you want to compute the minimum of the entire stream Fold.minimum from
226+-- streamly package would be much faster.
154227--
155228-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
156229--
@@ -199,8 +272,8 @@ minimum = Fold step initial extract
199272--
200273-- | The maximum element in a rolling window.
201274--
202−-- If you want to compute the maximum of the entire stream Fold.maximum from streamly
203−-- package would be much faster.
275+-- If you want to compute the maximum of the entire stream Fold.maximum from
276+-- streamly package would be much faster.
204277--
205278-- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size.
206279--
@@ -246,6 +319,23 @@ maximum = Fold step initial extract
246319 $ fromMaybe (0, error "max: Empty stream")
247320 $ DQ.head q
248321
322+-- | Arithmetic mean of elements in a sliding window:
323+--
324+-- \(\mu = \frac{\sum_{i=1}^n x_{i}}{n}\)
325+--
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
328+-- stream.
329+--
330+-- >>> mean = Fold.teeWith (/) sum length
331+--
332+-- /Space/: \(\mathcal{O}(1)\)
333+--
334+-- /Time/: \(\mathcal{O}(n)\)
335+{-# INLINE mean #-}
336+mean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a
337+mean = Fold.teeWith (/) sum length
338+
249339
250340-- | The difference between the maximum and minimum elements of a rolling window.
251341--