Converting Statistical functions to be array-enabled (phase #2) (#2593)

This commit is contained in:
Mark Baker 2022-02-15 20:34:39 +01:00 committed by GitHub
parent f1cb75eaec
commit cdbe5c7c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 352 additions and 39 deletions

View File

@ -1155,7 +1155,7 @@ class Statistical
* @param mixed $stdDev Standard Deviation
* @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function NORMDIST($value, $mean, $stdDev, $cumulative)
{
@ -1176,7 +1176,7 @@ class Statistical
* @param mixed $mean Mean Value
* @param mixed $stdDev Standard Deviation
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function NORMINV($probability, $mean, $stdDev)
{
@ -1197,7 +1197,7 @@ class Statistical
*
* @param mixed $value
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function NORMSDIST($value)
{
@ -1219,7 +1219,7 @@ class Statistical
* @param mixed $value
* @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function NORMSDIST2($value, $cumulative)
{
@ -1238,7 +1238,7 @@ class Statistical
*
* @param mixed $value
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function NORMSINV($value)
{
@ -1331,7 +1331,7 @@ class Statistical
* @param mixed $mean Mean Value
* @param mixed $cumulative
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function POISSON($value, $mean, $cumulative)
{
@ -1610,7 +1610,7 @@ class Statistical
* @param float $degrees degrees of freedom
* @param float $tails number of tails (1 or 2)
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function TDIST($value, $degrees, $tails)
{
@ -1630,7 +1630,7 @@ class Statistical
* @param float $probability Probability for the function
* @param float $degrees degrees of freedom
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
*/
public static function TINV($probability, $degrees)
{
@ -1787,7 +1787,7 @@ class Statistical
* @param float $beta Beta Parameter
* @param bool $cumulative
*
* @return float|string (string if result is an error)
* @return array|float|string (string if result is an error)
*/
public static function WEIBULL($value, $alpha, $beta, $cumulative)
{

View File

@ -2,12 +2,15 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Normal
{
use ArrayEnabled;
public const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
/**
@ -18,17 +21,23 @@ class Normal
* testing.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $mean Mean value as a float
* Or can be an array of values
* @param mixed $stdDev Standard Deviation as a float
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $mean, $stdDev, $cumulative)
{
$value = Functions::flattenSingleValue($value);
$mean = Functions::flattenSingleValue($mean);
$stdDev = Functions::flattenSingleValue($stdDev);
if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
@ -56,16 +65,21 @@ class Normal
* Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
*
* @param mixed $probability Float probability for which we want the value
* Or can be an array of values
* @param mixed $mean Mean Value as a float
* Or can be an array of values
* @param mixed $stdDev Standard Deviation as a float
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $mean, $stdDev)
{
$probability = Functions::flattenSingleValue($probability);
$mean = Functions::flattenSingleValue($mean);
$stdDev = Functions::flattenSingleValue($stdDev);
if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
}
try {
$probability = DistributionValidations::validateProbability($probability);

View File

@ -2,12 +2,15 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class Poisson
{
use ArrayEnabled;
/**
* POISSON.
*
@ -16,15 +19,21 @@ class Poisson
* cars arriving at a toll plaza in 1 minute.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $mean Mean value as a float
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $mean, $cumulative)
{
$value = Functions::flattenSingleValue($value);
$mean = Functions::flattenSingleValue($mean);
if (is_array($value) || is_array($mean) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);

View File

@ -18,9 +18,16 @@ class StandardNormal
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
* @param mixed $value Float value for which we want the probability
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::distribution()
* All we need to do is pass the value through as scalar or as array.
*
* @return float|string The result, or a string containing an error
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function cumulative($value)
{
@ -34,10 +41,18 @@ class StandardNormal
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
* @param mixed $value Float value for which we want the probability
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::distribution()
* All we need to do is pass the value and cumulative through as scalar or as array.
*
* @return float|string The result, or a string containing an error
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $cumulative)
{
@ -49,9 +64,16 @@ class StandardNormal
*
* Returns the inverse of the standard normal cumulative distribution
*
* @param mixed $value Float probability for which we want the value
* @param mixed $value float probability for which we want the value
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::inverse()
* All we need to do is pass the value through as scalar or as array
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($value)
{

View File

@ -2,11 +2,14 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class StudentT
{
use ArrayEnabled;
private const MAX_ITERATIONS = 256;
/**
@ -15,16 +18,21 @@ class StudentT
* Returns the probability of Student's T distribution.
*
* @param mixed $value Float value for the distribution
* Or can be an array of values
* @param mixed $degrees Integer value for degrees of freedom
* Or can be an array of values
* @param mixed $tails Integer value for the number of tails (1 or 2)
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $degrees, $tails)
{
$value = Functions::flattenSingleValue($value);
$degrees = Functions::flattenSingleValue($degrees);
$tails = Functions::flattenSingleValue($tails);
if (is_array($value) || is_array($degrees) || is_array($tails)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $tails);
}
try {
$value = DistributionValidations::validateFloat($value);
@ -47,14 +55,19 @@ class StudentT
* Returns the one-tailed probability of the chi-squared distribution.
*
* @param mixed $probability Float probability for the function
* Or can be an array of values
* @param mixed $degrees Integer value for degrees of freedom
* Or can be an array of values
*
* @return float|string The result, or a string containing an error
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $degrees)
{
$probability = Functions::flattenSingleValue($probability);
$degrees = Functions::flattenSingleValue($degrees);
if (is_array($probability) || is_array($degrees)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
}
try {
$probability = DistributionValidations::validateProbability($probability);

View File

@ -2,11 +2,14 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Weibull
{
use ArrayEnabled;
/**
* WEIBULL.
*
@ -14,18 +17,23 @@ class Weibull
* analysis, such as calculating a device's mean time to failure.
*
* @param mixed $value Float value for the distribution
* Or can be an array of values
* @param mixed $alpha Float alpha Parameter
* Or can be an array of values
* @param mixed $beta Float beta Parameter
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return float|string (string if result is an error)
* @return array|float|string (string if result is an error)
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $alpha, $beta, $cumulative)
{
$value = Functions::flattenSingleValue($value);
$alpha = Functions::flattenSingleValue($alpha);
$beta = Functions::flattenSingleValue($beta);
$cumulative = Functions::flattenSingleValue($cumulative);
if (is_array($value) || is_array($alpha) || is_array($beta) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $alpha, $beta, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -22,4 +23,32 @@ class NormDistTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/NORMDIST.php';
}
/**
* @dataProvider providerNormDistArray
*/
public function testNormDistArray(array $expectedResult, string $values, string $mean, string $stdDev): void
{
$calculation = Calculation::getInstance();
$formula = "=NORMDIST({$values}, {$mean}, {$stdDev}, false)";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerNormDistArray(): array
{
return [
'row/column vectors' => [
[
[0.04324582990797181, 0.03549422283581691, 0.026885636057682592],
[0.07365402806066465, 0.038837210996642585, 0.015790031660178828],
[0.12098536225957167, 0.0022159242059690033, 7.991870553452737E-6],
],
'12',
'{10, 6, 3}',
'{9; 5; 2}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -22,4 +23,32 @@ class NormInvTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/NORMINV.php';
}
/**
* @dataProvider providerNormInvArray
*/
public function testNormInvArray(array $expectedResult, string $probabilities, string $mean, string $stdDev): void
{
$calculation = Calculation::getInstance();
$formula = "=NORMINV({$probabilities}, {$mean}, {$stdDev})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerNormInvArray(): array
{
return [
'row/column vectors' => [
[
[2.651020499553155, 4.651020499553155],
[1.9765307493297324, 3.9765307493297324],
[-0.7214282515639576, 1.2785717484360424],
],
'0.25',
'{4, 6}',
'{2; 3; 7}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -22,4 +23,29 @@ class NormSDist2Test extends TestCase
{
return require 'tests/data/Calculation/Statistical/NORMSDIST2.php';
}
/**
* @dataProvider providerNormSDist2Array
*/
public function testNormSDist2Array(array $expectedResult, string $values): void
{
$calculation = Calculation::getInstance();
$formula = "=NORM.S.DIST({$values}, true)";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerNormSDist2Array(): array
{
return [
'row/column vectors' => [
[
[0.3085375387259869, 0.7733726476231317],
[0.99865010196837, 1.0],
],
'{-0.5, 0.75; 3, 12.5}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -23,4 +24,29 @@ class NormSDistTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/NORMSDIST.php';
}
/**
* @dataProvider providerNormSDistArray
*/
public function testNormSDistArray(array $expectedResult, string $values): void
{
$calculation = Calculation::getInstance();
$formula = "=NORMSDIST({$values})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerNormSDistArray(): array
{
return [
'row/column vectors' => [
[
[0.3085375387259869, 0.7733726476231317],
[0.99865010196837, 1.0],
],
'{-0.5, 0.75; 3, 12.5}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -23,4 +24,29 @@ class NormSInvTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/NORMSINV.php';
}
/**
* @dataProvider providerNormSInvArray
*/
public function testNormSInvArray(array $expectedResult, string $probabilities): void
{
$calculation = Calculation::getInstance();
$formula = "=NORMSINV({$probabilities})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerNormSInvArray(): array
{
return [
'row/column vectors' => [
[
['#NUM!', -1.959963986120195],
[-0.3853204662702544, 0.6744897502234225],
],
'{-0.75, 0.025; 0.35, 0.75}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -28,4 +29,31 @@ class PoissonTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/POISSON.php';
}
/**
* @dataProvider providerPoissonArray
*/
public function testPoissonArray(array $expectedResult, string $values, string $mean): void
{
$calculation = Calculation::getInstance();
$formula = "=POISSON({$values}, {$mean}, false)";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerPoissonArray(): array
{
return [
'row/column vectors' => [
[
[0.4749316557209494, 0.41547307344655265, 0.028388126533408903],
[0.3081373033023279, 0.3299417281086086, 0.0867439330307142],
[0.14758417287196898, 0.19139299302082188, 0.1804470443154836],
],
'{0.125, 0.5, 3}',
'{0.7; 1.2; 2}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -25,4 +26,31 @@ class TDistTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/TDIST.php';
}
/**
* @dataProvider providerTDistArray
*/
public function testTDistArray(array $expectedResult, string $values, string $degrees, string $tails): void
{
$calculation = Calculation::getInstance();
$formula = "=TDIST({$values}, {$degrees}, {$tails})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerTDistArray(): array
{
return [
'row/column vectors' => [
[
[0.020259663176916964, 0.06966298427942164, 0.040258118978631297],
[0.04051932635383393, 0.13932596855884327, 0.08051623795726259],
],
'2',
'{1.5, 3.5, 8}',
'{1; 2}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -24,4 +25,29 @@ class TinvTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/TINV.php';
}
/**
* @dataProvider providerTInvArray
*/
public function testTInvArray(array $expectedResult, string $values, string $degrees): void
{
$calculation = Calculation::getInstance();
$formula = "=TINV({$values}, {$degrees})";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerTInvArray(): array
{
return [
'row vector' => [
[
[0.29001075058679815, 0.5023133547575189, 0.4713169827948964],
],
'0.65',
'{1.5, 3.5, 8}',
],
];
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
@ -26,4 +27,32 @@ class WeibullTest extends TestCase
{
return require 'tests/data/Calculation/Statistical/WEIBULL.php';
}
/**
* @dataProvider providerWeibullArray
*/
public function testWeibullArray(array $expectedResult, string $values, string $alpha, string $beta): void
{
$calculation = Calculation::getInstance();
$formula = "=WEIBULL({$values}, {$alpha}, {$beta}, false)";
$result = $calculation->_calculateFormulaValue($formula);
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-14);
}
public function providerWeibullArray(): array
{
return [
'row/column vectors' => [
[
[0.18393972058572117, 0.36787944117144233, 0.9196986029286058],
[0.15163266492815836, 0.19470019576785122, 0.07572134644346439],
[0.13406400920712788, 0.1363430062345938, 0.0253391936076857],
],
'2',
'{1, 2, 5}',
'{2; 4; 5}',
],
];
}
}