├── .gitignore ├── .travis.yml ├── AUTHORS ├── Cargo.toml ├── README.md ├── LICENSE └── src ├── lib.rs ├── univariate_.rs └── stats_.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | script: 3 | - cargo build --verbose 4 | - cargo test --verbose 5 | - cargo doc 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Initial author: 2 | 3 | Jeff Belgum 4 | 5 | With help and contributions from: 6 | 7 | Benjamin Thompson 8 | Justin Uy 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "statistical" 3 | description = "A simple statistics library" 4 | version = "1.0.0" 5 | authors = ["Jeff Belgum "] 6 | 7 | repository = "https://github.com/JeffBelgum/statistical" 8 | documentation = "https://jeffbelgum.github.io/statistical/statistical/" 9 | homepage = "https://github.com/JeffBelgum/statistical" 10 | 11 | license = "MIT" 12 | readme = "README.md" 13 | keywords = ["statistics", "statistical", "analysis", "math", "algorithm"] 14 | 15 | [dependencies] 16 | rand = "0.6" 17 | num = "0.2" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Statistical [![Build Status](https://travis-ci.org/JeffBelgum/statistical.svg?branch=master)](https://travis-ci.org/JeffBelgum/statistical) [![](http://meritbadge.herokuapp.com/statistical)](https://crates.io/crates/statistical) 2 | ------------ 3 | 4 | A simple statistics library written in Rust. It draws inspiration from the 5 | python stdlib statistics module. 6 | 7 | 8 | [Documentation](https://jeffbelgum.github.io/statistical/statistical/) 9 | 10 | Usage 11 | ----- 12 | 13 | Add this to your `Cargo.toml`: 14 | 15 | ```ini 16 | [dependencies] 17 | statistical = "1.0.0" 18 | ``` 19 | 20 | and this to your crate root: 21 | 22 | ```rust 23 | extern crate statistical; 24 | ``` 25 | 26 | Contributions 27 | ------------- 28 | 29 | Pull Requests and Issues welcome! 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeff Belgum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Jeff Belgum 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | // documentation files (the "Software"), to deal in the Software without restriction, including without 5 | // limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 6 | // the Software, and to permit persons to whom the Software is furnished to do so, subject to the following 7 | // conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions 10 | // of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 13 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 14 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 16 | // DEALINGS IN THE SOFTWARE. 17 | 18 | //! A simple statistics library 19 | //! 20 | //! Heavily inspired by the python standard library statistics module. 21 | 22 | extern crate rand; 23 | extern crate num; 24 | 25 | mod univariate_; 26 | mod stats_; 27 | 28 | pub mod univariate { 29 | pub use univariate_::{ 30 | harmonic_mean, 31 | geometric_mean, 32 | quadratic_mean, 33 | mode, 34 | average_deviation, 35 | pearson_skewness, 36 | skewness, 37 | pskewness, 38 | kurtosis, 39 | pkurtosis, 40 | standard_error_mean, 41 | standard_error_skewness, 42 | standard_error_kurtosis 43 | }; 44 | } 45 | 46 | pub use univariate::mode; 47 | pub use stats_::{ 48 | Degree, 49 | mean, 50 | median, 51 | variance, 52 | population_variance, 53 | standard_deviation, 54 | population_standard_deviation, 55 | standard_scores 56 | }; 57 | -------------------------------------------------------------------------------- /src/univariate_.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Jeff Belgum 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | // documentation files (the "Software"), to deal in the Software without restriction, including without 5 | // limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 6 | // the Software, and to permit persons to whom the Software is furnished to do so, subject to the following 7 | // conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions 10 | // of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 13 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 14 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 16 | // DEALINGS IN THE SOFTWARE. 17 | 18 | extern crate rand; 19 | extern crate num; 20 | 21 | use std::collections::HashMap; 22 | use std::hash::Hash; 23 | 24 | use num::{Float, 25 | One, 26 | PrimInt, 27 | Zero}; 28 | 29 | use super::stats_ as stats; 30 | 31 | pub fn harmonic_mean(v: &[T]) -> T 32 | where T: Float 33 | { 34 | let invert = |x: &T| T::one() / *x; 35 | let sum_of_inverted = v.iter().map(invert).fold(T::zero(), |acc, elem| acc + elem); 36 | num::cast::(v.len()).unwrap() / sum_of_inverted 37 | } 38 | 39 | pub fn geometric_mean(v: &[T]) -> T 40 | where T: Float 41 | { 42 | let product = v.iter().fold(T::one(), |acc, elem| acc * *elem); 43 | let one_over_len = T::one() / num::cast(v.len()).unwrap(); 44 | product.powf(one_over_len) 45 | } 46 | 47 | pub fn quadratic_mean(v: &[T]) -> T 48 | where T: Float 49 | { 50 | let square = |x: &T| (*x).powi(2); 51 | let sum_of_squared = v.iter().map(square).fold(T::zero(), |acc, elem| acc + elem); 52 | (sum_of_squared / num::cast(v.len()).unwrap()).sqrt() 53 | } 54 | 55 | pub fn mode(v: &[T]) -> Option 56 | where T: Hash + Copy + Eq 57 | { 58 | match v.len() { 59 | 0 => None, 60 | 1 => Some(v[0]), 61 | _ => { 62 | let mut counter = HashMap::new(); 63 | for x in v.iter() { 64 | let count = counter.entry(x).or_insert(0); 65 | *count += 1; 66 | } 67 | let mut max = -1; 68 | let mut mode = None; 69 | 70 | for (val, count) in counter.iter() { 71 | if *count > max { 72 | max = *count; 73 | mode = Some(**val); 74 | } 75 | } 76 | mode 77 | } 78 | } 79 | } 80 | 81 | pub fn average_deviation(v: &[T], mean: Option) -> T 82 | where T: Float 83 | { 84 | let mean = mean.unwrap_or_else(|| stats::mean(v)); 85 | let dev = v.iter().map(|&x| (x-mean).abs()).fold(T::zero(), |acc, elem| acc + elem); 86 | dev / num::cast(v.len()).unwrap() 87 | 88 | } 89 | 90 | pub fn pearson_skewness(mean: T, mode: T, stdev: T) -> T 91 | where T: Float 92 | { 93 | (mean - mode) / stdev 94 | } 95 | 96 | pub fn skewness(v: &[T], mean: Option, pstdev: Option) -> T 97 | where T: Float 98 | { 99 | let m = stats::std_moment(v, stats::Degree::Three, mean, pstdev); 100 | let n = num::cast(v.len()).unwrap(); 101 | let skew = m / n; 102 | let k = ( n * ( n - T::one())).sqrt()/( n - num::cast(2).unwrap()); 103 | skew * k 104 | } 105 | 106 | pub fn pskewness(v: &[T], mean: Option, pstdev: Option) -> T 107 | where T: Float 108 | { 109 | let m = stats::std_moment(v, stats::Degree::Three, mean, pstdev); 110 | m / num::cast(v.len()).unwrap() 111 | } 112 | 113 | pub fn kurtosis(v: &[T], mean: Option, pstdev: Option) -> T 114 | where T: Float 115 | { 116 | let two = num::cast::(2.0).unwrap(); 117 | let three = num::cast::(3.0).unwrap(); 118 | 119 | let m = stats::std_moment(v, stats::Degree::Four, mean, pstdev); 120 | let n = num::cast(v.len()).unwrap(); 121 | let q = (n - T::one())/((n-two)*(n-three)); 122 | let gamma2 = m / n; 123 | let kurt = q * (( ( n + T::one() ) * gamma2) - ( (n-T::one()) * three )); 124 | kurt 125 | } 126 | 127 | pub fn pkurtosis(v: &[T], mean: Option, pstdev: Option) -> T 128 | where T: Float 129 | { 130 | let m = stats::std_moment(v, stats::Degree::Four, mean, pstdev); 131 | m / num::cast(v.len()).unwrap() - num::cast(3).unwrap() 132 | } 133 | 134 | pub fn standard_error_mean(stdev: T, sample_size: T, population_size: Option) -> T 135 | where T: Float 136 | { 137 | let mut err = stdev / sample_size.sqrt(); 138 | if let Some(p) = population_size { 139 | err = err * ((p - sample_size) / (p - T::one())).sqrt() 140 | } 141 | err 142 | 143 | } 144 | 145 | pub fn standard_error_skewness(sample_size: T) -> U 146 | where T: PrimInt, U: Float 147 | { 148 | (num::cast::(6.0).unwrap() / num::cast(sample_size).unwrap()).sqrt() 149 | } 150 | 151 | pub fn standard_error_kurtosis(sample_size: T) -> U 152 | where T: PrimInt, U: Float 153 | { 154 | (num::cast::(24.0).unwrap() / num::cast(sample_size).unwrap()).sqrt() 155 | } 156 | 157 | #[cfg(test)] 158 | mod test { 159 | use super::*; 160 | 161 | #[test] 162 | fn test_harmonic_mean() { 163 | let vec = vec![0.25, 0.5, 1.0, 1.0]; 164 | assert_eq!(harmonic_mean(&vec), 0.5); 165 | let vec = vec![0.5, 0.5, 0.5]; 166 | assert_eq!(harmonic_mean(&vec), 0.5); 167 | let vec = vec![1.0,2.0,4.0]; 168 | assert_eq!(harmonic_mean(&vec), 12.0/7.0); 169 | } 170 | #[test] 171 | fn test_geometric_mean() { 172 | let vec = vec![1.0, 2.0, 6.125, 12.25]; 173 | assert_eq!(geometric_mean(&vec), 3.5); 174 | } 175 | #[test] 176 | fn test_quadratic_mean() { 177 | let vec = vec![-3.0, -2.0, 0.0, 2.0, 3.0]; 178 | assert_eq!(quadratic_mean(&vec), 2.280350850198276); 179 | } 180 | #[test] 181 | fn test_mode() { 182 | let vec = vec![2,4,3,5,4,6,1,1,6,4,0,0]; 183 | assert_eq!(mode(&vec), Some(4)); 184 | let vec = vec![1]; 185 | assert_eq!(mode(&vec), Some(1)); 186 | } 187 | #[test] 188 | fn test_average_deviation() { 189 | let vec = vec![2.0, 2.25, 2.5, 2.5, 3.25]; 190 | assert_eq!(average_deviation(&vec, None), 0.3); 191 | assert_eq!(average_deviation(&vec, Some(2.75)), 0.45); 192 | } 193 | #[test] 194 | fn test_pearson_skewness() { 195 | assert_eq!(pearson_skewness(2.5, 2.25, 2.5), 0.1); 196 | assert_eq!(pearson_skewness(2.5, 5.75, 0.5), -6.5); 197 | } 198 | #[test] 199 | fn test_skewness() { 200 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 201 | assert_eq!(skewness(&vec, None, None), 1.7146101353987853); 202 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 203 | assert_eq!(skewness(&vec, Some(2.25), Some(1.0)), 1.4713288161532945); 204 | } 205 | #[test] 206 | fn test_pskewness() { 207 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 208 | assert_eq!(pskewness(&vec, None, None), 1.3747465025469285); 209 | } 210 | #[test] 211 | fn test_kurtosis() { 212 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 213 | assert_eq!(kurtosis(&vec, None, None), 3.036788927335642); 214 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 215 | assert_eq!(kurtosis(&vec, Some(2.25), Some(1.0)), 2.3064453125); 216 | } 217 | #[test] 218 | fn test_pkurtosis() { 219 | let vec = vec![1.25, 1.5, 1.5, 1.75, 1.75, 2.5, 2.75, 4.5]; 220 | assert_eq!(pkurtosis(&vec, None, None), 0.7794232987312579); 221 | } 222 | #[test] 223 | fn test_standard_error_mean() { 224 | assert_eq!(standard_error_mean(2.0, 16.0, None), 0.5); 225 | } 226 | #[test] 227 | fn test_standard_error_skewness() { 228 | assert_eq!(standard_error_skewness::(15), 0.63245553203); 229 | } 230 | #[test] 231 | fn test_standard_error_kurtosis() { 232 | assert_eq!(standard_error_kurtosis::(15), 1.2649110640); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/stats_.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Jeff Belgum 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | // documentation files (the "Software"), to deal in the Software without restriction, including without 5 | // limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 6 | // the Software, and to permit persons to whom the Software is furnished to do so, subject to the following 7 | // conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions 10 | // of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 13 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 14 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 16 | // DEALINGS IN THE SOFTWARE. 17 | 18 | extern crate rand; 19 | extern crate num; 20 | 21 | use num::{Float, 22 | Num, 23 | NumCast, 24 | One, 25 | Zero}; 26 | 27 | 28 | pub enum Degree { 29 | One, 30 | Two, 31 | Three, 32 | Four 33 | } 34 | 35 | pub fn std_moment(v: &[T], r: Degree, _mean: Option, pstdev: Option) -> T 36 | where T: Float 37 | { 38 | let _mean = _mean.unwrap_or_else(|| mean(v)); 39 | let pstdev = pstdev.unwrap_or_else(|| population_standard_deviation(v, Some(_mean))); 40 | let r = match r { 41 | Degree::One => 1, 42 | Degree::Two => 2, 43 | Degree::Three => 3, 44 | Degree::Four => 4 45 | }; 46 | v.iter().map(|&x| ((x-_mean)/pstdev).powi(r)).fold(T::zero(), |acc, elem| acc + elem) 47 | } 48 | 49 | /// The mean is the sum of a collection of numbers divided by the number of numbers in the collection. 50 | /// (reference)[http://en.wikipedia.org/wiki/Arithmetic_mean] 51 | pub fn mean(v: &[T]) -> T 52 | where T: Float 53 | { 54 | let len = num::cast(v.len()).unwrap(); 55 | v.iter().fold(T::zero(), |acc: T, elem| acc + *elem) / len 56 | } 57 | 58 | /// The median is the number separating the higher half of a data sample, a population, or 59 | /// a probability distribution, from the lower half (reference)[http://en.wikipedia.org/wiki/Median) 60 | pub fn median(v: &[T]) -> T 61 | where T: Copy + Num + NumCast + PartialOrd 62 | { 63 | assert!(v.len() > 0); 64 | let mut scratch: Vec<&T> = Vec::with_capacity(v.len()); 65 | scratch.extend(v.iter()); 66 | quicksort(&mut scratch); 67 | 68 | let mid = scratch.len() / 2; 69 | if scratch.len() % 2 == 1 { 70 | *scratch[mid] 71 | } else { 72 | (*scratch[mid] + *scratch[mid-1]) / num::cast(2).unwrap() 73 | } 74 | } 75 | 76 | pub fn sum_square_deviations(v: &[T], c: Option) -> T 77 | where T: Float 78 | { 79 | let c = match c { 80 | Some(c) => c, 81 | None => mean(v), 82 | }; 83 | 84 | let sum = v.iter().map( |x| (*x - c) * (*x - c) ).fold(T::zero(), |acc, elem| acc + elem); 85 | assert!(sum >= T::zero(), "negative sum of square root deviations"); 86 | sum 87 | } 88 | 89 | /// (Sample variance)[http://en.wikipedia.org/wiki/Variance#Sample_variance] 90 | pub fn variance(v: &[T], xbar: Option) -> T 91 | where T: Float 92 | { 93 | assert!(v.len() > 1, "variance requires at least two data points"); 94 | let len: T = num::cast(v.len()).unwrap(); 95 | let sum = sum_square_deviations(v, xbar); 96 | sum / (len - T::one()) 97 | } 98 | 99 | /// (Population variance)[http://en.wikipedia.org/wiki/Variance#Population_variance] 100 | pub fn population_variance(v: &[T], mu: Option) -> T 101 | where T: Float 102 | { 103 | assert!(v.len() > 0, "population variance requires at least one data point"); 104 | let len: T = num::cast(v.len()).unwrap(); 105 | let sum = sum_square_deviations(v, mu); 106 | sum / len 107 | } 108 | 109 | /// Standard deviation is a measure that is used to quantify the amount of variation or 110 | /// dispersion of a set of data values. (reference)[http://en.wikipedia.org/wiki/Standard_deviation] 111 | pub fn standard_deviation(v: &[T], xbar: Option) -> T 112 | where T: Float 113 | { 114 | let var = variance(v, xbar); 115 | var.sqrt() 116 | } 117 | 118 | /// Population standard deviation is a measure that is used to quantify the amount of variation or 119 | /// dispersion of a set of data values. (reference)[http://en.wikipedia.org/wiki/Standard_deviation] 120 | pub fn population_standard_deviation(v: &[T], mu: Option) -> T 121 | where T: Float 122 | { 123 | let pvar = population_variance(v, mu); 124 | pvar.sqrt() 125 | } 126 | 127 | 128 | /// Standard score is a given datum's (signed) number of standard deviations above the mean. 129 | /// (reference)[http://en.wikipedia.org/wiki/Standard_score] 130 | /// Method returns a vector of scores for a vector of inputs. scores[n] is the score of v[n] 131 | pub fn standard_scores(v: &[T]) -> Vec 132 | where T: Float 133 | { 134 | let mean = mean(&v); 135 | let standard_deviation = standard_deviation(&v, None); 136 | let scores: Vec = v.iter().map(|val| (*val - mean)/standard_deviation).collect(); 137 | return scores; 138 | } 139 | 140 | #[inline(always)] 141 | fn select_pivot(v: &mut [T]) 142 | where T: Copy 143 | { 144 | let idx = rand::random::() % v.len(); 145 | let tmp = v[0]; 146 | v[0] = v[idx]; 147 | v[idx] = tmp; 148 | } 149 | 150 | fn partition(v: &mut [T]) -> usize 151 | where T: PartialOrd + Copy 152 | { 153 | select_pivot(v); 154 | let pivot = v[0]; 155 | let mut i = 0; 156 | let mut j = 0; 157 | let end = v.len() - 1; 158 | while i < end { 159 | i += 1; 160 | if v[i] < pivot { 161 | v[j] = v[i]; 162 | j += 1; 163 | v[i] = v[j]; 164 | } 165 | 166 | } 167 | v[j] = pivot; 168 | j 169 | 170 | } 171 | 172 | pub fn quicksort(v: &mut [T]) 173 | where T: PartialOrd + Copy 174 | { 175 | if v.len() <= 1 { 176 | return 177 | } 178 | let pivot = partition(v); 179 | quicksort(&mut v[..pivot]); 180 | quicksort(&mut v[(pivot+1)..]); 181 | } 182 | 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | extern crate rand; 187 | extern crate num; 188 | 189 | use super::*; 190 | use num::Float; 191 | use num::abs; 192 | 193 | const EPSILON: f32 = 1e-6; 194 | 195 | #[test] 196 | fn test_mean() { 197 | let vec = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 198 | 199 | let diff = abs(mean(&vec) - 1.375); 200 | 201 | assert!(diff <= EPSILON); 202 | } 203 | 204 | #[test] 205 | fn test_median() { 206 | let vec = vec![1.0, 3.0]; 207 | let diff = abs(median(&vec) - 2.0); 208 | 209 | assert!(diff <= EPSILON); 210 | 211 | let vec = vec![1.0, 3.0, 5.0]; 212 | let diff = abs(median(&vec) - 3.0); 213 | 214 | assert!(diff <= EPSILON); 215 | 216 | let vec = vec![1.0, 3.0, 5.0, 7.0]; 217 | let diff = abs(median(&vec) - 4.0); 218 | 219 | assert!(diff <= EPSILON); 220 | } 221 | 222 | #[test] 223 | fn test_variance() { 224 | let v = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 225 | // result is within `epsilon` of expected value 226 | let expected = 1.428571; 227 | 228 | assert!((expected - variance(&v, None)).abs() < EPSILON); 229 | } 230 | 231 | #[test] 232 | fn test_population_variance() { 233 | let v = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 234 | // result is within `epsilon` of expected value 235 | let expected = 1.25; 236 | 237 | assert!((expected - population_variance(&v, None)).abs() < EPSILON); 238 | } 239 | 240 | #[test] 241 | fn test_standard_deviation() { 242 | let v = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 243 | // result is within `epsilon` of expected value 244 | let expected = 1.195229; 245 | 246 | assert!((expected - standard_deviation(&v, None)).abs() < EPSILON); 247 | } 248 | 249 | #[test] 250 | fn test_population_standard_deviation() { 251 | let v = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 252 | // result is within `epsilon` of expected value 253 | let expected = 1.118034; 254 | 255 | assert!((expected - population_standard_deviation(&v, None)).abs() < EPSILON); 256 | } 257 | 258 | #[test] 259 | fn test_standard_scores() { 260 | let v = vec![0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]; 261 | let expected = vec![-1.150407536484354, -0.941242529850835, -0.941242529850835, -0.10458250331675945, 0.10458250331675945, 0.31374750995027834, 1.150407536484354, 1.5687375497513918]; 262 | assert!(expected == standard_scores(&v)); 263 | } 264 | 265 | #[test] 266 | fn test_qsort_empty() { 267 | let mut vec: Vec = vec![]; 268 | quicksort(&mut vec); 269 | assert_eq!(vec, vec![]); 270 | } 271 | 272 | #[test] 273 | fn test_qsort_small() { 274 | let len = 10; 275 | let mut vec = Vec::with_capacity(len); 276 | for _ in 0..len { vec.push(rand::random::()); } 277 | quicksort(&mut vec); 278 | for i in 0..(len-1) { 279 | assert!(vec[i] < vec[i+1], "sorted vectors must be monotonically increasing"); 280 | } 281 | } 282 | 283 | #[test] 284 | fn test_qsort_large() { 285 | let len = 1_000_000; 286 | let mut vec = Vec::with_capacity(len); 287 | for _ in 0..len { vec.push(rand::random::()); } 288 | quicksort(&mut vec); 289 | for i in 0..(len-1) { 290 | assert!(vec[i] < vec[i+1], "sorted vectors must be monotonically increasing"); 291 | } 292 | } 293 | 294 | #[test] 295 | fn test_qsort_sorted() { 296 | let len = 1_000; 297 | let mut vec = Vec::with_capacity(len); 298 | for n in 0..len { vec.push(n); } 299 | quicksort(&mut vec); 300 | for i in 0..(len-1) { 301 | assert!(vec[i] < vec[i+1], "sorted vectors must be monotonically increasing"); 302 | } 303 | } 304 | 305 | #[test] 306 | fn test_qsort_reverse_sorted() { 307 | let len = 1_000; 308 | let mut vec = Vec::with_capacity(len); 309 | for n in 0..len { vec.push(len-n); } 310 | quicksort(&mut vec); 311 | for i in 0..(len-1) { 312 | assert!(vec[i] < vec[i+1], "sorted vectors must be monotonically increasing"); 313 | } 314 | } 315 | 316 | } 317 | --------------------------------------------------------------------------------