hipparchus_mean/
movingavg.rs1use 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 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}