hipparchus_mean/
movingavg.rs

1use crate::value::Fp;
2
3pub trait MovingAverage<T:Fp>
4{
5    fn average(self:&Self) -> Option<T>;
6    fn push(self:&mut Self, v:T) -> T;
7}
8
9#[derive(Debug)]
10pub struct CumulativeMovingAverage<T:Fp>
11{
12    average: T,
13    total: i32,
14}
15
16impl<T:Fp> CumulativeMovingAverage<T>
17{
18    pub fn new() -> CumulativeMovingAverage<T>
19    {
20        Self::from(T::zero(), 0)
21    }
22
23    pub fn from(average: T, total: i32) -> CumulativeMovingAverage<T>
24    {
25        CumulativeMovingAverage
26        {
27            average,
28            total,
29        }
30    }
31}
32
33impl<T:Fp> MovingAverage<T> for CumulativeMovingAverage<T>
34{
35    fn average(self:&Self) -> Option<T>
36    {
37        if self.total <= 0 { None } else { Some(self.average) }
38    }
39
40    fn push(self:&mut Self, v:T) -> T
41    {
42        let total = T::from_i32(self.total).unwrap();
43        self.average = (self.average * total + v) / (total + T::one());
44        self.total += 1;
45        self.average
46    }
47}
48
49pub struct WeightedMovingAverage<T:Fp>
50{
51    total: u64,
52    agg: T,
53    weight: T,
54}
55
56impl<T:Fp> WeightedMovingAverage<T>
57{
58    pub fn new() -> WeightedMovingAverage<T>
59    {
60        Self::from(0, T::zero())
61    }
62
63    pub fn from(total: u64, agg:T) -> WeightedMovingAverage<T>
64    {
65        WeightedMovingAverage
66        { 
67            total, agg,
68            weight: T::from_u64( (total + 1) * total / 2 ).unwrap()
69        }
70    }
71}
72
73impl<T:Fp> MovingAverage<T> for WeightedMovingAverage<T>
74{
75    fn average(self:&Self) -> Option<T>
76    {
77        if self.total <= 0 { None } else { Some(self.agg / self.weight) }
78    }
79
80    fn push(self:&mut Self, v:T) -> T
81    {
82        self.total += 1;
83        let w = T::from_u64(self.total).unwrap();
84        self.agg = self.agg + w * v;
85        self.weight = self.weight + w;
86        self.agg / self.weight
87    }
88}
89
90pub struct ExponentialMovingAverage<T:Fp>
91{
92    average: Option<T>,
93    decay: T,
94}
95
96impl<T:Fp> ExponentialMovingAverage<T>
97{
98    pub fn new(decay:f32) -> ExponentialMovingAverage<T>
99    {
100        Self::from(None, decay)
101    }
102
103    pub fn from(init: Option<T>, decay: f32) -> ExponentialMovingAverage<T>
104    {
105        ExponentialMovingAverage
106        {
107            average: init,
108            decay: T::from_f32(decay).unwrap(),
109        }
110    }
111}
112
113impl<T:Fp> MovingAverage<T> for ExponentialMovingAverage<T>
114{
115    fn average(self:&Self) -> Option<T>
116    {
117        self.average
118    }
119
120    fn push(self:&mut Self, v:T) -> T
121    {
122        let current = self.average.unwrap_or(v);
123        let next = current * self.decay + v * (T::one() - self.decay);
124        self.average = Some(next);
125        next
126    }
127}
128
129#[cfg(test)]
130mod tests 
131{
132    use super::*;
133    use float_cmp::assert_approx_eq;
134
135    #[test]
136    fn test_cma_new()
137    {
138        let cma = CumulativeMovingAverage::<f32>::new();
139        assert_eq!(0, cma.total);
140        assert_eq!(0.0, cma.average);
141        assert_eq!(None, cma.average());
142    }
143
144    #[test]
145    fn test_cma_from()
146    {
147        let cma = CumulativeMovingAverage::<f32>::from(1.0, 5);
148        assert_eq!(5, cma.total);
149        assert_approx_eq!(f32, 1.0, cma.average);
150        assert_approx_eq!(f32, 1.0, cma.average().unwrap());
151    }
152
153    #[test]
154    fn test_cma()
155    {
156        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
157        let expected = vec![1.0, 1.5, 2.0, 2.5, 3.0];
158
159        let mut cma = CumulativeMovingAverage::<f32>::new();
160        let mut i = 0;
161        for d in data
162        {
163            let e = expected[i];
164            let actual_push = cma.push(d);
165            let actual_average = cma.average().unwrap();
166            assert_approx_eq!(f32, actual_push, actual_average);
167            assert_approx_eq!(f32, e, actual_push);
168            i += 1;
169        }
170    }
171
172    #[test]
173    fn test_wma_new()
174    {
175        let wma = WeightedMovingAverage::<f32>::new();
176        assert_eq!(0, wma.total);
177        assert_eq!(0.0, wma.agg);
178        assert_eq!(0.0, wma.weight);
179        assert_eq!(None, wma.average());
180    }
181
182    #[test]
183    fn test_wma_from()
184    {
185        let wma = WeightedMovingAverage::<f32>::from(4, 30.0);
186        assert_eq!(4, wma.total);
187        assert_eq!(30.0, wma.agg);
188        assert_eq!(10.0, wma.weight);
189        assert_eq!(3.0, wma.average().unwrap());
190    }
191
192    #[test]
193    fn test_wma()
194    {
195        //  weight:  1,  3,  6, 10
196        //       w:  1,  2,  3,  4
197        //    item:  6,  3,  4,  4
198        //   delta:  6,  6, 12, 16
199        //     agg:  6, 12, 24, 40
200        // average:  6,  4,  4,  4
201        let data = vec![6.0, 3.0, 4.0, 4.0];
202        let expected = vec![6.0, 4.0, 4.0, 4.0];
203        let mut wma = WeightedMovingAverage::<f32>::new();
204        let mut i = 0;
205        for d in data
206        {
207            let e = expected[i];
208            let actual_push = wma.push(d);
209            let actual_average = wma.average().unwrap();
210            assert_approx_eq!(f32, actual_push, actual_average);
211            assert_approx_eq!(f32, e, actual_push);
212            i += 1;
213        }
214    }
215    
216    #[test]
217    fn test_ema_new()
218    {
219        let ema = ExponentialMovingAverage::<f32>::new(0.9);
220        assert_eq!(0.9, ema.decay);
221        assert_eq!(None, ema.average);
222        assert_eq!(None, ema.average());
223    }
224
225    #[test]
226    fn test_ema_from()
227    {
228        let ema = ExponentialMovingAverage::<f32>::from(Some(1.0), 0.9);
229        assert_eq!(0.9, ema.decay);
230        assert_eq!(1.0, ema.average.unwrap());
231        assert_eq!(1.0, ema.average().unwrap());
232    }
233
234    #[test]
235    fn test_ema()
236    {
237        let decay = 0.9;
238        let data = vec![1.0, 0.0, 0.0, 0.0];
239        let expected = vec![1.0, decay, decay * decay, decay * decay * decay];
240        let mut ema = ExponentialMovingAverage::<f32>::new(decay);
241        let mut i = 0;
242        for d in data
243        {
244            let e = expected[i];
245            let actual_push = ema.push(d);
246            let actual_average = ema.average().unwrap();
247            assert_approx_eq!(f32, actual_push, actual_average);
248            assert_approx_eq!(f32, e, actual_push);
249            i += 1;
250        }
251    }
252}