traquer/statistic/regression.rs
1//! Regression Functions
2//!
3//! Provides functions for estimating the relationships between a dependent variable
4//! and one or more independent variables. Potentially useful for forecasting and
5//! determing causal relationships.[[1]](https://en.wikipedia.org/wiki/Regression_analysis)
6use std::iter;
7
8use itertools::izip;
9use num_traits::cast::ToPrimitive;
10
11/// Mean Squared Error
12///
13/// Measures average squared difference between estimated and actual values.
14///
15/// ```math
16/// MSE = \frac{1}{T}\sum_{t=1}^{T} (X(t) - \hat{X}(t))^2
17/// ```
18///
19/// ## Sources
20///
21/// [[1]](https://en.wikipedia.org/wiki/Mean_squared_error)
22///
23/// # Examples
24///
25/// ```
26/// use traquer::statistic::regression;
27///
28/// regression::mse(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
29/// ```
30pub fn mse<'a, T: ToPrimitive>(data: &'a [T], estimate: &'a [T]) -> impl Iterator<Item = f64> + 'a {
31 data.iter()
32 .enumerate()
33 .zip(estimate)
34 .scan(0.0, |state, ((cnt, observe), est)| {
35 *state += (observe.to_f64().unwrap() - est.to_f64().unwrap()).powi(2);
36 Some(*state / (cnt + 1) as f64)
37 })
38}
39
40/// Root Mean Squared Error
41///
42/// Square root of MSE, but normalises it to same units as input.
43///
44/// ```math
45/// RMSE = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (Y_i – \hat{Y}_i)^2}
46/// ```
47///
48/// ## Sources
49///
50/// [[1]](https://en.wikipedia.org/wiki/Root_mean_square_deviation)
51///
52/// # Examples
53///
54/// ```
55/// use traquer::statistic::regression;
56///
57/// regression::rmse(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
58/// ```
59pub fn rmse<'a, T: ToPrimitive>(
60 data: &'a [T],
61 estimate: &'a [T],
62) -> impl Iterator<Item = f64> + 'a {
63 data.iter()
64 .enumerate()
65 .zip(estimate)
66 .scan(0.0, |state, ((cnt, observe), est)| {
67 *state += (observe.to_f64().unwrap() - est.to_f64().unwrap()).powi(2);
68 Some((*state / (cnt + 1) as f64).sqrt())
69 })
70}
71
72/// Mean Absolute Error
73///
74/// Measures the average magnitude of the errors in a set of predictions. Less sensitive to
75/// outliers compared to MSE and RMSE.
76///
77/// ```math
78/// MAE = \frac{1}{n} \sum_{i=1}^{n} |Y_i – \hat{Y}_i|
79/// ```
80///
81/// ## Sources
82///
83/// [[1]](https://en.wikipedia.org/wiki/Mean_absolute_error)
84///
85/// # Examples
86///
87/// ```
88/// use traquer::statistic::regression;
89///
90/// regression::mae(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
91/// ```
92pub fn mae<'a, T: ToPrimitive>(data: &'a [T], estimate: &'a [T]) -> impl Iterator<Item = f64> + 'a {
93 data.iter()
94 .enumerate()
95 .zip(estimate)
96 .scan(0.0, |state, ((cnt, observe), est)| {
97 *state += (observe.to_f64().unwrap() - est.to_f64().unwrap()).abs();
98 Some(*state / (cnt + 1) as f64)
99 })
100}
101
102/// Mean Absolute Percentage Error
103///
104/// Measures the error as a percentage of the actual value.
105///
106/// ```math
107/// MAPE = \frac{100%}{n} \sum_{i=1}^{n} \left|\frac{Y_i – \hat{Y}_i}{Y_i}\right|
108/// ```
109///
110/// ## Sources
111///
112/// [[1]](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error)
113///
114/// # Examples
115///
116/// ```
117/// use traquer::statistic::regression;
118///
119/// regression::mape(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
120/// ```
121pub fn mape<'a, T: ToPrimitive>(
122 data: &'a [T],
123 estimate: &'a [T],
124) -> impl Iterator<Item = f64> + 'a {
125 data.iter()
126 .enumerate()
127 .zip(estimate)
128 .scan(0.0, |state, ((cnt, observe), est)| {
129 *state += ((observe.to_f64().unwrap() - est.to_f64().unwrap())
130 / observe.to_f64().unwrap())
131 .abs();
132 Some(100.0 * *state / (cnt + 1) as f64)
133 })
134}
135
136/// Symmetric Mean Absolute Percentage Error
137///
138/// Similar to MAPE but attempts to limit the overweighting of negative errors in MAPE. Computed so
139/// range is bound to between 0 and 100.
140///
141/// ```math
142/// SMAPE = \frac{100}{n} \sum_{t=1}^n \frac{|F_t-A_t|}{|A_t|+|F_t|}
143/// ```
144///
145/// ## Sources
146///
147/// [[1]](https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error)
148///
149/// # Examples
150///
151/// ```
152/// use traquer::statistic::regression;
153///
154/// regression::smape(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
155/// ```
156pub fn smape<'a, T: ToPrimitive>(
157 data: &'a [T],
158 estimate: &'a [T],
159) -> impl Iterator<Item = f64> + 'a {
160 data.iter()
161 .enumerate()
162 .zip(estimate)
163 .scan(0.0, |state, ((cnt, observe), est)| {
164 *state += ((observe.to_f64().unwrap() - est.to_f64().unwrap()).abs()
165 / ((observe.to_f64().unwrap().abs() + est.to_f64().unwrap().abs()) / 2.0))
166 .max(0.0);
167 Some(100.0 * *state / (cnt + 1) as f64)
168 })
169}
170
171/// Mean Directional Accuracy
172///
173/// Measure of prediction accuracy of a forecasting method in statistics. It compares the forecast
174/// direction (upward or downward) to the actual realized direction.
175///
176/// ```math
177/// MDA = \frac{1}{N}\sum_t \mathbf{1}_{\sgn(A_t - A_{t-1}) = \sgn(F_t - A_{t-1})}
178/// ```
179///
180/// ## Sources
181///
182/// [[1]](https://en.wikipedia.org/wiki/Mean_directional_accuracy)
183///
184/// # Examples
185///
186/// ```
187/// use traquer::statistic::regression;
188///
189/// regression::mda(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
190/// ```
191pub fn mda<'a, T: ToPrimitive>(data: &'a [T], estimate: &'a [T]) -> impl Iterator<Item = f64> + 'a {
192 iter::once(f64::NAN).chain(data[1..].iter().enumerate().zip(&estimate[1..]).scan(
193 (0.0, data[0].to_f64().unwrap()),
194 |state, ((cnt, observe), est)| {
195 let dir = ((observe.to_f64().unwrap() - state.1).signum()
196 == (est.to_f64().unwrap() - state.1).signum()) as u8 as f64;
197 *state = (state.0 + dir, observe.to_f64().unwrap());
198 Some(state.0 / (cnt + 1) as f64)
199 },
200 ))
201}
202
203/// Mean Absolute Scaled Error
204///
205/// A forecasting accuracy metric that compares the mean absolute error (MAE) of your prediction
206/// to the MAE of a naive forecasting method, such as predicting the previous period's value. It
207/// helps determine if your forecasting model outperforms a simple baseline model.
208///
209/// ```math
210/// MASE = \mathrm{mean}\left( \frac{\left| e_j \right|}{\frac{1}{T-1}\sum_{t=2}^T \left| Y_t-Y_{t-1}\right|} \right) = \frac{\frac{1}{J}\sum_{j}\left| e_j \right|}{\frac{1}{T-1}\sum_{t=2}^T \left| Y_t-Y_{t-1}\right|}
211/// ```
212///
213/// ## Sources
214///
215/// [[1]](https://en.wikipedia.org/wiki/Mean_absolute_scaled_error)
216/// [[2]](https://otexts.com/fpp2/accuracy.html#scaled-errors)
217///
218/// # Examples
219///
220/// ```
221/// use traquer::statistic::regression;
222///
223/// regression::mase(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
224/// ```
225pub fn mase<'a, T: ToPrimitive>(
226 data: &'a [T],
227 estimate: &'a [T],
228) -> impl Iterator<Item = f64> + 'a {
229 let mae_est = mae(data, estimate);
230 let mae_naive = data.windows(2).zip(1..).scan(0.0, |state, (w, cnt)| {
231 *state += (w[1].to_f64().unwrap() - w[0].to_f64().unwrap()).abs();
232 Some(*state / cnt as f64)
233 });
234
235 iter::once(f64::NAN).chain(
236 mae_est
237 .skip(1)
238 .zip(mae_naive)
239 .map(|(est, naive)| est / naive),
240 )
241}
242
243/// Envelope-weighted Mean Absolute Error
244///
245/// A scale-independent, symmetric, error metric
246///
247/// ## Sources
248///
249/// [[1]](https://typethepipe.com/post/energy-forecasting-error-metrics/)
250/// [[2]](https://pdfs.semanticscholar.org/cf04/65bce25d78ccda6d8c5d12e141099aa606f4.pdf)
251///
252/// # Examples
253///
254/// ```
255/// use traquer::statistic::regression;
256///
257/// regression::emae(&[1.0,2.0,3.0,4.0,5.0], &[1.0,2.0,3.0,4.0,5.0]).collect::<Vec<f64>>();
258/// ```
259pub fn emae<'a, T: ToPrimitive>(
260 data: &'a [T],
261 estimate: &'a [T],
262) -> impl Iterator<Item = f64> + 'a {
263 izip!(data, estimate, 1..).scan((0.0, 0.0), |state, (actual, est, n)| {
264 let actual = actual.to_f64().unwrap();
265 let est = est.to_f64().unwrap();
266 *state = (state.0 + (actual - est).abs(), state.1 + actual.max(est));
267 Some(state.0 / state.1 * 100. / n as f64)
268 })
269}