tablepress
/
libraries
/
vendor
/
PhpSpreadsheet
/
Calculation
/
Statistical
/
Distributions
/
Normal.php
Normal.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
| 1 | <?php |
| 2 | |
| 3 | namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions; |
| 4 | |
| 5 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; |
| 6 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engineering; |
| 7 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception; |
| 8 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; |
| 9 | |
| 10 | class Normal |
| 11 | { |
| 12 | use ArrayEnabled; |
| 13 | |
| 14 | public const SQRT2PI = 2.5066282746310005024157652848110452530069867406099; |
| 15 | |
| 16 | /** |
| 17 | * NORMDIST. |
| 18 | * |
| 19 | * Returns the normal distribution for the specified mean and standard deviation. This |
| 20 | * function has a very wide range of applications in statistics, including hypothesis |
| 21 | * testing. |
| 22 | * |
| 23 | * @param mixed $value Float value for which we want the probability |
| 24 | * Or can be an array of values |
| 25 | * @param mixed $mean Mean value as a float |
| 26 | * Or can be an array of values |
| 27 | * @param mixed $stdDev Standard Deviation as a float |
| 28 | * Or can be an array of values |
| 29 | * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false) |
| 30 | * Or can be an array of values |
| 31 | * |
| 32 | * @return array<mixed>|float|string The result, or a string containing an error |
| 33 | * If an array of numbers is passed as an argument, then the returned result will also be an array |
| 34 | * with the same dimensions |
| 35 | */ |
| 36 | public static function distribution($value, $mean, $stdDev, $cumulative) |
| 37 | { |
| 38 | if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) { |
| 39 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative); |
| 40 | } |
| 41 | |
| 42 | try { |
| 43 | $value = DistributionValidations::validateFloat($value); |
| 44 | $mean = DistributionValidations::validateFloat($mean); |
| 45 | $stdDev = DistributionValidations::validateFloat($stdDev); |
| 46 | $cumulative = DistributionValidations::validateBool($cumulative); |
| 47 | } catch (Exception $e) { |
| 48 | return $e->getMessage(); |
| 49 | } |
| 50 | |
| 51 | if ($stdDev < 0) { |
| 52 | return ExcelError::NAN(); |
| 53 | } |
| 54 | |
| 55 | if ($cumulative) { |
| 56 | return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2)))); |
| 57 | } |
| 58 | |
| 59 | return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev)))); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * NORMINV. |
| 64 | * |
| 65 | * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation. |
| 66 | * |
| 67 | * @param mixed $probability Float probability for which we want the value |
| 68 | * Or can be an array of values |
| 69 | * @param mixed $mean Mean Value as a float |
| 70 | * Or can be an array of values |
| 71 | * @param mixed $stdDev Standard Deviation as a float |
| 72 | * Or can be an array of values |
| 73 | * |
| 74 | * @return array<mixed>|float|string The result, or a string containing an error |
| 75 | * If an array of numbers is passed as an argument, then the returned result will also be an array |
| 76 | * with the same dimensions |
| 77 | */ |
| 78 | public static function inverse($probability, $mean, $stdDev) |
| 79 | { |
| 80 | if (is_array($probability) || is_array($mean) || is_array($stdDev)) { |
| 81 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev); |
| 82 | } |
| 83 | |
| 84 | try { |
| 85 | $probability = DistributionValidations::validateProbability($probability); |
| 86 | $mean = DistributionValidations::validateFloat($mean); |
| 87 | $stdDev = DistributionValidations::validateFloat($stdDev); |
| 88 | } catch (Exception $e) { |
| 89 | return $e->getMessage(); |
| 90 | } |
| 91 | |
| 92 | if ($stdDev < 0) { |
| 93 | return ExcelError::NAN(); |
| 94 | } |
| 95 | |
| 96 | return (self::inverseNcdf($probability) * $stdDev) + $mean; |
| 97 | } |
| 98 | |
| 99 | /* |
| 100 | * inverse_ncdf.php |
| 101 | * ------------------- |
| 102 | * begin : Friday, January 16, 2004 |
| 103 | * copyright : (C) 2004 Michael Nickerson |
| 104 | * email : [email protected] |
| 105 | * |
| 106 | */ |
| 107 | private static function inverseNcdf(float $p): float |
| 108 | { |
| 109 | // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to |
| 110 | // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as |
| 111 | // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html |
| 112 | // I have not checked the accuracy of this implementation. Be aware that PHP |
| 113 | // will truncate the coeficcients to 14 digits. |
| 114 | |
| 115 | // You have permission to use and distribute this function freely for |
| 116 | // whatever purpose you want, but please show common courtesy and give credit |
| 117 | // where credit is due. |
| 118 | |
| 119 | // Input parameter is $p - probability - where 0 < p < 1. |
| 120 | |
| 121 | // Coefficients in rational approximations |
| 122 | /** @var array<int, float> */ |
| 123 | static $a = [ |
| 124 | 1 => -3.969683028665376e+01, |
| 125 | 2 => 2.209460984245205e+02, |
| 126 | 3 => -2.759285104469687e+02, |
| 127 | 4 => 1.383577518672690e+02, |
| 128 | 5 => -3.066479806614716e+01, |
| 129 | 6 => 2.506628277459239e+00, |
| 130 | ]; |
| 131 | |
| 132 | /** @var array<int, float> */ |
| 133 | static $b = [ |
| 134 | 1 => -5.447609879822406e+01, |
| 135 | 2 => 1.615858368580409e+02, |
| 136 | 3 => -1.556989798598866e+02, |
| 137 | 4 => 6.680131188771972e+01, |
| 138 | 5 => -1.328068155288572e+01, |
| 139 | ]; |
| 140 | |
| 141 | /** @var array<int, float> */ |
| 142 | static $c = [ |
| 143 | 1 => -7.784894002430293e-03, |
| 144 | 2 => -3.223964580411365e-01, |
| 145 | 3 => -2.400758277161838e+00, |
| 146 | 4 => -2.549732539343734e+00, |
| 147 | 5 => 4.374664141464968e+00, |
| 148 | 6 => 2.938163982698783e+00, |
| 149 | ]; |
| 150 | |
| 151 | /** @var array<int, float> */ |
| 152 | static $d = [ |
| 153 | 1 => 7.784695709041462e-03, |
| 154 | 2 => 3.224671290700398e-01, |
| 155 | 3 => 2.445134137142996e+00, |
| 156 | 4 => 3.754408661907416e+00, |
| 157 | ]; |
| 158 | |
| 159 | // Define lower and upper region break-points. |
| 160 | $p_low = 0.02425; //Use lower region approx. below this |
| 161 | $p_high = 1 - $p_low; //Use upper region approx. above this |
| 162 | |
| 163 | if (0 < $p && $p < $p_low) { |
| 164 | // Rational approximation for lower region. |
| 165 | $q = sqrt(-2 * log($p)); |
| 166 | |
| 167 | return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) |
| 168 | / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1); |
| 169 | } elseif ($p_high < $p && $p < 1) { |
| 170 | // Rational approximation for upper region. |
| 171 | $q = sqrt(-2 * log(1 - $p)); |
| 172 | |
| 173 | return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) |
| 174 | / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1); |
| 175 | } |
| 176 | |
| 177 | // Rational approximation for central region. |
| 178 | $q = $p - 0.5; |
| 179 | $r = $q * $q; |
| 180 | |
| 181 | return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q |
| 182 | / ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1); |
| 183 | } |
| 184 | } |
| 185 |