Implemented the CHISQ.DIST() Statistical function. (#1961)
* Implementation of the CHISQ.DIST() statistical function for left tail distribution
This commit is contained in:
parent
aff1d35e4a
commit
e2ff14fe89
|
|
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
### Added
|
### 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)
|
- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
|
|
@ -488,22 +488,22 @@ class Calculation
|
||||||
],
|
],
|
||||||
'CHIDIST' => [
|
'CHIDIST' => [
|
||||||
'category' => Category::CATEGORY_STATISTICAL,
|
'category' => Category::CATEGORY_STATISTICAL,
|
||||||
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'],
|
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'CHISQ.DIST' => [
|
'CHISQ.DIST' => [
|
||||||
'category' => Category::CATEGORY_STATISTICAL,
|
'category' => Category::CATEGORY_STATISTICAL,
|
||||||
'functionCall' => [Functions::class, 'DUMMY'],
|
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionLeftTail'],
|
||||||
'argumentCount' => '3',
|
'argumentCount' => '3',
|
||||||
],
|
],
|
||||||
'CHISQ.DIST.RT' => [
|
'CHISQ.DIST.RT' => [
|
||||||
'category' => Category::CATEGORY_STATISTICAL,
|
'category' => Category::CATEGORY_STATISTICAL,
|
||||||
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'],
|
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distributionRightTail'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'CHIINV' => [
|
'CHIINV' => [
|
||||||
'category' => Category::CATEGORY_STATISTICAL,
|
'category' => Category::CATEGORY_STATISTICAL,
|
||||||
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'],
|
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'CHISQ.INV' => [
|
'CHISQ.INV' => [
|
||||||
|
|
@ -513,7 +513,7 @@ class Calculation
|
||||||
],
|
],
|
||||||
'CHISQ.INV.RT' => [
|
'CHISQ.INV.RT' => [
|
||||||
'category' => Category::CATEGORY_STATISTICAL,
|
'category' => Category::CATEGORY_STATISTICAL,
|
||||||
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'],
|
'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverseRightTail'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'CHITEST' => [
|
'CHITEST' => [
|
||||||
|
|
|
||||||
|
|
@ -297,8 +297,8 @@ class Statistical
|
||||||
*
|
*
|
||||||
* @Deprecated 1.18.0
|
* @Deprecated 1.18.0
|
||||||
*
|
*
|
||||||
* @see Statistical\Distributions\ChiSquared::distribution()
|
* @see Statistical\Distributions\ChiSquared::distributionRightTail()
|
||||||
* Use the distribution() method in the Statistical\Distributions\ChiSquared class instead
|
* Use the distributionRightTail() method in the Statistical\Distributions\ChiSquared class instead
|
||||||
*
|
*
|
||||||
* @param float $value Value for the function
|
* @param float $value Value for the function
|
||||||
* @param float $degrees degrees of freedom
|
* @param float $degrees degrees of freedom
|
||||||
|
|
@ -307,7 +307,7 @@ class Statistical
|
||||||
*/
|
*/
|
||||||
public static function CHIDIST($value, $degrees)
|
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
|
* @Deprecated 1.18.0
|
||||||
*
|
*
|
||||||
* @see Statistical\Distributions\ChiSquared::inverse()
|
* @see Statistical\Distributions\ChiSquared::inverseRightTail()
|
||||||
* Use the inverse() method in the Statistical\Distributions\ChiSquared class instead
|
* Use the inverseRightTail() method in the Statistical\Distributions\ChiSquared class instead
|
||||||
*
|
*
|
||||||
* @param float $probability Probability for the function
|
* @param float $probability Probability for the function
|
||||||
* @param float $degrees degrees of freedom
|
* @param float $degrees degrees of freedom
|
||||||
|
|
@ -327,7 +327,7 @@ class Statistical
|
||||||
*/
|
*/
|
||||||
public static function CHIINV($probability, $degrees)
|
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.
|
* 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
|
* @Deprecated 1.18.0
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class ChiSquared
|
||||||
*
|
*
|
||||||
* @return float|string
|
* @return float|string
|
||||||
*/
|
*/
|
||||||
public static function distribution($value, $degrees)
|
public static function distributionRightTail($value, $degrees)
|
||||||
{
|
{
|
||||||
$value = Functions::flattenSingleValue($value);
|
$value = Functions::flattenSingleValue($value);
|
||||||
$degrees = Functions::flattenSingleValue($degrees);
|
$degrees = Functions::flattenSingleValue($degrees);
|
||||||
|
|
@ -48,16 +48,60 @@ class ChiSquared
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CHIINV.
|
* CHIDIST.
|
||||||
*
|
*
|
||||||
* Returns the one-tailed probability of the chi-squared distribution.
|
* 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 (float) $probability Probability for the function
|
||||||
* @param mixed (int) $degrees degrees of freedom
|
* @param mixed (int) $degrees degrees of freedom
|
||||||
*
|
*
|
||||||
* @return float|string
|
* @return float|string
|
||||||
*/
|
*/
|
||||||
public static function inverse($probability, $degrees)
|
public static function inverseRightTail($probability, $degrees)
|
||||||
{
|
{
|
||||||
$probability = Functions::flattenSingleValue($probability);
|
$probability = Functions::flattenSingleValue($probability);
|
||||||
$degrees = Functions::flattenSingleValue($degrees);
|
$degrees = Functions::flattenSingleValue($degrees);
|
||||||
|
|
@ -108,7 +152,7 @@ class ChiSquared
|
||||||
|
|
||||||
$degrees = self::degrees($rows, $columns);
|
$degrees = self::degrees($rows, $columns);
|
||||||
|
|
||||||
$result = self::distribution($result, $degrees);
|
$result = self::distributionRightTail($result, $degrees);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,15 @@ abstract class GammaBase
|
||||||
$xLo = 0;
|
$xLo = 0;
|
||||||
$xHi = $alpha * $beta * 5;
|
$xHi = $alpha * $beta * 5;
|
||||||
|
|
||||||
$x = $xNew = 1;
|
|
||||||
$dx = 1024;
|
$dx = 1024;
|
||||||
|
$x = $xNew = 1;
|
||||||
$i = 0;
|
$i = 0;
|
||||||
|
|
||||||
while ((abs($dx) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
|
while ((abs($dx) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
|
||||||
// Apply Newton-Raphson step
|
// Apply Newton-Raphson step
|
||||||
$result = self::calculateDistribution($x, $alpha, $beta, true);
|
$result = self::calculateDistribution($x, $alpha, $beta, true);
|
||||||
$error = $result - $probability;
|
$error = $result - $probability;
|
||||||
|
|
||||||
if ($error == 0.0) {
|
if ($error == 0.0) {
|
||||||
$dx = 0;
|
$dx = 0;
|
||||||
} elseif ($error < 0.0) {
|
} elseif ($error < 0.0) {
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ class NewtonRaphson
|
||||||
$xLo = 100;
|
$xLo = 100;
|
||||||
$xHi = 0;
|
$xHi = 0;
|
||||||
|
|
||||||
$x = $xNew = 1;
|
|
||||||
$dx = 1;
|
$dx = 1;
|
||||||
|
$x = $xNew = 1;
|
||||||
$i = 0;
|
$i = 0;
|
||||||
|
|
||||||
while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
|
while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ChiInvTest extends TestCase
|
class ChiDistLeftTailTest extends TestCase
|
||||||
{
|
{
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
|
|
@ -14,18 +14,18 @@ class ChiInvTest extends TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider providerCHIINV
|
* @dataProvider providerCHIDIST
|
||||||
*
|
*
|
||||||
* @param mixed $expectedResult
|
* @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);
|
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';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ChiDistTest extends TestCase
|
class ChiDistRightTailTest extends TestCase
|
||||||
{
|
{
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
|
|
@ -26,6 +26,6 @@ class ChiDistTest extends TestCase
|
||||||
|
|
||||||
public function providerCHIDIST()
|
public function providerCHIDIST()
|
||||||
{
|
{
|
||||||
return require 'tests/data/Calculation/Statistical/CHIDIST.php';
|
return require 'tests/data/Calculation/Statistical/CHIDISTRightTail.php';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
@ -10,13 +10,21 @@ return [
|
||||||
0.75, 10,
|
0.75, 10,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
18.30697345702,
|
0.007716715545,
|
||||||
0.050001, 10,
|
0.93, 1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.021651247532,
|
||||||
|
0.6, 2,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0.45493642312,
|
0.45493642312,
|
||||||
0.5, 1,
|
0.5, 1,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
4.351460191096,
|
||||||
|
0.5, 5,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
0.101531044268,
|
0.101531044268,
|
||||||
0.75, 1,
|
0.75, 1,
|
||||||
Loading…
Reference in New Issue