1use num_traits::Float;
5use rand::SeedableRng;
6use rand::prelude::SliceRandom;
7use rand::rngs::StdRng;
8use rustc_hash::FxHashSet;
9use std::fmt::Debug;
10use std::hash::Hash;
11use std::ops::AddAssign;
12
13use crate::core::math::vector_helpers::*;
14use crate::prelude::*;
15
16pub use bixverse_sparse::thread_chunks;
21
22pub fn flatten_vector<I, T>(vec: I) -> Vec<T>
32where
33 I: IntoIterator,
34 I::Item: IntoIterator<Item = T>,
35{
36 vec.into_iter().flatten().collect()
37}
38
39pub fn array_max<T>(arr: &[T]) -> T
49where
50 T: PartialOrd + Copy,
51{
52 let mut max_val = arr[0];
53 for number in arr {
54 if *number > max_val {
55 max_val = *number
56 }
57 }
58 max_val
59}
60
61pub fn array_min<T>(arr: &[T]) -> T
71where
72 T: PartialOrd + Copy,
73{
74 let mut min_val = arr[0];
75 for number in arr {
76 if *number < min_val {
77 min_val = *number
78 }
79 }
80 min_val
81}
82
83pub fn array_max_min<T>(arr: &[T]) -> (T, T)
93where
94 T: PartialOrd + Copy,
95{
96 let mut min_val = arr[0];
97 let mut max_val = arr[0];
98 for number in arr {
99 if *number < min_val {
100 min_val = *number
101 }
102 if *number > max_val {
103 max_val = *number
104 }
105 }
106
107 (max_val, min_val)
108}
109
110pub fn unique<T>(vec: &[T]) -> Vec<T>
120where
121 T: Copy + Eq + Hash + Debug,
122{
123 let mut set = FxHashSet::default();
124 vec.iter()
125 .filter(|&&item| set.insert(item))
126 .cloned()
127 .collect()
128}
129
130pub fn cumsum<T>(x: &[T]) -> Vec<T>
140where
141 T: Float + AddAssign<T>,
142{
143 let mut sum = T::zero();
144 x.iter()
145 .map(|&val| {
146 sum += val;
147 sum
148 })
149 .collect()
150}
151
152pub fn split_vector_randomly<T>(vec: &[T], x: usize, seed: u64) -> (Vec<T>, Vec<T>)
166where
167 T: Clone,
168{
169 let mut rng = StdRng::seed_from_u64(seed);
170 let mut shuffled = vec.to_vec();
171 shuffled.shuffle(&mut rng);
172
173 let (first_set, second_set) = shuffled.split_at(x);
174
175 (first_set.to_vec(), second_set.to_vec())
176}
177
178pub fn min_max_scale<T>(vec: &[T]) -> Vec<T>
192where
193 T: Float,
194{
195 if vec.is_empty() {
196 return Vec::new();
197 }
198 let (max_val, min_val) = array_max_min(vec);
199 let range = max_val - min_val;
200 if range == T::zero() {
201 return vec![T::zero(); vec.len()];
202 }
203 vec.iter().map(|&x| (x - min_val) / range).collect()
204}
205
206pub fn rank_normalise<T>(vec: &[T]) -> Vec<T>
218where
219 T: Float,
220{
221 if vec.is_empty() {
222 return Vec::new();
223 }
224 let n = T::from(vec.len()).unwrap();
225 rank_vector(vec).into_iter().map(|r| r / n).collect()
226}
227
228pub fn zscore_normalise<T>(vec: &[T]) -> Vec<T>
238where
239 T: Float,
240{
241 if vec.is_empty() {
242 return Vec::new();
243 }
244 let n = T::from(vec.len()).unwrap();
245 let mean = vec.iter().copied().fold(T::zero(), |acc, x| acc + x) / n;
246 let variance = vec.iter().copied().fold(T::zero(), |acc, x| {
247 let d = x - mean;
248 acc + d * d
249 }) / n;
250 let std_dev = variance.sqrt();
251 if std_dev == T::zero() {
252 return vec![T::zero(); vec.len()];
253 }
254 vec.iter().map(|&x| (x - mean) / std_dev).collect()
255}
256
257pub fn robust_scale<T>(vec: &[T]) -> Option<Vec<T>>
271where
272 T: BixverseFloat,
273{
274 if vec.is_empty() {
275 return None;
276 }
277 let median_val = median(vec)?;
278 let mad_val = mad(vec, Some(T::from_f64(MAD_SCALE).unwrap()))?;
279 if mad_val == T::zero() {
280 return Some(vec![T::zero(); vec.len()]);
281 }
282 Some(vec.iter().map(|&x| (x - median_val) / mad_val).collect())
283}
284
285#[cfg(test)]
290mod tests {
291 use super::*;
292
293 const EPS: f64 = 1e-10;
294
295 fn approx_eq(a: f64, b: f64) -> bool {
296 (a - b).abs() < EPS
297 }
298
299 fn vec_approx_eq(a: &[f64], b: &[f64]) -> bool {
300 a.len() == b.len() && a.iter().zip(b).all(|(&x, &y)| approx_eq(x, y))
301 }
302
303 #[test]
305 fn test_flatten_vector() {
306 let nested = vec![vec![1, 2], vec![3], vec![4, 5]];
307 assert_eq!(flatten_vector(nested), vec![1, 2, 3, 4, 5]);
308
309 let with_empties: Vec<Vec<i32>> = vec![vec![], vec![1], vec![]];
310 assert_eq!(flatten_vector(with_empties), vec![1]);
311 }
312
313 #[test]
315 fn test_array_max() {
316 assert_eq!(array_max(&[3, 1, 4, 1, 5, 9, 2, 6]), 9);
317 assert_eq!(array_max(&[42]), 42);
318 }
319
320 #[test]
322 fn test_array_min() {
323 assert_eq!(array_min(&[3, 1, 4, 1, 5, 9, 2, 6]), 1);
324 assert_eq!(array_min(&[42]), 42);
325 }
326
327 #[test]
329 fn test_array_max_min() {
330 let (max, min) = array_max_min(&[3.0_f64, 1.0, 4.0, 1.0, 5.0, 9.0]);
331 assert!(approx_eq(max, 9.0));
332 assert!(approx_eq(min, 1.0));
333
334 let (max, min) = array_max_min(&[7]);
335 assert_eq!(max, 7);
336 assert_eq!(min, 7);
337 }
338
339 #[test]
341 fn test_unique() {
342 let mut result = unique(&[1, 2, 2, 3, 3, 3]);
343 result.sort();
344 assert_eq!(result, vec![1, 2, 3]);
345
346 let mut result = unique(&[4, 2, 1]);
347 result.sort();
348 assert_eq!(result, vec![1, 2, 4]);
349 }
350
351 #[test]
353 fn test_cumsum() {
354 let result = cumsum(&[1.0_f64, 2.0, 3.0, 4.0]);
355 assert!(vec_approx_eq(&result, &[1.0, 3.0, 6.0, 10.0]));
356
357 let result = cumsum(&[5.0_f64]);
358 assert!(vec_approx_eq(&result, &[5.0]));
359 }
360
361 #[test]
363 fn test_split_vector_randomly() {
364 let vec: Vec<i32> = (0..10).collect();
365
366 let (a, b) = split_vector_randomly(&vec, 4, 42);
367 assert_eq!(a.len(), 4);
368 assert_eq!(b.len(), 6);
369
370 let (mut a, mut b) = split_vector_randomly(&vec, 4, 42);
372 a.append(&mut b);
373 a.sort();
374 assert_eq!(a, vec);
375
376 let (a1, _) = split_vector_randomly(&vec, 4, 99);
377 let (a2, _) = split_vector_randomly(&vec, 4, 99);
378 assert_eq!(a1, a2);
379 }
380
381 #[test]
383 fn test_min_max_scale() {
384 let result = min_max_scale(&[0.0_f64, 1.0, 2.0, 3.0, 4.0]);
385 assert!(vec_approx_eq(&result, &[0.0, 0.25, 0.5, 0.75, 1.0]));
386
387 let result = min_max_scale(&[3.0_f64, 3.0, 3.0]);
389 assert!(vec_approx_eq(&result, &[0.0, 0.0, 0.0]));
390 }
391
392 #[test]
394 fn test_rank_normalise() {
395 let result = rank_normalise(&[10.0_f64, 30.0, 20.0]);
397 assert!(vec_approx_eq(&result, &[1.0 / 3.0, 1.0, 2.0 / 3.0]));
398
399 let result = rank_normalise(&[1.0_f64, 1.0, 3.0]);
401 assert!(vec_approx_eq(&result, &[0.5, 0.5, 1.0]));
402 }
403
404 #[test]
406 fn test_zscore_normalise() {
407 let result = zscore_normalise(&[2.0_f64, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]);
408 let mean: f64 = result.iter().sum::<f64>() / result.len() as f64;
409 let variance: f64 =
410 result.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / result.len() as f64;
411 assert!(approx_eq(mean, 0.0));
412 assert!(approx_eq(variance, 1.0));
413
414 let result = zscore_normalise(&[5.0_f64, 5.0, 5.0]);
416 assert!(vec_approx_eq(&result, &[0.0, 0.0, 0.0]));
417 }
418
419 #[test]
421 fn test_robust_scale() {
422 let result = robust_scale(&[1.0_f64, 3.0, 2.0, 5.0, 4.0]).unwrap();
425 assert!(vec_approx_eq(
426 &result,
427 &[
428 -1.3489795003921634,
429 0.0,
430 -0.6744897501960817,
431 1.3489795003921634,
432 0.6744897501960817
433 ]
434 ));
435
436 let result = robust_scale(&[4.0_f64, 4.0, 4.0]).unwrap();
437 assert!(vec_approx_eq(&result, &[0.0, 0.0, 0.0]));
438
439 assert!(robust_scale::<f64>(&[]).is_none());
440 }
441}