1<?php 2 3/* 4 Phoronix Test Suite 5 URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/ 6 Copyright (C) 2009 - 2021, Phoronix Media 7 Copyright (C) 2009 - 2021, Michael Larabel 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see <http://www.gnu.org/licenses/>. 21*/ 22 23class pts_math 24{ 25 public static function values_outside_three_sigma_limits($values) 26 { 27 $p = pts_math::get_precision($values); 28 $tsl = pts_math::three_sigma_limits($values, $p); 29 $outside_limits = array(); 30 foreach($values as $num) 31 { 32 if($num < $tsl[0] || $num > $tsl[1]) 33 { 34 $outside_limits[] = $num; 35 } 36 } 37 38 return empty($outside_limits) ? false : $outside_limits; 39 } 40 public static function three_sigma_limits($values, $p = 2) 41 { 42 $avg = pts_math::arithmetic_mean($values); 43 $variance = pts_math::variance($values, $avg); 44 $std_dev = sqrt($variance); 45 $std_dev_3x = $std_dev * 3; 46 47 return array(round($avg - $std_dev_3x, $p), round($avg + $std_dev_3x, $p)); 48 } 49 public static function variance($values, $avg) 50 { 51 return array_sum(array_map(function($v) use ($avg) { return pow($v - $avg, 2); }, $values)) / count($values); 52 } 53 public static function arithmetic_mean($values) 54 { 55 return array_sum($values) / count($values); 56 } 57 public static function geometric_mean($values) 58 { 59 // simple code hits INF issue on large arrays 60 //return pow(array_product($values), (1 / count($values))); 61 $power = 1 / count($values); 62 $chunk_r = array(); 63 64 foreach(array_chunk($values, 8) as $chunk) 65 { 66 $chunk_r[] = pow(array_product($chunk), $power); 67 } 68 69 return array_product($chunk_r); 70 } 71 public static function harmonic_mean($values) 72 { 73 // useful for rates / all same result types 74 $sum = 0; 75 foreach($values as $v) 76 { 77 $sum += 1 / $v; 78 } 79 return (1 / $sum) * count($values); 80 } 81 public static function max_number($a, $b, $fallback_on_no_number = 1) 82 { 83 if(($an = is_numeric($a)) && ($bn = is_numeric($b))) 84 { 85 return max($a, $b); 86 } 87 else if($an) 88 { 89 return $a; 90 } 91 else if(isset($bn) && $bn) 92 { 93 return $b; 94 } 95 else 96 { 97 return $fallback_on_no_number; 98 } 99 } 100 public static function standard_error($values) 101 { 102 self::clean_numeric_array($values); 103 104 return empty($values) ? 0 : (self::standard_deviation($values) / sqrt(count($values))); 105 } 106 public static function remove_outliers($values, $mag = 2) 107 { 108 $ret = array(); 109 $mean = pts_math::arithmetic_mean($values); 110 $std_dev = self::standard_deviation($values); 111 $outlier = $mag * $std_dev; 112 foreach($values as $i) 113 { 114 if(is_numeric($i) && abs($i - $mean) <= $outlier) 115 { 116 $ret[] = $i; 117 } 118 } 119 120 return $ret; 121 } 122 public static function standard_deviation($values) 123 { 124 self::clean_numeric_array($values); 125 $count = count($values); 126 127 if($count < 2) 128 { 129 return 0; 130 } 131 132 $total = array_sum($values); 133 $mean = $total / $count; 134 $standard_sum = 0; 135 136 foreach($values as $value) 137 { 138 $standard_sum += pow(($value - $mean), 2); 139 } 140 141 return sqrt($standard_sum / ($count - 1)); 142 } 143 public static function percent_standard_deviation($values) 144 { 145 if(count($values) == 0) 146 { 147 // No values 148 return 0; 149 } 150 151 $standard_deviation = pts_math::standard_deviation($values); 152 $average_value = pts_math::arithmetic_mean($values); 153 154 return $average_value != 0 ? ($standard_deviation / $average_value * 100) : 0; 155 } 156 public static function get_precision($number) 157 { 158 // number of decimal digits 159 if($number === null) 160 { 161 return 0; 162 } 163 else if(is_array($number)) 164 { 165 $max_precision = 0; 166 foreach($number as $n) 167 { 168 $max_precision = max($max_precision, pts_math::get_precision($n)); 169 } 170 171 return $max_precision; 172 } 173 else 174 { 175 return strlen(substr(strrchr($number, '.'), 1)); 176 } 177 } 178 public static function set_precision($number, $precision = 2) 179 { 180 // This is better than using round() with precision because of the $precision is > than the current value, 0s will not be appended 181 return is_numeric($number) ? number_format($number, $precision, '.', '') : $number; 182 } 183 public static function find_percentile($values, $quartile, $values_sorted = false) 184 { 185 if($values_sorted == false) 186 { 187 sort($values, SORT_NUMERIC); 188 } 189 $qr_index = count($values) * $quartile; 190 $qr = $values[floor($qr_index)]; 191 192 return $qr; 193 } 194 public static function first_quartile($values) 195 { 196 return self::find_percentile($values, 0.25); 197 } 198 public static function third_quartile($values) 199 { 200 return self::find_percentile($values, 0.75); 201 } 202 public static function inter_quartile_range($values) 203 { 204 return self::third_quartile($values) - self::first_quartile($values); 205 } 206 protected static function clean_numeric_array(&$values) 207 { 208 foreach($values as $i => $value) 209 { 210 if(!is_numeric($value)) 211 { 212 unset($values[$i]); 213 } 214 } 215 } 216 public static function median($values) 217 { 218 if(count($values) < 3) 219 { 220 return; 221 } 222 223 sort($values); 224 $count = count($values); 225 $index = floor($count / 2); 226 if($count & 1) 227 { 228 return $values[$index]; 229 } 230 else if(is_numeric($values[($index - 1)]) && is_numeric($values[($index + 1)])) 231 { 232 return ($values[($index - 1)] + $values[($index + 1)]) / 2; 233 } 234 } 235 public static function number_with_unit_to_mb($value) 236 { 237 $value = trim($value); 238 $num = null; 239 $unit = null; 240 if(($x = strpos($value, ' ')) !== false) 241 { 242 $num = substr($value, 0, $x); 243 $unit = substr($value, ($x + 1)); 244 } 245 else 246 { 247 switch(substr($value, -1)) 248 { 249 case 'K': 250 $num = substr($value, 0, -1); 251 $unit = 'K'; 252 break; 253 } 254 } 255 256 return $num != null && $unit != null ? self::unit_to_mb($num, $unit) : null; 257 } 258 public static function unit_to_mb($value, $current_unit) 259 { 260 $v = null; 261 switch($current_unit) 262 { 263 case 'kb': 264 $v = $value / 1000; 265 break; 266 case 'K': 267 $v = $value / 1024; 268 break; 269 case 'MiB': 270 $v = $value; 271 break; 272 } 273 274 return $v; 275 } 276} 277 278?> 279