| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Circuit.Stats
Description
Streaming statistics and state-machine specialisations built on
Process.
The Process carrier itself lives in Circuit.Process; this module is the
box library: moving averages, standard deviations, regression, quantiles,
delays, and related streaming combinators.
fold is re-exported from Circuit.Process and is total (it returns
Nothing for an empty list). The statistical implementations in this module
are the canonical circuits-stats reference boxes.
Synopsis
- data Process a b where
- dipure :: (a -> a -> a) -> Process a a
- before :: Process b c -> (a -> b) -> Process a c
- after :: Process a b -> (b -> c) -> Process a c
- scan :: Process a b -> [a] -> [b]
- fold :: Process a b -> [a] -> Maybe b
- newtype Averager a b = Averager {
- sumCount :: (a, b)
- pattern A :: a -> b -> Averager a b
- av :: Divisive a => Averager a a -> a
- av_ :: (Eq a, Additive a, Divisive a) => Averager a a -> a -> a
- online :: (Divisive b, Additive b) => (a -> b) -> (b -> b) -> Process a b
- ma :: (Divisive a, Additive a) => a -> Process a a
- absma :: (Divisive a, Absolute a) => a -> Process a a
- sqma :: (Divisive a, Additive a) => a -> Process a a
- std :: ExpField a => a -> Process a a
- cov :: Field a => Process a a -> Process (a, a) a
- corrGauss :: ExpField a => a -> Process (a, a) a
- corr :: ExpField a => Process a a -> Process a a -> Process (a, a) a
- beta1 :: ExpField a => Process a a -> Process (a, a) a
- alpha1 :: ExpField a => Process a a -> Process (a, a) a
- reg1 :: ExpField a => Process a a -> Process (a, a) (a, a)
- beta :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) (Array Vector '[n] a)
- alpha :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) a
- reg :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) (Array Vector '[n] a, a)
- asum :: Additive a => Process a a
- aconst :: b -> Process a b
- last :: Process a a
- maybeLast :: a -> Process (Maybe a) a
- delay1 :: a -> Process a a
- delay :: [a] -> Process a a
- window :: Int -> Process a (Seq a)
- diff :: (a -> a -> b) -> Process a b
- gdiff :: (a -> b) -> (a -> a -> b) -> Process a b
- same :: Eq b => (a -> b) -> Process a Bool
- countM :: Ord a => Process a (Map a Int)
- sumM :: (Ord a, Additive b) => Process (a, b) (Map a b)
- listify :: Process a b -> Process [a] [b]
- data Medianer a b = Medianer {}
- onlineL1 :: (Ord b, Field b, Absolute b) => b -> b -> (a -> b) -> (b -> b) -> Process a b
- maL1 :: (Ord a, Field a, Absolute a) => a -> a -> a -> Process a a
Process re-export
A stateful process from a to b.
The existential state type s is hidden; the observable interface is the
triple inject step extract. Keeping the triple as the primitive (rather
than fusing extract into the step) preserves the streaming-statistics
invariant that the first output is extract (inject x), before any step.
Instances
| Copy (->) a => Copy Process a # | |
Defined in Circuit.Process | |
| Merge (->) a => Merge Process a # | |
Defined in Circuit.Process | |
| Category Process # | |
| Shared (,) Process # | Cartesian shared fusion on processes. The two processes share one feedback channel |
| Discard Process (a :: Type) # | |
Defined in Circuit.Process | |
| Zero (->) a => Zero Process (a :: Type) # | |
Defined in Circuit.Process | |
| Channel Either Process # | |
| Channel (,) Process # | |
| Strength Either Process # | |
| Strength (,) Process # | |
Defined in Circuit.Process | |
| Traced Either Process # | |
| Traced (,) Process # | |
Defined in Circuit.Process | |
| Action (,) Process # | |
Defined in Circuit.Process | |
| Tensor (,) Process # | |
| Unital (,) Process # | |
before :: Process b c -> (a -> b) -> Process a c Source #
Precompose a pure function before a process.
after :: Process a b -> (b -> c) -> Process a c Source #
Postcompose a pure function after a process.
scan :: Process a b -> [a] -> [b] #
List specialization of scanStream.
fold :: Process a b -> [a] -> Maybe b #
List specialization of foldStream.
Most common statistics are averages, which are some sort of aggregation of values (sum) and some sort of sample size (count).
Instances
| (Additive a, Additive b) => Monoid (Averager a b) Source # | av mempty == nan |
| (Additive a, Additive b) => Semigroup (Averager a b) Source # | |
| (Eq a, Eq b) => Eq (Averager a b) Source # | |
| (Show a, Show b) => Show (Averager a b) Source # | |
| (Additive a, Additive b) => Additive (Averager a b) Source # | |
| (Subtractive a, Subtractive b) => Subtractive (Averager a b) Source # | |
av :: Divisive a => Averager a a -> a Source #
extract the average from an Averager
av gives NaN on zero divide
av_ :: (Eq a, Additive a, Divisive a) => Averager a a -> a -> a Source #
substitute a default value on zero-divide
av_ (Averager (0,0)) x == x
online :: (Divisive b, Additive b) => (a -> b) -> (b -> b) -> Process a b Source #
online f g is a Process where f is a transformation of the data and
g is a decay function (usually convergent to zero) applied at each step.
online id id == av
online is best understood by examining usage
to produce a moving average and standard deviation:
An exponentially-weighted moving average with a decay rate of 0.9
ma r == online id (*r)
An exponentially-weighted moving average of the square.
sqma r = online (\x -> x * x) (* r)
Parallel-style exponentially-weighted standard deviation computation:
std r = parWith (\s ss -> sqrt (ss - s ** 2)) (ma r) (sqma r)
Statistics
The doctest examples are composed from some random series generated with Circuit.Stats.Simulate.
- xs0, xs1 & xs2 are samples from N(0,1)
- xsp is a pair of N(0,1)s with a correlation of 0.8
>>>:set -XDataKinds>>>import Data.Maybe (fromMaybe)>>>import Circuit.Stats.Simulate>>>let fold' p = fromMaybe (error "fold': empty input") . fold p>>>g <- create>>>xs0 <- rvs g 10000>>>xs1 <- rvs g 10000>>>xs2 <- rvs g 10000>>>xsp <- rvsp g 10000 0.8
ma :: (Divisive a, Additive a) => a -> Process a a Source #
A moving average using a decay rate of r. r=1 represents the simple average, and r=0 represents the latest value.
>>>fold' (ma 0) ([1..100])100.0
>>>fold' (ma 1) ([1..100])50.5
>>>fold' (ma 0.99) xs09.713356299018187e-2
absma :: (Divisive a, Absolute a) => a -> Process a a Source #
absolute average
>>>fold' (absma 1) xs00.8075705557429647
sqma :: (Divisive a, Additive a) => a -> Process a a Source #
average square
fold' (ma r) . fmap (**2) == fold' (sqma r)
std :: ExpField a => a -> Process a a Source #
standard deviation
The construction of standard deviation, combining ma and sqma in parallel:
std r = parWith (\s ss -> sqrt (ss - s ** (one+one))) (ma r) (sqma r)
The average deviation of the numbers 1..1000 is about 1 / sqrt 12 * 1000 https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)#Standard_uniform
>>>fold' (std 1) [0..1000]288.9636655359978
The average deviation with a decay of 0.99
>>>fold' (std 0.99) [0..1000]99.28328803163829
>>>fold' (std 1) xs01.0126438036262801
cov :: Field a => Process a a -> Process (a, a) a Source #
The covariance of a tuple given an underlying central tendency fold.
>>>fold' (cov (ma 1)) xsp0.7818936662586868
corrGauss :: ExpField a => a -> Process (a, a) a Source #
correlation of a tuple, specialised to Guassian
>>>fold' (corrGauss 1) xsp0.7978347126677433
corr :: ExpField a => Process a a -> Process a a -> Process (a, a) a Source #
a generalised version of correlation of a tuple
>>>fold' (corr (ma 1) (std 1)) xsp0.7978347126677433
corr (ma r) (std r) == corrGauss r
beta1 :: ExpField a => Process a a -> Process (a, a) a Source #
The beta in a simple linear regression of an (independent variable, single dependent variable) tuple given an underlying central tendency fold.
This is a generalisation of the classical regression formula, where averages are replaced by Process statistics.
\[ \begin{align} \beta & = \frac{n\sum xy - \sum x \sum y}{n\sum x^2 - (\sum x)^2} \\ & = \frac{n^2 \overline{xy} - n^2 \bar{x} \bar{y}}{n^2 \overline{x^2} - n^2 \bar{x}^2} \\ & = \frac{\overline{xy} - \bar{x} \bar{y}}{\overline{x^2} - \bar{x}^2} \\ \end{align} \]
>>>fold' (beta1 (ma 1)) $ zipWith (\x y -> (y, x + y)) xs0 xs10.999747321294513
alpha1 :: ExpField a => Process a a -> Process (a, a) a Source #
The alpha in a simple linear regression of an (independent variable, single dependent variable) tuple given an underlying central tendency fold.
\[ \begin{align} \alpha & = \frac{\sum y \sum x^2 - \sum x \sum xy}{n\sum x^2 - (\sum x)^2} \\ & = \frac{n^2 \bar{y} \overline{x^2} - n^2 \bar{x} \overline{xy}}{n^2 \overline{x^2} - n^2 \bar{x}^2} \\ & = \frac{\bar{y} \overline{x^2} - \bar{x} \overline{xy}}{\overline{x^2} - \bar{x}^2} \\ \end{align} \]
>>>fold' (alpha1 (ma 1)) $ zipWith (\x y -> ((3+y), x + 0.5 * (3 + y))) xs0 xs11.3680496627365146e-2
reg1 :: ExpField a => Process a a -> Process (a, a) (a, a) Source #
The (alpha, beta) tuple in a simple linear regression of an (independent variable, single dependent variable) tuple given an underlying central tendency fold.
>>>fold' (reg1 (ma 1)) $ zipWith (\x y -> ((3+y), x + 0.5 * (3 + y))) xs0 xs1(1.3680496627365146e-2,0.4997473212944953)
beta :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) (Array Vector '[n] a) Source #
multiple regression
\[ \begin{align} {\hat {{\mathbf {B}}}}=({\mathbf {X}}^{{{\rm {T}}}}{\mathbf {X}})^{{ -1}}{\mathbf {X}}^{{{\rm {T}}}}{\mathbf {Y}} \end{align} \]
\[ \begin{align} {\mathbf {X}}={\begin{bmatrix}{\mathbf {x}}_{1}^{{{\rm {T}}}}\\{\mathbf {x}}_{2}^{{{\rm {T}}}}\\\vdots \\{\mathbf {x}}_{n}^{{{\rm {T}}}}\end{bmatrix}}={\begin{bmatrix}x_{{1,1}}&\cdots &x_{{1,k}}\\x_{{2,1}}&\cdots &x_{{2,k}}\\\vdots &\ddots &\vdots \\x_{{n,1}}&\cdots &x_{{n,k}}\end{bmatrix}} \end{align} \]
>>>let ys = zipWith3 (\x y z -> 0.1 * x + 0.5 * y + 1 * z) xs0 xs1 xs2>>>let zs = zip (zipWith (\x y -> F.array @Vector @'[2] [x,y]) xs1 xs2) ys>>>fold' (beta 0.99) zs[0.6228820021456606,0.8461936860075405]
alpha :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) a Source #
alpha in a multiple regression
reg :: forall a (n :: Nat). (ExpField a, KnownNat n) => a -> Process (Array Vector '[n] a, a) (Array Vector '[n] a, a) Source #
multiple regression
>>>let ys = zipWith3 (\x y z -> 0.1 * x + 0.5 * y + 1 * z) xs0 xs1 xs2>>>let zs = zip (zipWith (\x y -> F.array @Vector @'[2] [x,y]) xs1 xs2) ys>>>fold' (reg 0.99) zs([0.6228820021456606,0.8461936860075405],2.536775201287266e-2)
maybeLast :: a -> Process (Maybe a) a Source #
most recent value if it exists, previous value otherwise.
Arguments
| :: [a] | initial statistical values, delay equals length |
| -> Process a a |
delays values by n steps
delay [0] == delay1 0
delay [] == id
delay [1,2] = delay1 2 . delay1 1
>>>scan (delay [-2,-1]) [0..3][-2,-1,0,1]
Autocorrelation example:
scan (parWith (,) id (delay [0]) >>> beta (ma 0.99)) xs0
window :: Int -> Process a (Seq a) Source #
a moving window of a's, most recent at the front of the sequence
listify :: Process a b -> Process [a] [b] Source #
Convert a Process to a Process operating on lists.
median
A rough Median. The average absolute value of the stat is used to callibrate estimate drift towards the median