Implemented the CHISQ.DIST() Statistical function. (#1961)

* Implementation of the CHISQ.DIST() statistical function for left tail distribution
This commit is contained in:
Mark Baker 2021-03-28 16:13:00 +02:00 committed by GitHub
parent aff1d35e4a
commit e2ff14fe89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 183 additions and 29 deletions

View File

@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added
- Implemented the CHITEST() Statistical function.
- Implemented the CHITEST() and CHISQ.DIST() Statistical function.
- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
### Changed

View File

@ -488,22 +488,22 @@ class Calculation
],
'CHIDIST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'],
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
'argumentCount' => '2',
],
'CHISQ.DIST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Functions::class, 'DUMMY'],
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionLeftTail'],
'argumentCount' => '3',
],
'CHISQ.DIST.RT' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'],
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
'argumentCount' => '2',
],
'CHIINV' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'],
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
'argumentCount' => '2',
],
'CHISQ.INV' => [
@ -513,7 +513,7 @@ class Calculation
],
'CHISQ.INV.RT' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'],
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
'argumentCount' => '2',
],
'CHITEST' => [

View File

@ -297,8 +297,8 @@ class Statistical
*
* @Deprecated 1.18.0
*
* @see Statistical\Distributions\ChiSquared::distribution()
* Use the distribution() method in the Statistical\Distributions\ChiSquared class instead
* @see Statistical\Distributions\ChiSquared::distributionRightTail()
* Use the distributionRightTail() method in the Statistical\Distributions\ChiSquared class instead
*
* @param float $value Value for the function
* @param float $degrees degrees of freedom
@ -307,7 +307,7 @@ class Statistical
*/
public static function CHIDIST($value, $degrees)
{
return Statistical\Distributions\ChiSquared::distribution($value, $degrees);
return Statistical\Distributions\ChiSquared::distributionRightTail($value, $degrees);
}
/**
@ -317,8 +317,8 @@ class Statistical
*
* @Deprecated 1.18.0
*
* @see Statistical\Distributions\ChiSquared::inverse()
* Use the inverse() method in the Statistical\Distributions\ChiSquared class instead
* @see Statistical\Distributions\ChiSquared::inverseRightTail()
* Use the inverseRightTail() method in the Statistical\Distributions\ChiSquared class instead
*
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
@ -327,7 +327,7 @@ class Statistical
*/
public static function CHIINV($probability, $degrees)
{
return Statistical\Distributions\ChiSquared::inverse($probability, $degrees);
return Statistical\Distributions\ChiSquared::inverseRightTail($probability, $degrees);
}
/**
@ -2159,7 +2159,7 @@ class Statistical
/**
* TINV.
*
* Returns the one-tailed probability of the chi-squared distribution.
* Returns the one-tailed probability of the Student-T distribution.
*
* @Deprecated 1.18.0
*

View File

@ -21,7 +21,7 @@ class ChiSquared
*
* @return float|string
*/
public static function distribution($value, $degrees)
public static function distributionRightTail($value, $degrees)
{
$value = Functions::flattenSingleValue($value);
$degrees = Functions::flattenSingleValue($degrees);
@ -48,16 +48,60 @@ class ChiSquared
}
/**
* CHIINV.
* CHIDIST.
*
* Returns the one-tailed probability of the chi-squared distribution.
*
* @param mixed (float) $value Value for the function
* @param mixed (int) $degrees degrees of freedom
* @param mixed $cumulative
*
* @return float|string
*/
public static function distributionLeftTail($value, $degrees, $cumulative)
{
$value = Functions::flattenSingleValue($value);
$degrees = Functions::flattenSingleValue($degrees);
$cumulative = Functions::flattenSingleValue($cumulative);
try {
$value = self::validateFloat($value);
$degrees = self::validateInt($degrees);
$cumulative = self::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees < 1) {
return Functions::NAN();
}
if ($value < 0) {
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
return 1;
}
return Functions::NAN();
}
if ($cumulative === true) {
return 1 - self::distributionRightTail($value, $degrees);
}
return (($value ** (($degrees / 2) - 1) * exp(-$value / 2))) /
((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
}
/**
* CHIINV.
*
* Returns the inverse of the right-tailed probability of the chi-squared distribution.
*
* @param mixed (float) $probability Probability for the function
* @param mixed (int) $degrees degrees of freedom
*
* @return float|string
*/
public static function inverse($probability, $degrees)
public static function inverseRightTail($probability, $degrees)
{
$probability = Functions::flattenSingleValue($probability);
$degrees = Functions::flattenSingleValue($degrees);
@ -108,7 +152,7 @@ class ChiSquared
$degrees = self::degrees($rows, $columns);
$result = self::distribution($result, $degrees);
$result = self::distributionRightTail($result, $degrees);
return $result;
}

View File

@ -30,14 +30,15 @@ abstract class GammaBase
$xLo = 0;
$xHi = $alpha * $beta * 5;
$x = $xNew = 1;
$dx = 1024;
$x = $xNew = 1;
$i = 0;
while ((abs($dx) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
// Apply Newton-Raphson step
$result = self::calculateDistribution($x, $alpha, $beta, true);
$error = $result - $probability;
if ($error == 0.0) {
$dx = 0;
} elseif ($error < 0.0) {

View File

@ -20,8 +20,8 @@ class NewtonRaphson
$xLo = 100;
$xHi = 0;
$x = $xNew = 1;
$dx = 1;
$x = $xNew = 1;
$i = 0;
while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {

View File

@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class ChiInvTest extends TestCase
class ChiDistLeftTailTest extends TestCase
{
protected function setUp(): void
{
@ -14,18 +14,18 @@ class ChiInvTest extends TestCase
}
/**
* @dataProvider providerCHIINV
* @dataProvider providerCHIDIST
*
* @param mixed $expectedResult
*/
public function testCHIINV($expectedResult, ...$args): void
public function testCHIDIST($expectedResult, ...$args): void
{
$result = Statistical::CHIINV(...$args);
$result = Statistical\Distributions\ChiSquared::distributionLeftTail(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerCHIINV()
public function providerCHIDIST()
{
return require 'tests/data/Calculation/Statistical/CHIINV.php';
return require 'tests/data/Calculation/Statistical/CHIDISTLeftTail.php';
}
}

View File

@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class ChiDistTest extends TestCase
class ChiDistRightTailTest extends TestCase
{
protected function setUp(): void
{
@ -26,6 +26,6 @@ class ChiDistTest extends TestCase
public function providerCHIDIST()
{
return require 'tests/data/Calculation/Statistical/CHIDIST.php';
return require 'tests/data/Calculation/Statistical/CHIDISTRightTail.php';
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class ChiInvRightTailTest extends TestCase
{
protected function setUp(): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
}
/**
* @dataProvider providerCHIINV
*
* @param mixed $expectedResult
* @param mixed $probability
* @param mixed $degrees
*/
public function testCHIINV($expectedResult, $probability, $degrees): void
{
$result = Statistical::CHIINV($probability, $degrees);
if (!is_string($expectedResult)) {
$reverse = Statistical\Distributions\ChiSquared::distributionRightTail($result, $degrees);
self::assertEqualsWithDelta($probability, $reverse, 1E-12);
}
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerCHIINV()
{
return require 'tests/data/Calculation/Statistical/CHIINVRightTail.php';
}
}

View File

@ -0,0 +1,64 @@
<?php
return [
[
0.520499877813,
0.5, 1, true,
],
[
0.207553748710,
2, 3, false,
],
[
0.111565080074,
3, 2, false,
],
[
0.776869839852,
3, 2, true,
],
[
0.039646370521,
3, 9, false,
],
[
0.035705027315,
3, 9, true,
],
[
0.103349469094,
7.5, 8, false,
],
[
0.516232618446,
7.5, 8, true,
],
[
0.020666985354,
8, 3, false,
],
[
0.953988294311,
8, 3, true,
],
[
'#VALUE!',
'NaN', 3, true,
],
[
'#VALUE!',
2, 'NaN', true,
],
[
'#VALUE!',
2, 3, 'NaN',
],
'Value < 0' => [
'#NUM!',
-8, 3, true,
],
'Degrees < 1' => [
'#NUM!',
8, 0, true,
],
];

View File

@ -10,13 +10,21 @@ return [
0.75, 10,
],
[
18.30697345702,
0.050001, 10,
0.007716715545,
0.93, 1,
],
[
1.021651247532,
0.6, 2,
],
[
0.45493642312,
0.5, 1,
],
[
4.351460191096,
0.5, 5,
],
[
0.101531044268,
0.75, 1,