Statistical refactoring - Confidence() and Trend() (#1898)

- Move TREND() functions into the Statistical Trends class
- Unit tests for TREND()
- Create Confidence class for Statistical Confidence functions, and the CONFIDENCE() method
This commit is contained in:
Mark Baker 2021-03-04 21:45:56 +01:00 committed by GitHub
parent d2a83b404a
commit a79a4ddbab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 177 additions and 50 deletions

View File

@ -579,12 +579,12 @@ class Calculation
],
'CONFIDENCE' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'CONFIDENCE'],
'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
'argumentCount' => '3',
],
'CONFIDENCE.NORM' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'CONFIDENCE'],
'functionCall' => [Statistical\Confidence::class, 'CONFIDENCE'],
'argumentCount' => '3',
],
'CONFIDENCE.T' => [
@ -2454,7 +2454,7 @@ class Calculation
],
'TREND' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'TREND'],
'functionCall' => [Statistical\Trends::class, 'TREND'],
'argumentCount' => '1-4',
],
'TRIM' => [

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Conditional;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
@ -832,6 +833,11 @@ class Statistical
*
* Returns the confidence interval for a population mean
*
* @Deprecated 1.18.0
*
* @see Statistical\Confidence::CONFIDENCE()
* Use the CONFIDENCE() method in the Statistical\Confidence class instead
*
* @param float $alpha
* @param float $stdDev Standard Deviation
* @param float $size
@ -840,23 +846,7 @@ class Statistical
*/
public static function CONFIDENCE($alpha, $stdDev, $size)
{
$alpha = Functions::flattenSingleValue($alpha);
$stdDev = Functions::flattenSingleValue($stdDev);
$size = Functions::flattenSingleValue($size);
if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
$size = floor($size);
if (($alpha <= 0) || ($alpha >= 1)) {
return Functions::NAN();
}
if (($stdDev <= 0) || ($size < 1)) {
return Functions::NAN();
}
return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
}
return Functions::VALUE();
return Confidence::CONFIDENCE($alpha, $stdDev, $size);
}
/**
@ -2941,6 +2931,11 @@ class Statistical
*
* Returns values along a linear Trend
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::TREND()
* Use the TREND() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
@ -2950,22 +2945,7 @@ class Statistical
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitLinear->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
}
return $returnArray;
return Trends::TREND($yValues, $xValues, $newValues, $const);
}
/**

View File

@ -0,0 +1,41 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
class Confidence
{
/**
* CONFIDENCE.
*
* Returns the confidence interval for a population mean
*
* @param float $alpha
* @param float $stdDev Standard Deviation
* @param float $size
*
* @return float|string
*/
public static function CONFIDENCE($alpha, $stdDev, $size)
{
$alpha = Functions::flattenSingleValue($alpha);
$stdDev = Functions::flattenSingleValue($stdDev);
$size = Functions::flattenSingleValue($size);
if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
$size = floor($size);
if (($alpha <= 0) || ($alpha >= 1)) {
return Functions::NAN();
}
if (($stdDev <= 0) || ($size < 1)) {
return Functions::NAN();
}
return Statistical::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
}
return Functions::VALUE();
}
}

View File

@ -388,4 +388,36 @@ class Trends
return $bestFitLinear->getStdevOfResiduals();
}
/**
* TREND.
*
* Returns values along a linear Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param bool $const a logical value specifying whether to force the intersect to equal 0
*
* @return array of float
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitLinear->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
}
return $returnArray;
}
}

View File

@ -17,10 +17,11 @@ class GrowthTest extends TestCase
* @dataProvider providerGROWTH
*
* @param mixed $expectedResult
* @param mixed $yValues
*/
public function testGROWTH($expectedResult, ...$args): void
public function testGROWTH($expectedResult, $yValues, ...$args): void
{
$result = Statistical::GROWTH(...$args);
$result = Statistical::GROWTH($yValues, ...$args);
self::assertEqualsWithDelta($expectedResult, $result[0], 1E-12);
}

