forrust/prediction/dumb.rs
1use crate::forecasting::ExpSmoothing;
2use crate::time_series::{TimeSeries, Style};
3use crate::regression::LinearRegression;
4use crate::plotable::Plotable;
5use crate::time_series::Grouper;
6
7use plotlib::view::{View, ContinuousView};
8use plotlib::repr::Plot;
9
10use plotlib::page::Page;
11
12const DEFAULT_ALPHA: f64 = 0.4;
13
14/// Takes an exponential smoothing and makes a dumb prediction
15/// of the next season
16/// This uses an algorithm i made up myself
17/// that takes to account the distances the exponential smoothing
18/// for each month relative to the linear regresion of a time series
19/// and calculates a growth factor thats 'a prediction' of the signal
20/// for the future, and thats when i use a random value to add
21/// some noise
22pub struct Dumb {
23 original: TimeSeries,
24 expsmooth: Option<ExpSmoothing>,
25 linear_reg: Option<LinearRegression>,
26 season: usize,
27 prediction: Vec<(f64, f64)>,
28}
29
30impl Dumb {
31 pub fn new(time_series: &TimeSeries) -> Self {
32 let mut this = Self {
33 original: time_series.clone(),
34 expsmooth: None,
35 linear_reg: None,
36 season: 0,
37 prediction: Vec::new(),
38 };
39 this.expsmooth = Some(
40 ExpSmoothing::new(time_series).with_alpha(DEFAULT_ALPHA)
41 );
42 this.linear_reg = Some(LinearRegression::new(time_series));
43 this
44 }
45
46 /// Sets the length of the season
47 /// its a must for the simulation
48 /// Dumb is usable after this point
49 pub fn with_season(mut self, season: usize) -> Self {
50 self.season = season; //0 not set, panics
51 self.update_data();
52 self
53 }
54
55 fn update_data(&mut self) {
56 if self.season == 0 {
57 panic!("No season length set for Dumb")
58 }
59
60 let mut sm = self.exp_smooth().as_time_series().get_data();
61 sm.insert(0, (1.0, -1.0));
62 let mut sm = TimeSeries::from_pairs_vec(sm);
63 //first, the boostrap
64 //enero bootstrap
65 //cada mes luego de enero usar mi algoritmo
66 if let Some(expsmooth) = &self.expsmooth {
67 self.prediction = Vec::new(); //reset
68 let alpha = expsmooth.alpha();
69 let len = sm.len();
70 let r = alpha * sm.get_range_at(len - 1) + (1.0 - alpha) * expsmooth.as_time_series().get_range_at(len - 1); //last
71
72 self.prediction.push((1.0, r)); //first value just a boostrap
73
74 //separating all months in sesons acording to season length (self.season)
75 let mut past = Vec::new();
76 let mut c = 1;
77 for seas in 0..(sm.len() / self.season) as usize {
78 //for every past season of each month
79 past.push(vec![0.0; self.season]);
80 for i in 0..self.season {
81 //for each point of this season
82 past[seas][i] = sm.get_range_at(c);
83 c += 1;
84 }
85
86 }
87
88 //for each month, get its seasonal components
89 //its like inverting the array from 2x12 to 12x2
90 let mut months = Vec::new(); //holds a vector of past values for every season of a month
91 for month in 0..self.season {
92 months.push(vec![0.0; past.len()]);
93 for season in 0..past.len() {
94 months[month][season] = past[season][month];
95 }
96 }
97 //println!("{:?}", months);
98 //get distances
99 let mut x_points = Vec::new();
100 //x_points.push(vec![-1.0;past.len()]);//ignore first element: january
101 for i in 0..months.len() + 1 {//every month, its seasons
102 x_points.push(vec![0.0; past.len()]);
103 for season in 0..months[0].len() { //every season, every year
104 x_points[i][season] = (i + season*self.season) as f64;
105 }
106
107 }
108 //println!("{:?}", past);
109 //println!("{:?}", x_points);
110 //regression points used to get the distance between
111 //the regression line and the exponential smoothing signal
112 let regression_points: Vec<Vec<f64>> = x_points[1..].iter().map(
113 |x| {x.iter().map(
114 |y| {
115 self.get_linear_regression().calculate(y.clone())
116 }
117 ).collect()}
118 ).collect();
119 //println!("{:?}", &x_points[2..]); //ignore null and january
120 //println!("{:?}", self.exp_smooth().as_time_series().get_data());
121 //println!("{:?}", ®ression_points[1..]);
122
123 let smooth = self.exp_smooth().as_time_series();
124
125 //x_points an regression_points have the same sice,
126 //x_points represents the numbers in x of each month
127 //regression_points represents the value of regresion in each season for amonths
128 let x_points = (&x_points[2..]).to_vec();
129 let regression_points = (®ression_points).to_vec();
130
131 /// get the distances between the exp smooth and linear reg of a month every year
132 let mut distances = Vec::new();
133 for i in 0..x_points.len() { //every month pair
134 distances.push(Vec::new());
135 for j in 0..x_points[i].len() { //every element of the pair
136 distances[i].push(smooth.get_range_at(x_points[i][j] as usize) - regression_points[i][j]);
137 //print!("distance: {}, ", smooth.get_range_at(x_points[i][j] as usize) - regression_points[i][j] )
138 }
139 }
140
141 let mut factors = Vec::new();
142 for i in 0..distances.len() {
143 factors.push(Vec::new());
144 for j in 0..distances[i].len() - 1 {
145 let mut growth = distances[i][j + 1] / distances[i][j];
146 //FIXME: con que uno sea nega ya todo es nega
147 //muffling para negativos
148 if distances[i][j] < 0.0 && distances[i][j + 1] > 0.0 { //last below linear reg
149 //change sign and use only half
150 growth = -1.0 * growth / 2.0;
151 }
152
153 if growth > 10.0 || growth < -10.0 {
154 growth = DEFAULT_ALPHA * growth;
155 }
156 factors[i].push(growth);//the lastes divided by the one before: growth
157 }
158 }
159
160 //factors to multiply last distances for
161 let mut pro_factors = Vec::new();
162 for i in 0..factors.len() { //hardcoded for 2 factors, in 3 years
163 if factors[i].len() != 2 {
164 panic!("HARDCODED FOR 2 FACTORS IN 3 YEARS");
165 }
166 pro_factors.push(factors[i][0] * 0.8 + factors[i][1]);
167 }
168
169 let mut last_d = Vec::new(); //hardcoded for 2 factors, in 3 years
170 for i in 0..distances.len() {
171 last_d.push(distances[i][2] * pro_factors[i]);
172 }
173 //println!("ex = {:?}", expsmooth.as_time_series().get_data());
174 println!("d = {:?}", distances);
175 //println!("f = {:?}", factors);
176 //println!("pro= {:?}", pro_factors);
177 println!("lastd = {:?}", last_d);
178 let mut finales = Vec::new();
179 for i in 0..last_d.len() {
180
181 finales.push(last_d[i] + self.get_linear_regression().calculate(i as f64 + 36.0));
182 }
183 let mut counter = 38.0; //december + 1
184 for element in finales.iter() {
185 self.prediction.push((counter, element.clone().abs().floor()));
186 counter += 1.0;
187 }
188
189 //HARDCODE: FIRST ELEMENT IN PREDICTION IS 37: JAN YEAR 4
190 self.prediction[0].0 = 37.0;
191 println!("finales: {:?}", self.prediction());
192 //TODO: Calculate distance between regression points
193 //and exponential smooting signal
194
195 //TODO: get the growth factors
196 //TODO: muffle growths: for 3 seasons use (0.4, 0.6) -> muffling factors
197 //TODO: new point: (muffled 1 + muffled 2) * last distance
198 //TODO: add noise to prediction signal, no noise algorithm yet
199 //TODO: calculate regression in new point
200 //TODO: add the combo (signal + noise) to the calculated regression point
201 //TODO(CHECK): If the values get too spiky, muffle with mean * alpha factor at +- random level
202 } else {
203 panic!("Can't update Dumbs data, no expsmooth set.");
204 }
205 }
206
207 pub fn prediction(&self) -> Vec<(f64, f64)> {
208 self.prediction.clone()
209 }
210
211 pub fn get_linear_regression(&self) -> LinearRegression {
212 if let Some(reg) = &self.linear_reg {
213 reg.clone()
214 } else {
215 panic!("No linear regression calculated for Dumb");
216 }
217 }
218
219 fn exp_smooth(&self) -> ExpSmoothing {
220 if let Some(ex) = &self.expsmooth {
221 ex.clone()
222 } else {
223 panic!("Dumb has no exponential smoothing set")
224 }
225 }
226
227 pub fn plot_to_file(&self, filename: String) {
228 Page::single(
229 self.plot().as_ref()
230 ).save(filename).unwrap();
231 }
232}
233
234impl Plotable for Dumb {
235 fn plot(&self) -> Box<dyn View> {
236 //plot every thing
237 //timseries + regresion + smooth + prediction
238 let mut tm = self.original.clone();
239 let smooth = self.exp_smooth();
240 let linear = self.get_linear_regression();
241 let pred = TimeSeries::from_pairs_vec(self.prediction());
242
243 let mut group = Grouper::new(&tm)
244 .last_with_style(Style::from_color("#000000"))
245 .add(&smooth.as_time_series())
246 .last_with_style(Style::from_color("#af0af6"))
247 .add(&linear.as_time_series())
248 .last_with_style(Style::from_color("#87faa4"))
249 .add(&pred)
250 .last_with_style(Style::from_color("#ff00a2"));
251
252 group.plot()
253 }
254 fn as_plot(&self) -> Plot {
255 unimplemented!()
256 }
257}