tablepress
/
libraries
/
vendor
/
PhpSpreadsheet
/
Calculation
/
Statistical
/
Distributions
/
Binomial.php
Binomial.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.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\Exception; |
| 7 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Functions; |
| 8 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; |
| 9 | use TablePress\PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Combinations; |
| 10 | |
| 11 | class Binomial |
| 12 | { |
| 13 | use ArrayEnabled; |
| 14 | |
| 15 | /** |
| 16 | * BINOMDIST. |
| 17 | * |
| 18 | * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with |
| 19 | * a fixed number of tests or trials, when the outcomes of any trial are only success or failure, |
| 20 | * when trials are independent, and when the probability of success is constant throughout the |
| 21 | * experiment. For example, BINOMDIST can calculate the probability that two of the next three |
| 22 | * babies born are male. |
| 23 | * |
| 24 | * @param mixed $value Integer number of successes in trials |
| 25 | * Or can be an array of values |
| 26 | * @param mixed $trials Integer umber of trials |
| 27 | * Or can be an array of values |
| 28 | * @param mixed $probability Probability of success on each trial as a float |
| 29 | * Or can be an array of values |
| 30 | * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false) |
| 31 | * Or can be an array of values |
| 32 | * |
| 33 | * @return array<mixed>|float|string 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, $trials, $probability, $cumulative) |
| 37 | { |
| 38 | if (is_array($value) || is_array($trials) || is_array($probability) || is_array($cumulative)) { |
| 39 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $trials, $probability, $cumulative); |
| 40 | } |
| 41 | |
| 42 | try { |
| 43 | $value = DistributionValidations::validateInt($value); |
| 44 | $trials = DistributionValidations::validateInt($trials); |
| 45 | $probability = DistributionValidations::validateProbability($probability); |
| 46 | $cumulative = DistributionValidations::validateBool($cumulative); |
| 47 | } catch (Exception $e) { |
| 48 | return $e->getMessage(); |
| 49 | } |
| 50 | |
| 51 | if (($value < 0) || ($value > $trials)) { |
| 52 | return ExcelError::NAN(); |
| 53 | } |
| 54 | |
| 55 | if ($cumulative) { |
| 56 | return self::calculateCumulativeBinomial($value, $trials, $probability); |
| 57 | } |
| 58 | /** @var float $comb */ |
| 59 | $comb = Combinations::withoutRepetition($trials, $value); |
| 60 | |
| 61 | return $comb * $probability ** $value |
| 62 | * (1 - $probability) ** ($trials - $value); |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * BINOM.DIST.RANGE. |
| 67 | * |
| 68 | * Returns the Binomial Distribution probability for the number of successes from a specified number |
| 69 | * of trials falling into a specified range. |
| 70 | * |
| 71 | * @param mixed $trials Integer number of trials |
| 72 | * Or can be an array of values |
| 73 | * @param mixed $probability Probability of success on each trial as a float |
| 74 | * Or can be an array of values |
| 75 | * @param mixed $successes The integer number of successes in trials |
| 76 | * Or can be an array of values |
| 77 | * @param mixed $limit Upper limit for successes in trials as null, or an integer |
| 78 | * If null, then this will indicate the same as the number of Successes |
| 79 | * Or can be an array of values |
| 80 | * |
| 81 | * @return array<mixed>|float|int|string If an array of numbers is passed as an argument, then the returned result will also be an array |
| 82 | * with the same dimensions |
| 83 | */ |
| 84 | public static function range($trials, $probability, $successes, $limit = null) |
| 85 | { |
| 86 | if (is_array($trials) || is_array($probability) || is_array($successes) || is_array($limit)) { |
| 87 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $successes, $limit); |
| 88 | } |
| 89 | |
| 90 | $limit = $limit ?? $successes; |
| 91 | |
| 92 | try { |
| 93 | $trials = DistributionValidations::validateInt($trials); |
| 94 | $probability = DistributionValidations::validateProbability($probability); |
| 95 | $successes = DistributionValidations::validateInt($successes); |
| 96 | $limit = DistributionValidations::validateInt($limit); |
| 97 | } catch (Exception $e) { |
| 98 | return $e->getMessage(); |
| 99 | } |
| 100 | |
| 101 | if (($successes < 0) || ($successes > $trials)) { |
| 102 | return ExcelError::NAN(); |
| 103 | } |
| 104 | if (($limit < 0) || ($limit > $trials) || $limit < $successes) { |
| 105 | return ExcelError::NAN(); |
| 106 | } |
| 107 | |
| 108 | $summer = 0; |
| 109 | for ($i = $successes; $i <= $limit; ++$i) { |
| 110 | /** @var float $comb */ |
| 111 | $comb = Combinations::withoutRepetition($trials, $i); |
| 112 | $summer += $comb * $probability ** $i |
| 113 | * (1 - $probability) ** ($trials - $i); |
| 114 | } |
| 115 | |
| 116 | return $summer; |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * NEGBINOMDIST. |
| 121 | * |
| 122 | * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that |
| 123 | * there will be number_f failures before the number_s-th success, when the constant |
| 124 | * probability of a success is probability_s. This function is similar to the binomial |
| 125 | * distribution, except that the number of successes is fixed, and the number of trials is |
| 126 | * variable. Like the binomial, trials are assumed to be independent. |
| 127 | * |
| 128 | * @param mixed $failures Number of Failures as an integer |
| 129 | * Or can be an array of values |
| 130 | * @param mixed $successes Threshold number of Successes as an integer |
| 131 | * Or can be an array of values |
| 132 | * @param mixed $probability Probability of success on each trial as a float |
| 133 | * Or can be an array of values |
| 134 | * |
| 135 | * @return array<mixed>|float|string The result, or a string containing an error |
| 136 | * If an array of numbers is passed as an argument, then the returned result will also be an array |
| 137 | * with the same dimensions |
| 138 | * |
| 139 | * TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST |
| 140 | * The cumulative default should be false to reflect the behaviour of NEGBINOMDIST |
| 141 | */ |
| 142 | public static function negative($failures, $successes, $probability) |
| 143 | { |
| 144 | if (is_array($failures) || is_array($successes) || is_array($probability)) { |
| 145 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $failures, $successes, $probability); |
| 146 | } |
| 147 | |
| 148 | try { |
| 149 | $failures = DistributionValidations::validateInt($failures); |
| 150 | $successes = DistributionValidations::validateInt($successes); |
| 151 | $probability = DistributionValidations::validateProbability($probability); |
| 152 | } catch (Exception $e) { |
| 153 | return $e->getMessage(); |
| 154 | } |
| 155 | |
| 156 | if (($failures < 0) || ($successes < 1)) { |
| 157 | return ExcelError::NAN(); |
| 158 | } |
| 159 | if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) { |
| 160 | if (($failures + $successes - 1) <= 0) { |
| 161 | return ExcelError::NAN(); |
| 162 | } |
| 163 | } |
| 164 | /** @var float $comb */ |
| 165 | $comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1); |
| 166 | |
| 167 | return $comb |
| 168 | * ($probability ** $successes) * ((1 - $probability) ** $failures); |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * BINOM.INV. |
| 173 | * |
| 174 | * Returns the smallest value for which the cumulative binomial distribution is greater |
| 175 | * than or equal to a criterion value |
| 176 | * |
| 177 | * @param mixed $trials number of Bernoulli trials as an integer |
| 178 | * Or can be an array of values |
| 179 | * @param mixed $probability probability of a success on each trial as a float |
| 180 | * Or can be an array of values |
| 181 | * @param mixed $alpha criterion value as a float |
| 182 | * Or can be an array of values |
| 183 | * |
| 184 | * @return array<mixed>|int|string If an array of numbers is passed as an argument, then the returned result will also be an array |
| 185 | * with the same dimensions |
| 186 | */ |
| 187 | public static function inverse($trials, $probability, $alpha) |
| 188 | { |
| 189 | if (is_array($trials) || is_array($probability) || is_array($alpha)) { |
| 190 | return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $alpha); |
| 191 | } |
| 192 | |
| 193 | try { |
| 194 | $trials = DistributionValidations::validateInt($trials); |
| 195 | $probability = DistributionValidations::validateProbability($probability); |
| 196 | $alpha = DistributionValidations::validateFloat($alpha); |
| 197 | } catch (Exception $e) { |
| 198 | return $e->getMessage(); |
| 199 | } |
| 200 | |
| 201 | if ($trials < 0) { |
| 202 | return ExcelError::NAN(); |
| 203 | } elseif (($alpha < 0.0) || ($alpha > 1.0)) { |
| 204 | return ExcelError::NAN(); |
| 205 | } |
| 206 | |
| 207 | $successes = 0; |
| 208 | while ($successes <= $trials) { |
| 209 | $result = self::calculateCumulativeBinomial($successes, $trials, $probability); |
| 210 | if ($result >= $alpha) { |
| 211 | break; |
| 212 | } |
| 213 | ++$successes; |
| 214 | } |
| 215 | |
| 216 | return $successes; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * @return float|int |
| 221 | */ |
| 222 | private static function calculateCumulativeBinomial(int $value, int $trials, float $probability) |
| 223 | { |
| 224 | $summer = 0; |
| 225 | for ($i = 0; $i <= $value; ++$i) { |
| 226 | /** @var float $comb */ |
| 227 | $comb = Combinations::withoutRepetition($trials, $i); |
| 228 | $summer += $comb * $probability ** $i |
| 229 | * (1 - $probability) ** ($trials - $i); |
| 230 | } |
| 231 | |
| 232 | return $summer; |
| 233 | } |
| 234 | } |
| 235 |