Move the trend functions from Statistical and into their own group class (#1890)

* Move the trend functions from Statistical and into their own group class
* Additional LINEST()/LOGEST() tests, and fix for the returned array
This commit is contained in:
Mark Baker 2021-03-03 12:51:50 +01:00 committed by GitHub
parent 04e7c30758
commit 70e371189c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 470 additions and 239 deletions

View File

@ -599,7 +599,7 @@ class Calculation
],
'CORREL' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'CORREL'],
'functionCall' => [Statistical\Trends::class, 'CORREL'],
'argumentCount' => '2',
],
'COS' => [
@ -679,12 +679,12 @@ class Calculation
],
'COVAR' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'COVAR'],
'functionCall' => [Statistical\Trends::class, 'COVAR'],
'argumentCount' => '2',
],
'COVARIANCE.P' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'COVAR'],
'functionCall' => [Statistical\Trends::class, 'COVAR'],
'argumentCount' => '2',
],
'COVARIANCE.S' => [
@ -1084,7 +1084,7 @@ class Calculation
],
'FORECAST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'FORECAST'],
'functionCall' => [Statistical\Trends::class, 'FORECAST'],
'argumentCount' => '3',
],
'FORECAST.ETS' => [
@ -1109,7 +1109,7 @@ class Calculation
],
'FORECAST.LINEAR' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'FORECAST'],
'functionCall' => [Statistical\Trends::class, 'FORECAST'],
'argumentCount' => '3',
],
'FORMULATEXT' => [
@ -1423,7 +1423,7 @@ class Calculation
],
'INTERCEPT' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'INTERCEPT'],
'functionCall' => [Statistical\Trends::class, 'INTERCEPT'],
'argumentCount' => '2',
],
'INTRATE' => [
@ -1560,7 +1560,7 @@ class Calculation
],
'LINEST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'LINEST'],
'functionCall' => [Statistical\Trends::class, 'LINEST'],
'argumentCount' => '1-4',
],
'LN' => [
@ -1580,7 +1580,7 @@ class Calculation
],
'LOGEST' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'LOGEST'],
'functionCall' => [Statistical\Trends::class, 'LOGEST'],
'argumentCount' => '1-4',
],
'LOGINV' => [
@ -2143,7 +2143,7 @@ class Calculation
],
'RSQ' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'RSQ'],
'functionCall' => [Statistical\Trends::class, 'RSQ'],
'argumentCount' => '2',
],
'RTD' => [
@ -2228,7 +2228,7 @@ class Calculation
],
'SLOPE' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'SLOPE'],
'functionCall' => [Statistical\Trends::class, 'SLOPE'],
'argumentCount' => '2',
],
'SMALL' => [
@ -2293,7 +2293,7 @@ class Calculation
],
'STEYX' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical::class, 'STEYX'],
'functionCall' => [Statistical\Trends::class, 'STEYX'],
'argumentCount' => '2',
],
'SUBSTITUTE' => [

View File

@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Permutations;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
@ -21,33 +22,6 @@ class Statistical
const MAX_ITERATIONS = 256;
const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
private static function checkTrendArrays(&$array1, &$array2)
{
if (!is_array($array1)) {
$array1 = [$array1];
}
if (!is_array($array2)) {
$array2 = [$array2];
}
$array1 = Functions::flattenArray($array1);
$array2 = Functions::flattenArray($array2);
foreach ($array1 as $key => $value) {
if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
unset($array1[$key], $array2[$key]);
}
}
foreach ($array2 as $key => $value) {
if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
unset($array1[$key], $array2[$key]);
}
}
$array1 = array_merge($array1);
$array2 = array_merge($array2);
return true;
}
/**
* Incomplete beta function.
*
@ -890,6 +864,11 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::CORREL()
* Use the CORREL() method in the Statistical\Trends class instead
*
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
@ -897,24 +876,7 @@ class Statistical
*/
public static function CORREL($yValues, $xValues = null)
{
if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
return Functions::VALUE();
}
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCorrelation();
return Trends::CORREL($xValues, $yValues);
}
/**
@ -1033,6 +995,11 @@ class Statistical
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::COVAR()
* Use the COVAR() method in the Statistical\Trends class instead
*
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
@ -1040,21 +1007,7 @@ class Statistical
*/
public static function COVAR($yValues, $xValues)
{
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCovariance();
return Trends::COVAR($yValues, $xValues);
}
/**
@ -1380,6 +1333,11 @@ class Statistical
*
* Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::FORECAST()
* Use the FORECAST() method in the Statistical\Trends class instead
*
* @param float $xValue Value of X for which we want to find Y
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
@ -1388,24 +1346,7 @@ class Statistical
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
$xValue = Functions::flattenSingleValue($xValue);
if (!is_numeric($xValue)) {
return Functions::VALUE();
} elseif (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getValueOfYForX($xValue);
return Trends::FORECAST($xValue, $yValues, $xValues);
}
/**
@ -1722,6 +1663,11 @@ class Statistical
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::INTERCEPT()
* Use the INTERCEPT() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@ -1729,21 +1675,7 @@ class Statistical
*/
public static function INTERCEPT($yValues, $xValues)
{
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getIntersect();
return Trends::INTERCEPT($yValues, $xValues);
}
/**
@ -1838,6 +1770,11 @@ class Statistical
* Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
* and then returns an array that describes the line.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::LINEST()
* Use the LINEST() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
@ -1847,48 +1784,7 @@ class Statistical
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = range(1, count(Functions::flattenArray($yValues)));
}
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return 0;
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if ($stats) {
return [
[
$bestFitLinear->getSlope(),
$bestFitLinear->getSlopeSE(),
$bestFitLinear->getGoodnessOfFit(),
$bestFitLinear->getF(),
$bestFitLinear->getSSRegression(),
],
[
$bestFitLinear->getIntersect(),
$bestFitLinear->getIntersectSE(),
$bestFitLinear->getStdevOfResiduals(),
$bestFitLinear->getDFResiduals(),
$bestFitLinear->getSSResiduals(),
],
];
}
return [
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
];
return Trends::LINEST($yValues, $xValues, $const, $stats);
}
/**
@ -1897,6 +1793,11 @@ class Statistical
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::LOGEST()
* Use the LOGEST() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
@ -1906,54 +1807,7 @@ class Statistical
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = range(1, count(Functions::flattenArray($yValues)));
}
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
foreach ($yValues as $value) {
if ($value <= 0.0) {
return Functions::NAN();
}
}
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return 1;
}
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if ($stats) {
return [
[
$bestFitExponential->getSlope(),
$bestFitExponential->getSlopeSE(),
$bestFitExponential->getGoodnessOfFit(),
$bestFitExponential->getF(),
$bestFitExponential->getSSRegression(),
],
[
$bestFitExponential->getIntersect(),
$bestFitExponential->getIntersectSE(),
$bestFitExponential->getStdevOfResiduals(),
$bestFitExponential->getDFResiduals(),
$bestFitExponential->getSSResiduals(),
],
];
}
return [
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
];
return Trends::LOGEST($yValues, $xValues, $const, $stats);
}
/**
@ -2711,6 +2565,11 @@ class Statistical
*
* Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::RSQ()
* Use the RSQ() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@ -2718,21 +2577,7 @@ class Statistical
*/
public static function RSQ($yValues, $xValues)
{
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getGoodnessOfFit();
return Trends::RSQ($yValues, $xValues);
}
/**
@ -2784,6 +2629,11 @@ class Statistical
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::SLOPE()
* Use the SLOPE() method in the Statistical\Trends class instead
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
@ -2791,21 +2641,7 @@ class Statistical
*/
public static function SLOPE($yValues, $xValues)
{
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getSlope();
return Trends::SLOPE($yValues, $xValues);
}
/**
@ -2971,6 +2807,11 @@ class Statistical
/**
* STEYX.
*
* @Deprecated 1.18.0
*
* @see Statistical\Trends::STEYX()
* Use the STEYX() method in the Statistical\Trends class instead
*
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
@ -2980,21 +2821,7 @@ class Statistical
*/
public static function STEYX($yValues, $xValues)
{
if (!self::checkTrendArrays($yValues, $xValues)) {
return Functions::VALUE();
}
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
return Functions::NA();
} elseif ($yValueCount == 1) {
return Functions::DIV0();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getStdevOfResiduals();
return Trends::STEYX($yValues, $xValues);
}
/**

View File

@ -0,0 +1,359 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
class Trends
{
private static function filterTrendValues(array &$array1, array &$array2): void
{
foreach ($array1 as $key => $value) {
if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
unset($array1[$key], $array2[$key]);
}
}
}
private static function checkTrendArrays(&$array1, &$array2): void
{
if (!is_array($array1)) {
$array1 = [$array1];
}
if (!is_array($array2)) {
$array2 = [$array2];
}
$array1 = Functions::flattenArray($array1);
$array2 = Functions::flattenArray($array2);
self::filterTrendValues($array1, $array2);
self::filterTrendValues($array2, $array1);
// Reset the array indexes
$array1 = array_merge($array1);
$array2 = array_merge($array2);
}
protected static function validateTrendArrays(array $yValues, array $xValues): void
{
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
throw new Exception(Functions::NA());
} elseif ($yValueCount === 1) {
throw new Exception(Functions::DIV0());
}
}
/**
* CORREL.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function CORREL($yValues, $xValues = null)
{
if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
return Functions::VALUE();
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCorrelation();
}
/**
* COVAR.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function COVAR($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCovariance();
}
/**
* FORECAST.
*
* Calculates, or predicts, a future value by using existing values.
* The predicted value is a y-value for a given x-value.
*
* @param float $xValue Value of X for which we want to find Y
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
*
* @return bool|float|string
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
$xValue = Functions::flattenSingleValue($xValue);
if (!is_numeric($xValue)) {
return Functions::VALUE();
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getValueOfYForX($xValue);
}
/**
* INTERCEPT.
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function INTERCEPT($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getIntersect();
}
/**
* LINEST.
*
* Calculates the statistics for a line by using the "least squares" method to calculate a straight line
* that best fits your data, and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
],
[
$bestFitLinear->getSlopeSE(),
$bestFitLinear->getIntersectSE(),
],
[
$bestFitLinear->getGoodnessOfFit(),
$bestFitLinear->getStdevOfResiduals(),
],
[
$bestFitLinear->getF(),
$bestFitLinear->getDFResiduals(),
],
[
$bestFitLinear->getSSRegression(),
$bestFitLinear->getSSResiduals(),
],
];
}
return [
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
];
}
/**
* LOGEST.
*
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param bool $const a logical value specifying whether to force the intersect to equal 0
* @param bool $stats a logical value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
foreach ($yValues as $value) {
if ($value < 0.0) {
return Functions::NAN();
}
}
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
],
[
$bestFitExponential->getSlopeSE(),
$bestFitExponential->getIntersectSE(),
],
[
$bestFitExponential->getGoodnessOfFit(),
$bestFitExponential->getStdevOfResiduals(),
],
[
$bestFitExponential->getF(),
$bestFitExponential->getDFResiduals(),
],
[
$bestFitExponential->getSSRegression(),
$bestFitExponential->getSSResiduals(),
],
];
}
return [
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
];
}
/**
* RSQ.
*
* Returns the square of the Pearson product moment correlation coefficient through data points
* in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function RSQ($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getGoodnessOfFit();
}
/**
* SLOPE.
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function SLOPE($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getSlope();
}
/**
* STEYX.
*
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function STEYX($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getStdevOfResiduals();
}
}

View File

@ -8,4 +8,42 @@ return [
true,
false,
],
[
[
[56.837944664032, 11704.347826086974],
[54.403741747077, 131988.39973671117],
[0.108159322123, 13123.598915116556],
[1.091488562082, 9],
[187985818.1818185, 1550059636.363636],
],
[142000, 144000, 151000, 150000, 139000, 169000, 126000, 142900, 163000, 169000, 149000],
[2310, 2333, 2356, 2379, 2402, 2425, 2448, 2471, 2494, 2517, 2540],
true,
true,
],
// [
// [
// [-234.2371645, 2553.21066, 12529.76817, 27.64138737, 52317.83051],
// [13.26801148, 530.6691519, 400.0668382, 5.429374042, 12237.3616],
// [0.996747993, 970.5784629, '#N/A', '#N/A', '#N/A'],
// [459.7536742, 6, '#N/A', '#N/A', '#N/A'],
// [1732393319, 5652135.316, '#N/A', '#N/A', '#N/A'],
// ],
// [142000, 144000, 151000, 150000, 139000, 169000, 126000, 142900, 163000, 169000, 149000],
// [
// [2310, 2, 2, 20],
// [2333, 2, 2, 12],
// [2356, 3, 1.5, 33],
// [2379, 3, 2, 43],
// [2402, 2, 3, 53],
// [2425, 4, 2, 23],
// [2448, 2, 1.5, 99],
// [2471, 2, 2, 34],
// [2494, 3, 3, 23],
// [2517, 4, 4, 55],
// [2540, 2, 3, 22],
// ],
// true,
// true,
// ],
];

View File

@ -8,4 +8,11 @@ return [
true,
false,
],
[
[1.482939830687, 2.257475168225],
[2, 3, 6, 8, 12, 15, 25, 34, 50],
[0, 1, 2, 3, 4, 5, 6, 7, 8],
true,
false,
],
];