Skip to main content

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!("{:?}", &regression_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 = (&regression_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}