circuits-stats
Safe HaskellNone
LanguageGHC2024

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

Process re-export

data Process a b where #

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.

Constructors

Process :: forall s a b. (a -> s) -> (s -> a -> s) -> (s -> b) -> Process a b 

Instances

Instances details
Copy (->) a => Copy Process a # 
Instance details

Defined in Circuit.Process

Methods

copy :: Process a (a, a) #

Merge (->) a => Merge Process a # 
Instance details

Defined in Circuit.Process

Methods

plus :: Process (a, a) a #

Category Process # 
Instance details

Defined in Circuit.Process

Methods

id :: Process a a #

(.) :: Process b c -> Process a b -> Process a c #

Shared (,) Process #

Cartesian shared fusion on processes.

The two processes share one feedback channel s. At each tick the schedule chooses which body advances; the gated body's input is discarded and it does not step. Each process is injected lazily on its first firing, so a body that is never scheduled consumes no inputs and produces no outputs.

Instance details

Defined in Circuit.Process

Methods

sharedBy :: Schedule s -> Process (s, a) (s, b) -> Process (s, c) (s, d) -> Process (s, (a, c)) (s, These b d) #

Discard Process (a :: Type) # 
Instance details

Defined in Circuit.Process

Methods

discard :: Process a () #

Zero (->) a => Zero Process (a :: Type) # 
Instance details

Defined in Circuit.Process

Methods

zero :: Process () a #

Channel Either Process # 
Instance details

Defined in Circuit.Process

Methods

assoc :: Process (Either (Either a b) c) (Either a (Either b c)) #

assoc' :: Process (Either a (Either b c)) (Either (Either a b) c) #

slide :: Process (Either a (Either b c)) (Either b (Either a c)) #

Channel (,) Process # 
Instance details

Defined in Circuit.Process

Methods

assoc :: Process ((a, b), c) (a, (b, c)) #

assoc' :: Process (a, (b, c)) ((a, b), c) #

slide :: Process (a, (b, c)) (b, (a, c)) #

Strength Either Process # 
Instance details

Defined in Circuit.Process

Methods

strength :: Process b c -> Process (Either a b) (Either a c) #

Strength (,) Process # 
Instance details

Defined in Circuit.Process

Methods

strength :: Process b c -> Process (a, b) (a, c) #

Traced Either Process # 
Instance details

Defined in Circuit.Process

Methods

trace :: Process (Either a b) (Either a c) -> Process b c #

Traced (,) Process # 
Instance details

Defined in Circuit.Process

Methods

trace :: Process (a, b) (a, c) -> Process b c #

Action (,) Process # 
Instance details

Defined in Circuit.Process

Methods

braid :: Process (a, b) (b, a) #

Tensor (,) Process # 
Instance details

Defined in Circuit.Process

Methods

tensor :: Process a b -> Process c d -> Process (a, c) (b, d) #

Unital (,) Process # 
Instance details

Defined in Circuit.Process

Methods

unitl :: Process (Unit (,), a) a #

unitl' :: Process a (Unit (,), a) #

unitr :: Process (a, Unit (,)) a #

unitr' :: Process a (a, Unit (,)) #

dipure :: (a -> a -> a) -> Process a a Source #

Create a Process from a (pure) binary operation.

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.

newtype Averager a b Source #

Most common statistics are averages, which are some sort of aggregation of values (sum) and some sort of sample size (count).

Constructors

Averager 

Fields

Instances

Instances details
(Additive a, Additive b) => Monoid (Averager a b) Source #
av mempty == nan
Instance details

Defined in Circuit.Stats

Methods

mempty :: Averager a b #

mappend :: Averager a b -> Averager a b -> Averager a b #

mconcat :: [Averager a b] -> Averager a b #

(Additive a, Additive b) => Semigroup (Averager a b) Source # 
Instance details

Defined in Circuit.Stats

Methods

(<>) :: Averager a b -> Averager a b -> Averager a b #

sconcat :: NonEmpty (Averager a b) -> Averager a b #

stimes :: Integral b0 => b0 -> Averager a b -> Averager a b #

(Eq a, Eq b) => Eq (Averager a b) Source # 
Instance details

Defined in Circuit.Stats

Methods

(==) :: Averager a b -> Averager a b -> Bool #

(/=) :: Averager a b -> Averager a b -> Bool #

(Show a, Show b) => Show (Averager a b) Source # 
Instance details

Defined in Circuit.Stats

Methods

showsPrec :: Int -> Averager a b -> ShowS #

show :: Averager a b -> String #

showList :: [Averager a b] -> ShowS #

(Additive a, Additive b) => Additive (Averager a b) Source # 
Instance details

Defined in Circuit.Stats

Methods

(+) :: Averager a b -> Averager a b -> Averager a b #

zero :: Averager a b #

(Subtractive a, Subtractive b) => Subtractive (Averager a b) Source # 
Instance details

Defined in Circuit.Stats

Methods

negate :: Averager a b -> Averager a b #

(-) :: Averager a b -> Averager a b -> Averager a b #

pattern A :: a -> b -> Averager a b Source #

Pattern for an Averager.

A sum count

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) xs0
9.713356299018187e-2

absma :: (Divisive a, Absolute a) => a -> Process a a Source #

absolute average

>>> fold' (absma 1) xs0
0.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) xs0
1.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)) xsp
0.7818936662586868

corrGauss :: ExpField a => a -> Process (a, a) a Source #

correlation of a tuple, specialised to Guassian

>>> fold' (corrGauss 1) xsp
0.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)) xsp
0.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 xs1
0.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 xs1
1.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)

asum :: Additive a => Process a a Source #

accumulated sum

aconst :: b -> Process a b Source #

constant Process

last :: Process a a Source #

most recent value

maybeLast :: a -> Process (Maybe a) a Source #

most recent value if it exists, previous value otherwise.

delay1 :: a -> Process a a Source #

delay input values by 1

delay Source #

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

diff :: (a -> a -> b) -> Process a b Source #

binomial operator applied to last and this value

gdiff :: (a -> b) -> (a -> a -> b) -> Process a b Source #

generalised diff function.

same :: Eq b => (a -> b) -> Process a Bool Source #

Unchanged since last time.

countM :: Ord a => Process a (Map a Int) Source #

Count observed values

sumM :: (Ord a, Additive b) => Process (a, b) (Map a b) Source #

Sum values of a key-value pair.

listify :: Process a b -> Process [a] [b] Source #

Convert a Process to a Process operating on lists.

median

data Medianer a b Source #

A rough Median. The average absolute value of the stat is used to callibrate estimate drift towards the median

Constructors

Medianer 

Fields

onlineL1 :: (Ord b, Field b, Absolute b) => b -> b -> (a -> b) -> (b -> b) -> Process a b Source #

onlineL1' takes a function and turns it into a Process where the step is an incremental update of an (isomorphic) median statistic.

maL1 :: (Ord a, Field a, Absolute a) => a -> a -> a -> Process a a Source #

moving median