View File

@ -0,0 +1,33 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class TrendTest extends TestCase
{
protected function setUp(): void
{
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
}
/**
* @dataProvider providerGROWTH
*
* @param mixed $expectedResult
* @param mixed $yValues
*/
public function testTREND($expectedResult, $yValues, ...$args): void
{
$result = Statistical::TREND($yValues, ...$args);
self::assertEqualsWithDelta($expectedResult, $result[0], 1E-12);
}
public function providerGROWTH()
{
return require 'tests/data/Calculation/Statistical/TREND.php';
}
}

View File

@ -41,10 +41,11 @@ class FontTest extends TestCase
* @dataProvider providerFontSizeToPixels
*
* @param mixed $expectedResult
* @param mixed $size
*/
public function testFontSizeToPixels($expectedResult, ...$args): void
public function testFontSizeToPixels($expectedResult, $size): void
{
$result = Font::fontSizeToPixels(...$args);
$result = Font::fontSizeToPixels($size);
self::assertEquals($expectedResult, $result);
}
@ -57,10 +58,11 @@ class FontTest extends TestCase
* @dataProvider providerInchSizeToPixels
*
* @param mixed $expectedResult
* @param mixed $size
*/
public function testInchSizeToPixels($expectedResult, ...$args): void
public function testInchSizeToPixels($expectedResult, $size): void
{
$result = Font::inchSizeToPixels(...$args);
$result = Font::inchSizeToPixels($size);
self::assertEquals($expectedResult, $result);
}
@ -73,10 +75,11 @@ class FontTest extends TestCase
* @dataProvider providerCentimeterSizeToPixels
*
* @param mixed $expectedResult
* @param mixed $size
*/
public function testCentimeterSizeToPixels($expectedResult, ...$args): void
public function testCentimeterSizeToPixels($expectedResult, $size): void
{
$result = Font::centimeterSizeToPixels(...$args);
$result = Font::centimeterSizeToPixels($size);
self::assertEquals($expectedResult, $result);
}

View File

@ -11,10 +11,11 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetRed
*
* @param mixed $expectedResult
* @param mixed $color
*/
public function testGetRed($expectedResult, ...$args): void
public function testGetRed($expectedResult, $color, ...$args): void
{
$result = Color::getRed(...$args);
$result = Color::getRed($color, ...$args);
self::assertEquals($expectedResult, $result);
}
@ -27,10 +28,11 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetGreen
*
* @param mixed $expectedResult
* @param mixed $color
*/
public function testGetGreen($expectedResult, ...$args): void
public function testGetGreen($expectedResult, $color, ...$args): void
{
$result = Color::getGreen(...$args);
$result = Color::getGreen($color, ...$args);
self::assertEquals($expectedResult, $result);
}
@ -43,10 +45,11 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetBlue
*
* @param mixed $expectedResult
* @param mixed $color
*/
public function testGetBlue($expectedResult, ...$args): void
public function testGetBlue($expectedResult, $color, ...$args): void
{
$result = Color::getBlue(...$args);
$result = Color::getBlue($color, ...$args);
self::assertEquals($expectedResult, $result);
}

View File

@ -0,0 +1,34 @@
<?php
return [
[
[
[133953.33333333334],
[134971.51515151517],
[135989.69696969696],
[137007.87878787878],
[138026.0606060606],
[139044.24242424243],
[140062.42424242425],
[141080.60606060608],
[142098.78787878787],
[143116.9696969697],
[144135.15151515152],
[145153.33333333334],
],
[133890, 135000, 135790, 137300, 138130, 139100, 139900, 141120, 141890, 143230, 144000, 145290],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
],
[
[
[146171.51515151517],
[147189.69696969696],
[148207.87878787878],
[149226.0606060606],
[150244.24242424243],
],
[133890, 135000, 135790, 137300, 138130, 139100, 139900, 141120, 141890, 143230, 144000, 145290],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17],
],
];