1use chrono::prelude::*;
2use rayon::prelude::*;
3use std::cmp::PartialOrd;
4use std::fs::File;
5use std::io::{BufRead, BufReader};
6use std::path::Path;
7use std::{error::Error, fmt};
8use nalgebra::{DVector, DMatrix};
9use plotly::{Plot, Scatter};
10
11
12pub fn suitable_xfmt(d: chrono::Duration) -> &'static str {
16 let xfmt = if d > chrono::Duration::weeks(1) {
17 "%y-%m-%d"
18 } else if d > chrono::Duration::days(1) {
19 "%m-%d %H"
20 } else {
21 "%d %H:%M"
22 };
23 return xfmt;
24}
25
26
27
28pub fn read_bad_datetimes<P>(fin: P) -> Vec<DateTime<FixedOffset>>
30where
31 P: AsRef<Path>,
32{
33 let file = File::open(fin).unwrap();
34 let buf = BufReader::new(file);
35 let mut bad_datetimes: Vec<DateTime<FixedOffset>> = Vec::new();
36 for l in buf.lines() {
37 let l_unwrap = match l {
38 Ok(l_ok) => l_ok,
39 Err(l_err) => {
40 println!("Err, could not read/unwrap line {}", l_err);
41 continue;
42 }
43 };
44 bad_datetimes.push(DateTime::parse_from_rfc3339(&l_unwrap).unwrap());
45 }
46 return bad_datetimes;
47}
48
49pub fn min_and_max<'a, I, T>(mut s: I) -> (T, T)
50where
51 I: Iterator<Item = &'a T>,
52 T: 'a + std::cmp::PartialOrd + Clone,
53{
54 let (mut min, mut max) = match s.next() {
55 Some(v) => (v, v),
56 None => panic!("could not iterate over slice"),
57 };
58 for es in s {
59 if es > max {
60 max = es
61 } else if es < min {
62 min = es
63 }
64 }
65 return (min.clone(), max.clone());
66}
67
68pub fn make_window(w_central: f64, w_side: f64, side: usize) -> Vec<f64> {
69 let w_step = (w_central - w_side) / (side as f64);
70 let up = (0..side + 1).map(|n| w_side + (n as f64 * w_step));
71 let down = up.clone().rev().skip(1);
72 let updown = up.chain(down).collect();
73 updown
74}
75
76pub fn mavg(v: &[f64], w: &[f64], max_missing_v: usize, max_missing_wpct: f64) -> Vec<f64> {
83 let len_v: i32 = v.len() as i32;
84 let len_w: i32 = w.len() as i32;
85 assert!(
86 len_w < len_v,
87 "length of moving average window > length of vector"
88 );
89 assert!(
90 len_w % 2 == 1,
91 "the moving average window has an even number of elements; \
92 it should be odd to have a central element"
93 );
94 let side: i32 = (len_w - 1) / 2;
95 let sum_all_w: f64 = w.iter().sum();
96 let max_missing_w: f64 = sum_all_w / 100. * max_missing_wpct;
97 let mut vout: Vec<f64> = Vec::with_capacity(len_v as usize);
98 for i in 0..len_v {
99 let mut missing_v = 0;
100 let mut missing_w = 0.;
101 let mut sum_ve_we = 0.;
102 let mut sum_we = 0.;
103 let mut ve: f64;
104 let vl = i - side;
105 let vr = i + side + 1;
106 for (j, we) in (vl..vr).zip(w.iter()) {
107 if (j < 0) || (j >= len_v) {
108 missing_v += 1;
109 missing_w += we;
110 } else {
111 ve = v[j as usize];
112 if ve.is_nan() {
113 missing_v += 1;
114 missing_w += we;
115 } else {
116 sum_ve_we += ve * we;
117 sum_we += we;
118 }
119 }
120 if (missing_v > max_missing_v) || (missing_w > max_missing_w) {
121 sum_ve_we = f64::NAN;
122 break;
127 }
128 }
129 vout.push(sum_ve_we / sum_we);
130 }
131 vout
132}
133
134pub fn mavg_parallel_simd(v: &[f64], w: &[f64]) -> Vec<f64> {
141 let len_v: usize = v.len();
142 let len_w: usize = w.len();
143 assert!(
144 len_w < len_v,
145 "length of moving average window > length of vector"
146 );
147 assert!(
148 len_w % 2 == 1,
149 "the moving average window has an even number of elements; \
150 it should be odd to have a central element"
151 );
152 let sum_all_w: f64 = w.iter().sum();
153 let side: usize = (len_w - 1) / 2;
154 let mut vout: Vec<f64> = vec![f64::NAN; len_v];
155 v.par_windows(len_w as usize)
156 .zip(vout[side as usize..].par_iter_mut())
157 .for_each(|(window, vout_e)| {
158 let product: Vec<f64> = window
159 .iter()
160 .zip(w)
161 .map(|(win_e, wt_e)| win_e * wt_e)
162 .collect();
163 let sum: f64 = product.iter().sum();
164 *vout_e = sum / sum_all_w;
165 });
166 vout
167}
168
169pub fn mavg_parallel_fold(v: &[f64], w: &[f64]) -> Vec<f64> {
175 let len_v: usize = v.len();
176 let len_w: usize = w.len();
177 assert!(
178 len_w < len_v,
179 "length of moving average window > length of vector"
180 );
181 assert!(
182 len_w % 2 == 1,
183 "the moving average window has an even number of elements; \
184 it should be odd to have a central element"
185 );
186 let sum_all_w: f64 = w.iter().sum();
187 let side: usize = (len_w - 1) / 2;
188 let mut vout: Vec<f64> = vec![f64::NAN; len_v];
189 v.par_windows(len_w as usize)
190 .zip(vout[side as usize..].par_iter_mut())
191 .for_each(|(window, vout_e)| {
192 *vout_e = window
193 .iter()
194 .zip(w)
195 .map(|(win_e, wt_e)| win_e * wt_e)
196 .fold(0., |acc, x| acc + x)
197 / sum_all_w;
198 });
199 vout
200}
201
202
203pub fn awat_regression_plot(data: DVector<f64>, model:DVector<f64>) {
204 let l = data.column(0).len();
205 let base: Vec<i32> = (0..l).map(|e| e as i32).collect();
206 let mut plot = Plot::new();
207 let trace_data = Scatter::new(base.clone(), data.data.as_vec().to_vec()).name("data");
208 plot.add_trace(trace_data);
209 let trace_model = Scatter::new(base.clone(), model.data.as_vec().to_vec()).name("fit");
210 plot.add_trace(trace_model);
211 plot.show();
212}
213
214
215pub fn awat_regression(v: &[f64], len_w: usize) -> (u8, f64) {
223 let k_max = 6; let plot = false;
225
226 let len_v: usize = v.len();
227 assert!(
228 len_w < len_v,
229 "length of moving average window > length of vector"
230 );
231 assert!(
232 len_w % 2 == 1,
233 "the moving average window has an even number of elements; \
234 it should be odd to have a central element"
235 );
236
237 let base_lower: i32 = - (len_w as i32 - 1) / 2;
238 let base_upper: i32 = (len_w as i32 + 1) / 2;
239 let base: Vec<i32> = (base_lower..base_upper).collect();
240
241 let mut g_all = DMatrix::from_element(len_w, k_max + 1, 1.);
243 for i in 1..7 {
244 let dv = DVector::from_iterator(len_w, base.clone().iter().map(|e| e.pow(i) as f64));
245 g_all.set_column(i as usize, &dv);
246 }
247 let d = DVector::from_column_slice(&v[0..len_w]);
251 let ssd = d.dot(&d);
252
253 let mut solutions = DMatrix::zeros(k_max + 1, k_max);
254 let mut models: DMatrix<f64> = DMatrix::zeros(len_w, k_max);
255 let mut ssrs: Vec<f64> = Vec::with_capacity(6);
256 let mut aics: Vec<f64> = Vec::with_capacity(6);
257
258 for k in 1..(k_max + 1) {
259
260 let g = g_all.columns(0, k + 1);
261
262 let s: DVector<f64> = (g.transpose() * &g).try_inverse().unwrap() * g.transpose() * d.clone();
263
264 for (i, &e) in s.iter().enumerate() {
265 solutions[(i, k - 1)] = e
266 }
267
268 let sol: DVector<f64> = solutions.column(k - 1).into();
269
270 let model = &g_all * sol;
271 models.set_column(k - 1, &model);
272
273 let res = &model - &d;
274 let ssr = res.dot(&res);
275 ssrs.push(ssr);
276
277 let n = (k + 1) as f64;
279 let r = len_w as f64;
280 let aic = r * f64::ln(ssr / r) + (2. * n) + ((2. * n * (n + 1.)) / (r - n - 1.));
281 aics.push(aic);
282
283 }
284
285 let mut min_index = 0;
287 let mut min_value = aics[0];
288 for (i, &e) in aics.iter().enumerate() {
289 if e < min_value {
290 min_value = e;
291 min_index = i;
292 }
293 }
294 let mod_best: DVector<f64> = models.column(min_index).into();
296 let ssr_best = ssrs[min_index];
297 let k_best = (min_index + 1) as u8;
298
299 if plot {
300 awat_regression_plot(d, mod_best)
301 }
302
303 let b = ssr_best / ssd;
304
305 (k_best, b)
306}
307
308
309pub fn find_anomalies(
322 v: &[f64],
323 window_width: usize,
324 min_window_data: usize,
325 max_iqr: f64,
326) -> (Vec<usize>, Vec<f64>) {
327 pub const MIN_DATA_IQR: usize = 6usize;
329 if min_window_data < MIN_DATA_IQR {
330 panic!(
331 "find_anomalies: more than {} data are required for the IQR calculation",
332 MIN_DATA_IQR
333 );
334 }
335 if min_window_data > window_width {
336 panic!("find_anomalies: impossible to proceed as window_width < min_window_data");
337 }
338 let mut anomalies_index: Vec<usize> = Vec::new();
339 let indices: Vec<usize> = (0..v.len()).collect();
340 for (wl, wi) in v.windows(window_width).zip(indices.windows(window_width)) {
341 let (_ql, _qu, iqr) = match calculate_iqr(wl, min_window_data) {
342 Ok(res) => res,
343 Err(_e) => {
344 continue;
345 }
346 };
347 if iqr > max_iqr {
348 anomalies_index.append(&mut wi.to_owned());
349 }
350 }
351 anomalies_index.sort_by(|a, b| a.partial_cmp(b).unwrap());
355 let (anomalies_index_dedup, _) = anomalies_index.partition_dedup_by(|a, b| a == b);
356 let anomalies_index_dedup = anomalies_index_dedup.to_vec();
357 let mut anomalies_load: Vec<f64> = Vec::new();
358 for i in anomalies_index_dedup.iter() {
359 anomalies_load.push(v[*i]);
360 }
361 return (anomalies_index_dedup, anomalies_load);
362}
363
364pub fn calculate_iqr(s: &[f64], min_len: usize) -> Result<(f64, f64, f64), LenErr> {
374 let mut v: Vec<f64> = s.iter().filter(|n| n.is_finite()).map(|n| *n).collect();
375 let v_len = v.len();
376
377 if v_len < min_len {
378 let err = LenErr {
379 min_len: Some(min_len),
380 got_len: v_len,
381 max_len: None,
382 };
383 return Err(err);
384 }
385 v.sort_by(|a, b| a.partial_cmp(&b).unwrap());
386 let hl = (v_len as f64 - 1.) * 0.25;
387 let hu = (v_len as f64 - 1.) * 0.75;
388 let hl_int = hl.floor() as usize;
389 let hl_fract = hl.fract();
390 let hu_int = hu.floor() as usize;
391 let hu_fract = hu.fract();
392 let ql_int = v[hl_int];
393 let qu_int = v[hu_int];
394 let ql_fract = (v[hl_int + 1usize] - v[hl_int]) * hl_fract;
395 let qu_fract = (v[hu_int + 1usize] - v[hu_int]) * hu_fract;
396 let ql = ql_int + ql_fract;
397 let qu = qu_int + qu_fract;
398 let iqr = qu - ql;
399 return Ok((ql, qu, iqr));
400}
401
402pub fn mean_or_nan(v: &Vec<f64>) -> f64 {
403
404 let mut contains_nan = false;
405 v.iter().for_each(|f| {
406 if f.is_nan() {
407 contains_nan = true
408 }
409 });
410
411 let mean = if contains_nan {
412 f64::NAN
413 } else if v.len() == 0usize {
414 f64::NAN
415 } else {
416 v.iter().sum::<f64>() / v.len() as f64
417 };
418
419 mean
420}
421
422
423pub fn compare_f64_exact(a: f64, b: f64) -> bool {
424 (a.is_nan() && b.is_nan()) || (a == b)
425}
426
427pub fn compare_vecf64_exact(va: &[f64], vb: &[f64]) -> bool {
428 (va.len() == vb.len()) && va.iter().zip(vb).all(|(a, b)| compare_f64_exact(*a, *b))
429}
430
431pub fn compare_f64_approx(a: f64, b: f64, max: f64) -> bool {
432 (a.is_nan() && b.is_nan()) || ((a - b).abs() < max)
433}
434
435pub fn compare_vecf64_approx(va: &[f64], vb: &[f64]) -> bool {
436 (va.len() == vb.len())
437 && va
438 .iter()
439 .zip(vb)
440 .all(|(a, b)| compare_f64_approx(*a, *b, 0.1f64))
441}
442
443pub fn discharge_by_index<T: Copy>(ve: &[T], vi: &[usize]) -> Vec<T> {
450 let mut vout: Vec<T> = Vec::with_capacity(ve.len());
451 let mut vi = vi.to_vec();
452 vi.sort();
453 assert!(vi[vi.len() - 1usize] < ve.len());
454 let mut vi_iter = vi.iter();
455 let mut ve_iter = ve.iter().enumerate();
456 while let Some(i) = vi_iter.next() {
457 loop {
458 let (vei, vee) = ve_iter.next().unwrap();
459 if vei < *i {
460 vout.push(*vee);
461 } else if vei == *i {
462 break;
463 } else {
464 panic!("indices error: {} > {}", vei, i);
465 }
466 }
467 }
468 while let Some((_, vee)) = ve_iter.next() {
469 vout.push(*vee);
470 }
471 return vout;
472}
473
474pub fn setnan_by_index(ve: &mut [f64], vi: &[usize]) {
481 if vi.len() == 0 {
482 return;
483 }
484 let mut vi = vi.to_vec();
485 vi.sort();
486 assert!(vi[vi.len() - 1usize] < ve.len());
487 let mut vi_iter = vi.iter();
488 let mut ve_iter = ve.iter_mut().enumerate();
489 while let Some(i) = vi_iter.next() {
490 loop {
491 let (vei, vee) = ve_iter.next().unwrap();
492 if vei < *i {
493 continue;
494 } else if vei == *i {
495 *vee = f64::NAN;
496 break;
497 } else {
498 panic!("indices error: {} > {}", vei, i);
499 }
500 }
501 }
502}
503
504#[derive(Debug)]
506pub struct EmptyTimeLoad();
507impl fmt::Display for EmptyTimeLoad {
508 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509 write!(f, "Found an empty TimeLoad")
510 }
511}
512impl Error for EmptyTimeLoad {}
513
514#[derive(Debug)]
517pub struct LenErr {
518 pub min_len: Option<usize>,
519 pub got_len: usize,
520 pub max_len: Option<usize>,
521}
522impl Error for LenErr {}
523impl fmt::Display for LenErr {
524 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525 write!(
526 f,
527 "Invalid length, got {}, required is >= {:?} and <= {:?}",
528 self.got_len, self.min_len, self.max_len
529 )
530 }
531}