]>
Commit | Line | Data |
---|---|---|
ba974798 DC |
1 | //! Helpers for common statistics tasks |
2 | use num_traits::NumAssignRef; | |
3 | use num_traits::cast::ToPrimitive; | |
4 | ||
5 | /// Calculates the sum of a list of numbers | |
6 | /// ``` | |
7 | /// # use proxmox_backup::tools::statistics::sum; | |
8 | /// # use num_traits::cast::ToPrimitive; | |
9 | /// | |
10 | /// assert_eq!(sum(&[0,1,2,3,4,5]), 15); | |
11 | /// assert_eq!(sum(&[-1,1,-2,2]), 0); | |
12 | /// assert!((sum(&[0.0, 0.1,0.2]).to_f64().unwrap() - 0.3).abs() < 0.001); | |
13 | /// assert!((sum(&[0.0, -0.1,0.2]).to_f64().unwrap() - 0.1).abs() < 0.001); | |
14 | /// ``` | |
15 | pub fn sum<T>(list: &[T]) -> T | |
16 | where | |
17 | T: NumAssignRef + ToPrimitive | |
18 | { | |
19 | let mut sum = T::zero(); | |
20 | for num in list { | |
21 | sum += num; | |
22 | } | |
23 | sum | |
24 | } | |
25 | ||
26 | /// Calculates the mean of a variable x | |
27 | /// ``` | |
28 | /// # use proxmox_backup::tools::statistics::mean; | |
29 | /// | |
30 | /// assert!((mean(&[0,1,2,3,4,5]).unwrap() - 2.5).abs() < 0.001); | |
31 | /// assert_eq!(mean::<u64>(&[]), None) | |
32 | /// ``` | |
33 | pub fn mean<T>(list: &[T]) -> Option<f64> | |
34 | where | |
35 | T: NumAssignRef + ToPrimitive | |
36 | { | |
37 | let len = list.len(); | |
38 | if len == 0 { | |
39 | return None | |
40 | } | |
41 | Some(sum(list).to_f64()?/(list.len() as f64)) | |
42 | } | |
43 | ||
44 | /// Calculates the variance of a variable x | |
45 | /// ``` | |
46 | /// # use proxmox_backup::tools::statistics::variance; | |
47 | /// | |
48 | /// assert!((variance(&[1,2,3,4]).unwrap() - 1.25).abs() < 0.001); | |
49 | /// assert_eq!(variance::<u64>(&[]), None) | |
50 | /// ``` | |
51 | pub fn variance<T>(list: &[T]) -> Option<f64> | |
52 | where | |
53 | T: NumAssignRef + ToPrimitive | |
54 | { | |
55 | covariance(list, list) | |
56 | } | |
57 | ||
58 | /// Calculates the (non-corrected) covariance of two variables x,y | |
59 | pub fn covariance<X, Y> (x: &[X], y: &[Y]) -> Option<f64> | |
60 | where | |
61 | X: NumAssignRef + ToPrimitive, | |
62 | Y: NumAssignRef + ToPrimitive, | |
63 | { | |
64 | let len_x = x.len(); | |
65 | let len_y = y.len(); | |
66 | if len_x == 0 || len_y == 0 || len_x != len_y { | |
67 | return None | |
68 | } | |
69 | ||
70 | let mean_x = mean(x)?; | |
71 | let mean_y = mean(y)?; | |
72 | ||
cdde66d2 | 73 | let covariance: f64 = (0..len_x).map(|i| { |
ba974798 DC |
74 | let x = x[i].to_f64().unwrap_or(0.0); |
75 | let y = y[i].to_f64().unwrap_or(0.0); | |
76 | (x - mean_x)*(y - mean_y) | |
cdde66d2 | 77 | }).sum(); |
ba974798 DC |
78 | |
79 | Some(covariance/(len_x as f64)) | |
80 | } | |
81 | ||
82 | /// Returns the factors (a,b) of a linear regression y = a + bx | |
83 | /// for the variables [x,y] or None if the lists are not the same length | |
84 | /// ``` | |
85 | /// # use proxmox_backup::tools::statistics::linear_regression; | |
86 | /// | |
87 | /// let x = &[0,1,2,3,4]; | |
88 | /// let y = &[-4,-2,0,2,4]; | |
89 | /// let (a,b) = linear_regression(x,y).unwrap(); | |
90 | /// assert!((a - -4.0).abs() < 0.001); | |
91 | /// assert!((b - 2.0).abs() < 0.001); | |
92 | /// ``` | |
93 | pub fn linear_regression<X, Y> (x: &[X], y: &[Y]) -> Option<(f64, f64)> | |
94 | where | |
95 | X: NumAssignRef + ToPrimitive, | |
96 | Y: NumAssignRef + ToPrimitive | |
97 | { | |
98 | let len_x = x.len(); | |
99 | let len_y = y.len(); | |
100 | if len_x == 0 || len_y == 0 || len_x != len_y { | |
101 | return None | |
102 | } | |
103 | ||
104 | let mean_x = mean(x)?; | |
105 | let mean_y = mean(y)?; | |
106 | ||
107 | let mut covariance = 0.0; | |
108 | let mut variance = 0.0; | |
109 | ||
110 | for i in 0..len_x { | |
111 | let x = x[i].to_f64()?; | |
112 | let y = y[i].to_f64()?; | |
113 | ||
114 | let x_mean_x = x - mean_x; | |
115 | ||
116 | covariance += x_mean_x*(y - mean_y); | |
117 | variance += x_mean_x * x_mean_x; | |
118 | } | |
119 | ||
120 | let beta = covariance/variance; | |
121 | let alpha = mean_y - beta*mean_x; | |
122 | Some((alpha,beta)) | |
123 | } |