PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Calculation / Statistical / Distributions / StudentT.php

StudentT.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php

333 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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\Information\ExcelError;
8
9 /**
10 * Some of this code is drived from Perl CPAN Statistical::Distributions.
11 * Its copyright statement is:
12 * Copyright 2003 Michael Kospach. All rights reserved.
13 *
14 * This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
15 */
16 class StudentT
17 {
18 use ArrayEnabled;
19
20 /**
21 * TDIST.
22 *
23 * Returns the probability of Student's T distribution.
24 *
25 * @param mixed $value Float value for the distribution
26 * Or can be an array of values
27 * @param mixed $degrees Integer value for degrees of freedom
28 * Or can be an array of values
29 * @param mixed $tails Integer value for the number of tails (1 or 2)
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, $degrees, $tails)
37 {
38 return self::calcDistribution($value, $degrees, $tails, \Closure::fromCallable([self::class, 'distribution']));
39 }
40
41 /**
42 * T.DIST.2T.
43 * Returns the two-tailed Student's t distribution.
44 *
45 * @return array<mixed>|float|string The result, or a string containing an error
46 * @param mixed $value
47 * @param mixed $degrees
48 */
49 public static function tDotDistDot2T($value, $degrees)
50 {
51 return self::calcDistribution($value, $degrees, 2, \Closure::fromCallable([self::class, 'distribution']));
52 }
53
54 /**
55 * T.DIST.RT.
56 * Returns the right-tailed Student's t distribution.
57 *
58 * @return array<mixed>|float|string The result, or a string containing an error
59 * @param mixed $value
60 * @param mixed $degrees
61 */
62 public static function tDotDistDotRT($value, $degrees)
63 {
64 return self::calcDistribution($value, $degrees, 1, \Closure::fromCallable([self::class, 'distribution']));
65 }
66
67 /**
68 * @return array<mixed>|float|string The result, or a string containing an error
69 * @param mixed $value
70 * @param mixed $degrees
71 * @param mixed $tails
72 */
73 private static function calcDistribution($value, $degrees, $tails, callable $callback)
74 {
75 if (is_array($value) || is_array($degrees) || is_array($tails)) {
76 return self::evaluateArrayArguments($callback, $value, $degrees, $tails);
77 }
78
79 try {
80 $value = DistributionValidations::validateFloat($value);
81 $degrees = DistributionValidations::validateInt($degrees);
82 $tails = DistributionValidations::validateInt($tails);
83 } catch (Exception $e) {
84 return $e->getMessage();
85 }
86
87 if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
88 return ExcelError::NAN();
89 }
90
91 return self::subTProb($value, $degrees, $tails);
92 }
93
94 /**
95 * Based on code from Perl CPAN Statistical::Distributions.
96 */
97 private static function subTProb(float $x, int $n, int $tails): float
98 {
99 $w = atan2($x / sqrt($n), 1);
100 $z = cos($w) ** 2;
101 $y = 1;
102
103 for ($i = $n - 2; $i >= 2; $i -= 2) {
104 $y = 1 + ($i - 1) / $i * $z * $y;
105 }
106
107 if ($n % 2 == 0) {
108 $a = sin($w) / 2;
109 $b = 0.5;
110 } else {
111 $a = ($n == 1) ? 0 : (sin($w) * cos($w) / M_PI);
112 $b = 0.5 + $w / M_PI;
113 }
114
115 return $tails * max(0, 1 - $b - $a * $y);
116 }
117
118 /**
119 * T.DIST.
120 * Returns the Student's left-tailed t distribution,
121 * either as a cumulative distribution function (cdf) (TRUE)
122 * or as a probability density function (pdf) (FALSE),
123 * where TRUE/FALSE are the value of $cumulative parameter.
124 *
125 * "True" algoritm adapted from java.
126 * org.apache.commons.math3.distribution.TDistribution.
127 * "False" algorithm comes from:
128 * https://statproofbook.github.io/P/t-pdf.html
129 *
130 * @param mixed $cumulative Expecting bool. See above for explanation.
131 *
132 * @return array<mixed>|float|string The result, or a string containing an error
133 * @param mixed $value
134 * @param mixed $degrees
135 */
136 public static function tDotDist($value, $degrees, $cumulative)
137 {
138 if (is_array($value) || is_array($degrees) || is_array($cumulative)) {
139 return self::evaluateArrayArguments(\Closure::fromCallable([self::class, 'tDotDist']), $value, $degrees, $cumulative);
140 }
141
142 try {
143 $value = DistributionValidations::validateFloat($value);
144 $degrees = DistributionValidations::validateInt($degrees);
145 $cumulative = DistributionValidations::validateBool($cumulative);
146 } catch (Exception $e) {
147 return $e->getMessage();
148 }
149
150 /** @var int $degrees */
151 if (($degrees < 1)) {
152 return ExcelError::NAN();
153 }
154 /** @var float $value */
155 if (!$cumulative) {
156 return self::tDotDistFalse($value, $degrees);
157 }
158
159 $f16 = $degrees / ($degrees + $value * $value);
160 $g16 = 0.5 * $degrees;
161 $h16 = 0.5;
162 $result = Beta::distribution($f16, $g16, $h16);
163 if (is_numeric($result)) {
164 $result = ($value < 0) ? (0.5 * $result) : (1 - 0.5 * $result);
165 }
166
167 return $result;
168 }
169
170 /**
171 * @return float|string
172 */
173 private static function tDotDistFalse(float $value, int $degrees)
174 {
175 $result = $k15 = Gamma::gamma(($degrees + 1) / 2);
176 if (is_numeric($k15)) {
177 $result = $k16 = Gamma::gamma($degrees / 2);
178 if (is_numeric($k16)) {
179 $k17 = sqrt(M_PI * $degrees);
180 $k18 = $k15 / ($k16 * $k17);
181 $k19 = $value * $value / $degrees + 1;
182 $k20 = -($degrees + 1) / 2;
183 $k21 = $k19 ** $k20;
184 $result = $k18 * $k21;
185 }
186 }
187
188 /** @var float|string $result */
189 return $result;
190 }
191
192 /**
193 * TINV and T.INV.2T.
194 * Returns the two-tailed inverse of the Student t distribution.
195 *
196 * @param mixed $probability Float probability for the function
197 * Or can be an array of values
198 * @param mixed $degrees Integer value for degrees of freedom
199 * Or can be an array of values
200 *
201 * @return array<mixed>|float|string The result, or a string containing an error
202 * If an array of numbers is passed as an argument, then the returned result will also be an array
203 * with the same dimensions
204 */
205 public static function inverse($probability, $degrees)
206 {
207 return self::calcInverse($probability, $degrees, 2, \Closure::fromCallable([self::class, 'inverse']));
208 }
209
210 /**
211 * @return array<mixed>|float|string The result, or a string containing an error
212 * @param mixed $probability
213 * @param mixed $degrees
214 */
215 private static function calcInverse($probability, $degrees, int $tails, callable $callback2)
216 {
217 if (is_array($probability) || is_array($degrees)) {
218 return self::evaluateArrayArguments($callback2, $probability, $degrees, $tails);
219 }
220
221 try {
222 $probability = DistributionValidations::validateProbability($probability);
223 $degrees = DistributionValidations::validateInt($degrees);
224 } catch (Exception $e) {
225 return $e->getMessage();
226 }
227
228 if ($degrees <= 0) {
229 return ExcelError::NAN();
230 }
231
232 $callback = fn ($value) => self::distribution($value, $degrees, $tails);
233
234 $newtonRaphson = new NewtonRaphson($callback);
235
236 $result = $newtonRaphson->execute($probability);
237 if (is_numeric($result) && $tails === 1) {
238 $result = -$result; // @codeCoverageIgnore
239 }
240
241 return $result;
242 }
243
244 /**
245 * T.INV.
246 * Returns the left-tailed inverse of the Student's t distribution.
247 *
248 * Based on code from Perl CPAN Statistical::Distributions.
249 *
250 * @return array<mixed>|float|string The result, or a string containing an error
251 * @param mixed $probability
252 * @param mixed $degrees
253 */
254 public static function tDotInv($probability, $degrees)
255 {
256 if (is_array($probability) || is_array($degrees)) {
257 return self::evaluateArrayArguments(\Closure::fromCallable([self::class, 'tDotInv']), $probability, $degrees);
258 }
259
260 try {
261 $probability = DistributionValidations::validateProbability($probability);
262 $degrees = DistributionValidations::validateInt($degrees);
263 } catch (Exception $e) {
264 return $e->getMessage();
265 }
266
267 if ($degrees < 1) {
268 return ExcelError::NAN();
269 }
270 if ($probability == 0.5) {
271 return 0.0;
272 }
273 if ($probability < 0.5) {
274 $result = self::tDotInv(1.0 - $probability, $degrees);
275
276 return is_numeric($result) ? -$result : $result;
277 }
278 $p = $probability;
279 $n = $degrees;
280 $u = self::subU($p);
281 $u2 = $u ** 2;
282
283 $a = ($u2 + 1) / 4;
284 $b = ((5 * $u2 + 16) * $u2 + 3) / 96;
285 $c = (((3 * $u2 + 19) * $u2 + 17) * $u2 - 15) / 384;
286 $d = ((((79 * $u2 + 776) * $u2 + 1482) * $u2 - 1920) * $u2 - 945) / 92160;
287 $e = (((((27 * $u2 + 339) * $u2 + 930) * $u2 - 1782) * $u2 - 765) * $u2 + 17955) / 368640;
288
289 $x = $u * (1 + ($a + ($b + ($c + ($d + $e / $n) / $n) / $n) / $n) / $n);
290
291 if ($n <= log10($p) ** 2 + 3) {
292 do {
293 $p1 = self::subTProb($x, $n, 1);
294 $n1 = $n + 1;
295 $delta = ($p1 - $p)
296 / exp(($n1 * log($n1 / ($n + $x * $x))
297 + log($n / $n1 / 2 / M_PI) - 1
298 + (1 / $n1 - 1 / $n) / 6) / 2);
299 $x += $delta;
300 $round = sprintf('%.' . abs((int) (log10(abs($x)) - 4)) . 'F', $delta);
301 } while (($x) && ($round != 0));
302 }
303
304 return -$x;
305 }
306
307 /**
308 * Based on code from Perl CPAN Statistical::Distributions.
309 */
310 private static function subU(float $p): float
311 {
312 $y = -log(4 * $p * (1 - $p));
313 $x = sqrt(
314 $y * (1.570796288
315 + $y * (.03706987906
316 + $y * (-.8364353589E-3
317 + $y * (-.2250947176E-3
318 + $y * (.6841218299E-5
319 + $y * (0.5824238515E-5
320 + $y * (-.104527497E-5
321 + $y * (.8360937017E-7
322 + $y * (-.3231081277E-8
323 + $y * (.3657763036E-10
324 + $y * .6936233982E-12))))))))))
325 );
326 if ($p > 0.5) {
327 $x = -$x;
328 }
329
330 return $x;
331 }
332 }
333