ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ChiSquared.php
Go to the documentation of this file.
1 <?php
2 
4 
7 
8 class ChiSquared
9 {
10  private const MAX_ITERATIONS = 256;
11 
12  private const EPS = 2.22e-16;
13 
24  public static function distributionRightTail($value, $degrees)
25  {
26  $value = Functions::flattenSingleValue($value);
27  $degrees = Functions::flattenSingleValue($degrees);
28 
29  try {
31  $degrees = DistributionValidations::validateInt($degrees);
32  } catch (Exception $e) {
33  return $e->getMessage();
34  }
35 
36  if ($degrees < 1) {
37  return Functions::NAN();
38  }
39  if ($value < 0) {
41  return 1;
42  }
43 
44  return Functions::NAN();
45  }
46 
47  return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2));
48  }
49 
61  public static function distributionLeftTail($value, $degrees, $cumulative)
62  {
63  $value = Functions::flattenSingleValue($value);
64  $degrees = Functions::flattenSingleValue($degrees);
65  $cumulative = Functions::flattenSingleValue($cumulative);
66 
67  try {
69  $degrees = DistributionValidations::validateInt($degrees);
70  $cumulative = DistributionValidations::validateBool($cumulative);
71  } catch (Exception $e) {
72  return $e->getMessage();
73  }
74 
75  if ($degrees < 1) {
76  return Functions::NAN();
77  }
78  if ($value < 0) {
80  return 1;
81  }
82 
83  return Functions::NAN();
84  }
85 
86  if ($cumulative === true) {
87  return 1 - self::distributionRightTail($value, $degrees);
88  }
89 
90  return (($value ** (($degrees / 2) - 1) * exp(-$value / 2))) /
91  ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
92  }
93 
104  public static function inverseRightTail($probability, $degrees)
105  {
106  $probability = Functions::flattenSingleValue($probability);
107  $degrees = Functions::flattenSingleValue($degrees);
108 
109  try {
110  $probability = DistributionValidations::validateProbability($probability);
111  $degrees = DistributionValidations::validateInt($degrees);
112  } catch (Exception $e) {
113  return $e->getMessage();
114  }
115 
116  if ($degrees < 1) {
117  return Functions::NAN();
118  }
119 
120  $callback = function ($value) use ($degrees) {
121  return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
122  / Gamma::gammaValue($degrees / 2));
123  };
124 
125  $newtonRaphson = new NewtonRaphson($callback);
126 
127  return $newtonRaphson->execute($probability);
128  }
129 
140  public static function inverseLeftTail($probability, $degrees)
141  {
142  $probability = Functions::flattenSingleValue($probability);
143  $degrees = Functions::flattenSingleValue($degrees);
144 
145  try {
146  $probability = DistributionValidations::validateProbability($probability);
147  $degrees = DistributionValidations::validateInt($degrees);
148  } catch (Exception $e) {
149  return $e->getMessage();
150  }
151 
152  if ($degrees < 1) {
153  return Functions::NAN();
154  }
155 
156  return self::inverseLeftTailCalculation($probability, $degrees);
157  }
158 
171  public static function test($actual, $expected)
172  {
173  $rows = count($actual);
174  $actual = Functions::flattenArray($actual);
175  $expected = Functions::flattenArray($expected);
176  $columns = count($actual) / $rows;
177 
178  $countActuals = count($actual);
179  $countExpected = count($expected);
180  if ($countActuals !== $countExpected || $countActuals === 1) {
181  return Functions::NAN();
182  }
183 
184  $result = 0.0;
185  for ($i = 0; $i < $countActuals; ++$i) {
186  if ($expected[$i] == 0.0) {
187  return Functions::DIV0();
188  } elseif ($expected[$i] < 0.0) {
189  return Functions::NAN();
190  }
191  $result += (($actual[$i] - $expected[$i]) ** 2) / $expected[$i];
192  }
193 
194  $degrees = self::degrees($rows, $columns);
195 
196  $result = self::distributionRightTail($result, $degrees);
197 
198  return $result;
199  }
200 
201  protected static function degrees(int $rows, int $columns): int
202  {
203  if ($rows === 1) {
204  return $columns - 1;
205  } elseif ($columns === 1) {
206  return $rows - 1;
207  }
208 
209  return ($columns - 1) * ($rows - 1);
210  }
211 
212  private static function inverseLeftTailCalculation(float $probability, int $degrees): float
213  {
214  // bracket the root
215  $min = 0;
216  $sd = sqrt(2.0 * $degrees);
217  $max = 2 * $sd;
218  $s = -1;
219 
220  while ($s * self::pchisq($max, $degrees) > $probability * $s) {
221  $min = $max;
222  $max += 2 * $sd;
223  }
224 
225  // Find root using bisection
226  $chi2 = 0.5 * ($min + $max);
227 
228  while (($max - $min) > self::EPS * $chi2) {
229  if ($s * self::pchisq($chi2, $degrees) > $probability * $s) {
230  $min = $chi2;
231  } else {
232  $max = $chi2;
233  }
234  $chi2 = 0.5 * ($min + $max);
235  }
236 
237  return $chi2;
238  }
239 
240  private static function pchisq($chi2, $degrees)
241  {
242  return self::gammp($degrees, 0.5 * $chi2);
243  }
244 
245  private static function gammp($n, $x)
246  {
247  if ($x < 0.5 * $n + 1) {
248  return self::gser($n, $x);
249  }
250 
251  return 1 - self::gcf($n, $x);
252  }
253 
254  // Return the incomplete gamma function P(n/2,x) evaluated by
255  // series representation. Algorithm from numerical recipe.
256  // Assume that n is a positive integer and x>0, won't check arguments.
257  // Relative error controlled by the eps parameter
258  private static function gser($n, $x)
259  {
260  $gln = Gamma::ln($n / 2);
261  $a = 0.5 * $n;
262  $ap = $a;
263  $sum = 1.0 / $a;
264  $del = $sum;
265  for ($i = 1; $i < 101; ++$i) {
266  ++$ap;
267  $del = $del * $x / $ap;
268  $sum += $del;
269  if ($del < $sum * self::EPS) {
270  break;
271  }
272  }
273 
274  return $sum * exp(-$x + $a * log($x) - $gln);
275  }
276 
277  // Return the incomplete gamma function Q(n/2,x) evaluated by
278  // its continued fraction representation. Algorithm from numerical recipe.
279  // Assume that n is a postive integer and x>0, won't check arguments.
280  // Relative error controlled by the eps parameter
281  private static function gcf($n, $x)
282  {
283  $gln = Gamma::ln($n / 2);
284  $a = 0.5 * $n;
285  $b = $x + 1 - $a;
286  $fpmin = 1.e-300;
287  $c = 1 / $fpmin;
288  $d = 1 / $b;
289  $h = $d;
290  for ($i = 1; $i < 101; ++$i) {
291  $an = -$i * ($i - $a);
292  $b += 2;
293  $d = $an * $d + $b;
294  if (abs($d) < $fpmin) {
295  $d = $fpmin;
296  }
297  $c = $b + $an / $c;
298  if (abs($c) < $fpmin) {
299  $c = $fpmin;
300  }
301  $d = 1 / $d;
302  $del = $d * $c;
303  $h = $h * $del;
304  if (abs($del - 1) < self::EPS) {
305  break;
306  }
307  }
308 
309  return $h * exp(-$x + $a * log($x) - $gln);
310  }
311 }
$result
$h
$s
Definition: pwgen.php:45
static flattenArray($array)
Convert a multi-dimensional array to a simple 1-dimensional array.
Definition: Functions.php:583
$n
Definition: RandomTest.php:85
static distributionLeftTail($value, $degrees, $cumulative)
CHIDIST.
Definition: ChiSquared.php:61
$rows
Definition: xhr_table.php:10
static inverseLeftTailCalculation(float $probability, int $degrees)
Definition: ChiSquared.php:212
$i
Definition: disco.tpl.php:19
static flattenSingleValue($value='')
Convert an array to a single scalar value by extracting the first element.
Definition: Functions.php:649
if(! $in) $columns
Definition: Utf8Test.php:45
$x
Definition: complexTest.php:9
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
static getCompatibilityMode()
Return the current Compatibility Mode.
Definition: Functions.php:93