Start work on refactoring the last of the Excel Statistical functions (#2033)
* Refactoring the last of the Excel Statistical functions
This commit is contained in:
parent
160ae59751
commit
e4973fa041
|
|
@ -1085,11 +1085,6 @@ parameters:
|
|||
count: 1
|
||||
path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
|
||||
|
||||
-
|
||||
message: "#^Binary operation \"\\-\" between float\\|int and float\\|string results in an error\\.$#"
|
||||
count: 4
|
||||
path: src/PhpSpreadsheet/Calculation/Statistical.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\:\\:MAXIFS\\(\\) should return float but returns float\\|string\\|null\\.$#"
|
||||
count: 1
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ class Calculation
|
|||
],
|
||||
'DEVSQ' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'DEVSQ'],
|
||||
'functionCall' => [Statistical\Deviations::class, 'sumSquares'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'DGET' => [
|
||||
|
|
@ -1185,7 +1185,7 @@ class Calculation
|
|||
],
|
||||
'GAUSS' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'GAUSS'],
|
||||
'functionCall' => [Statistical\Distributions\StandardNormal::class, 'gauss'],
|
||||
'argumentCount' => '1',
|
||||
],
|
||||
'GCD' => [
|
||||
|
|
@ -1195,7 +1195,7 @@ class Calculation
|
|||
],
|
||||
'GEOMEAN' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'GEOMEAN'],
|
||||
'functionCall' => [Statistical\Averages\Mean::class, 'geometric'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'GESTEP' => [
|
||||
|
|
@ -1215,7 +1215,7 @@ class Calculation
|
|||
],
|
||||
'HARMEAN' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'HARMEAN'],
|
||||
'functionCall' => [Statistical\Averages\Mean::class, 'harmonic'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'HEX2BIN' => [
|
||||
|
|
@ -1529,12 +1529,12 @@ class Calculation
|
|||
],
|
||||
'KURT' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'KURT'],
|
||||
'functionCall' => [Statistical\Deviations::class, 'kurtosis'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'LARGE' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'LARGE'],
|
||||
'functionCall' => [Statistical\Size::class, 'large'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'LCM' => [
|
||||
|
|
@ -2071,7 +2071,7 @@ class Calculation
|
|||
],
|
||||
'RANK.EQ' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'RANK'],
|
||||
'functionCall' => [Statistical\Percentiles::class, 'RANK'],
|
||||
'argumentCount' => '2,3',
|
||||
],
|
||||
'RATE' => [
|
||||
|
|
@ -2218,7 +2218,7 @@ class Calculation
|
|||
],
|
||||
'SKEW' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'SKEW'],
|
||||
'functionCall' => [Statistical\Deviations::class, 'skew'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'SKEW.P' => [
|
||||
|
|
@ -2238,7 +2238,7 @@ class Calculation
|
|||
],
|
||||
'SMALL' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'SMALL'],
|
||||
'functionCall' => [Statistical\Size::class, 'small'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'SORT' => [
|
||||
|
|
@ -2263,7 +2263,7 @@ class Calculation
|
|||
],
|
||||
'STANDARDIZE' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'STANDARDIZE'],
|
||||
'functionCall' => [Statistical\Standardize::class, 'execute'],
|
||||
'argumentCount' => '3',
|
||||
],
|
||||
'STDEV' => [
|
||||
|
|
@ -2288,12 +2288,12 @@ class Calculation
|
|||
],
|
||||
'STDEVP' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'STDEVP'],
|
||||
'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'STDEVPA' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'STDEVPA'],
|
||||
'functionCall' => [Statistical\StandardDeviations::class, 'STDEVPA'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'STEYX' => [
|
||||
|
|
@ -2469,7 +2469,7 @@ class Calculation
|
|||
],
|
||||
'TRIMMEAN' => [
|
||||
'category' => Category::CATEGORY_STATISTICAL,
|
||||
'functionCall' => [Statistical::class, 'TRIMMEAN'],
|
||||
'functionCall' => [Statistical\Averages\Mean::class, 'trim'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'TRUE' => [
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
|
|||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
|
||||
|
||||
/**
|
||||
* @deprecated 1.18.0
|
||||
*/
|
||||
class Statistical
|
||||
{
|
||||
const LOG_GAMMA_X_MAX_VALUE = 2.55e305;
|
||||
const EPS = 2.22e-16;
|
||||
const MAX_VALUE = 1.2e308;
|
||||
const MAX_ITERATIONS = 256;
|
||||
const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
|
||||
|
||||
/**
|
||||
|
|
@ -428,48 +430,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* DEVSQ(value1[,value2[, ...]])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Deviations::sumSquares()
|
||||
* Use the sumSquares() method in the Statistical\Deviations class instead
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DEVSQ(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
|
||||
// Return value
|
||||
$returnValue = null;
|
||||
|
||||
$aMean = Averages::average($aArgs);
|
||||
if ($aMean != Functions::DIV0()) {
|
||||
$aCount = -1;
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
// Is it a numeric value?
|
||||
if (
|
||||
(is_bool($arg)) &&
|
||||
((!Functions::isCellValue($k)) ||
|
||||
(Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
|
||||
) {
|
||||
$arg = (int) $arg;
|
||||
}
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
if ($returnValue === null) {
|
||||
$returnValue = ($arg - $aMean) ** 2;
|
||||
} else {
|
||||
$returnValue += ($arg - $aMean) ** 2;
|
||||
}
|
||||
++$aCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
if ($returnValue === null) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
return Functions::NA();
|
||||
return Statistical\Deviations::sumSquares(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -671,18 +643,18 @@ class Statistical
|
|||
* Calculates the probability that a member of a standard normal population will fall between
|
||||
* the mean and z standard deviations from the mean.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Distributions\StandardNormal::gauss()
|
||||
* Use the gauss() method in the Statistical\Distributions\StandardNormal class instead
|
||||
*
|
||||
* @param float $value
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function GAUSS($value)
|
||||
{
|
||||
$value = Functions::flattenSingleValue($value);
|
||||
if (!is_numeric($value)) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
return Statistical\Distributions\Normal::distribution($value, 0, 1, true) - 0.5;
|
||||
return Statistical\Distributions\StandardNormal::gauss($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -695,23 +667,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* GEOMEAN(value1[,value2[, ...]])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Averages\Mean::geometric()
|
||||
* Use the geometric() method in the Statistical\Averages\Mean class instead
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function GEOMEAN(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
$aMean = MathTrig\Product::evaluate($aArgs);
|
||||
if (is_numeric($aMean) && ($aMean > 0)) {
|
||||
$aCount = Counts::COUNT($aArgs);
|
||||
if (Minimum::MIN($aArgs) > 0) {
|
||||
return $aMean ** (1 / $aCount);
|
||||
}
|
||||
}
|
||||
|
||||
return Functions::NAN();
|
||||
return Statistical\Averages\Mean::geometric(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -745,38 +712,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* HARMEAN(value1[,value2[, ...]])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Averages\Mean::harmonic()
|
||||
* Use the harmonic() method in the Statistical\Averages\Mean class instead
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function HARMEAN(...$args)
|
||||
{
|
||||
// Return value
|
||||
$returnValue = 0;
|
||||
|
||||
// Loop through arguments
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
if (Minimum::MIN($aArgs) < 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$aCount = 0;
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
if ($arg <= 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$returnValue += (1 / $arg);
|
||||
++$aCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
if ($aCount > 0) {
|
||||
return 1 / ($returnValue / $aCount);
|
||||
}
|
||||
|
||||
return Functions::NA();
|
||||
return Statistical\Averages\Mean::harmonic(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -835,40 +782,18 @@ class Statistical
|
|||
* kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
|
||||
* relatively flat distribution.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Deviations::kurtosis()
|
||||
* Use the kurtosis() method in the Statistical\Deviations class instead
|
||||
*
|
||||
* @param array ...$args Data Series
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function KURT(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
$mean = Averages::average($aArgs);
|
||||
$stdDev = StandardDeviations::STDEV($aArgs);
|
||||
|
||||
if ($stdDev > 0) {
|
||||
$count = $summer = 0;
|
||||
// Loop through arguments
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
if (
|
||||
(is_bool($arg)) &&
|
||||
(!Functions::isMatrixValue($k))
|
||||
) {
|
||||
} else {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$summer += (($arg - $mean) / $stdDev) ** 4;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
if ($count > 3) {
|
||||
return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 / (($count - 2) * ($count - 3)));
|
||||
}
|
||||
}
|
||||
|
||||
return Functions::DIV0();
|
||||
return Statistical\Deviations::kurtosis(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -880,37 +805,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* LARGE(value1[,value2[, ...]],entry)
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Size::large()
|
||||
* Use the large() method in the Statistical\Size class instead
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function LARGE(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
$entry = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($entry)) && (!is_string($entry))) {
|
||||
$entry = (int) floor($entry);
|
||||
|
||||
// Calculate
|
||||
$mArgs = [];
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$mArgs[] = $arg;
|
||||
}
|
||||
}
|
||||
$count = Counts::COUNT($mArgs);
|
||||
--$entry;
|
||||
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
rsort($mArgs);
|
||||
|
||||
return $mArgs[$entry];
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Statistical\Size::large(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1503,40 +1409,18 @@ class Statistical
|
|||
* asymmetric tail extending toward more positive values. Negative skewness indicates a
|
||||
* distribution with an asymmetric tail extending toward more negative values.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Deviations::skew()
|
||||
* Use the skew() method in the Statistical\Deviations class instead
|
||||
*
|
||||
* @param array ...$args Data Series
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function SKEW(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
$mean = Averages::average($aArgs);
|
||||
$stdDev = StandardDeviations::STDEV($aArgs);
|
||||
|
||||
if ($stdDev === 0.0 || is_string($stdDev)) {
|
||||
return Functions::DIV0();
|
||||
}
|
||||
|
||||
$count = $summer = 0;
|
||||
// Loop through arguments
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
|
||||
} elseif (!is_numeric($arg)) {
|
||||
return Functions::VALUE();
|
||||
} else {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$summer += (($arg - $mean) / $stdDev) ** 3;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count > 2) {
|
||||
return $summer * ($count / (($count - 1) * ($count - 2)));
|
||||
}
|
||||
|
||||
return Functions::DIV0();
|
||||
return Statistical\Deviations::skew(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1568,38 +1452,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* SMALL(value1[,value2[, ...]],entry)
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Size::small()
|
||||
* Use the small() method in the Statistical\Size class instead
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function SMALL(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
// Calculate
|
||||
$entry = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($entry)) && (!is_string($entry))) {
|
||||
$entry = (int) floor($entry);
|
||||
|
||||
$mArgs = [];
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$mArgs[] = $arg;
|
||||
}
|
||||
}
|
||||
$count = Counts::COUNT($mArgs);
|
||||
--$entry;
|
||||
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
sort($mArgs);
|
||||
|
||||
return $mArgs[$entry];
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Statistical\Size::small(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1607,6 +1471,11 @@ class Statistical
|
|||
*
|
||||
* Returns a normalized value from a distribution characterized by mean and standard_dev.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Statistical\Standardize::execute()
|
||||
* Use the execute() method in the Statistical\Standardize class instead
|
||||
*
|
||||
* @param float $value Value to normalize
|
||||
* @param float $mean Mean Value
|
||||
* @param float $stdDev Standard Deviation
|
||||
|
|
@ -1615,19 +1484,7 @@ class Statistical
|
|||
*/
|
||||
public static function STANDARDIZE($value, $mean, $stdDev)
|
||||
{
|
||||
$value = Functions::flattenSingleValue($value);
|
||||
$mean = Functions::flattenSingleValue($mean);
|
||||
$stdDev = Functions::flattenSingleValue($stdDev);
|
||||
|
||||
if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
|
||||
if ($stdDev <= 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
return ($value - $mean) / $stdDev;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Statistical\Standardize::execute($value, $mean, $stdDev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1812,42 +1669,18 @@ class Statistical
|
|||
* Excel Function:
|
||||
* TRIMEAN(value1[,value2[, ...]], $discard)
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
*@see Statistical\Averages\Mean::trim()
|
||||
* Use the trim() method in the Statistical\Averages\Mean class instead
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function TRIMMEAN(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
// Calculate
|
||||
$percent = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($percent)) && (!is_string($percent))) {
|
||||
if (($percent < 0) || ($percent > 1)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$mArgs = [];
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$mArgs[] = $arg;
|
||||
}
|
||||
}
|
||||
|
||||
$discard = floor(Counts::COUNT($mArgs) * $percent / 2);
|
||||
sort($mArgs);
|
||||
|
||||
for ($i = 0; $i < $discard; ++$i) {
|
||||
array_pop($mArgs);
|
||||
array_shift($mArgs);
|
||||
}
|
||||
|
||||
return Averages::average($mArgs);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Statistical\Averages\Mean::trim(...$args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1860,12 +1693,12 @@ class Statistical
|
|||
*
|
||||
* @Deprecated 1.17.0
|
||||
*
|
||||
*@see Statistical\Variances::VAR()
|
||||
* Use the VAR() method in the Statistical\Variances class instead
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string (string if result is an error)
|
||||
*
|
||||
*@see Statistical\Variances::VAR()
|
||||
* Use the VAR() method in the Statistical\Variances class instead
|
||||
*/
|
||||
public static function VARFunc(...$args)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
|
||||
|
||||
class Mean
|
||||
{
|
||||
/**
|
||||
* GEOMEAN.
|
||||
*
|
||||
* Returns the geometric mean of an array or range of positive data. For example, you
|
||||
* can use GEOMEAN to calculate average growth rate given compound interest with
|
||||
* variable rates.
|
||||
*
|
||||
* Excel Function:
|
||||
* GEOMEAN(value1[,value2[, ...]])
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function geometric(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
$aMean = MathTrig\Product::evaluate($aArgs);
|
||||
if (is_numeric($aMean) && ($aMean > 0)) {
|
||||
$aCount = Counts::COUNT($aArgs);
|
||||
if (Minimum::MIN($aArgs) > 0) {
|
||||
return $aMean ** (1 / $aCount);
|
||||
}
|
||||
}
|
||||
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
/**
|
||||
* HARMEAN.
|
||||
*
|
||||
* Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
|
||||
* arithmetic mean of reciprocals.
|
||||
*
|
||||
* Excel Function:
|
||||
* HARMEAN(value1[,value2[, ...]])
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function harmonic(...$args)
|
||||
{
|
||||
// Loop through arguments
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
if (Minimum::MIN($aArgs) < 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$returnValue = 0;
|
||||
$aCount = 0;
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
if ($arg <= 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$returnValue += (1 / $arg);
|
||||
++$aCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
if ($aCount > 0) {
|
||||
return 1 / ($returnValue / $aCount);
|
||||
}
|
||||
|
||||
return Functions::NA();
|
||||
}
|
||||
|
||||
/**
|
||||
* TRIMMEAN.
|
||||
*
|
||||
* Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
|
||||
* taken by excluding a percentage of data points from the top and bottom tails
|
||||
* of a data set.
|
||||
*
|
||||
* Excel Function:
|
||||
* TRIMEAN(value1[,value2[, ...]], $discard)
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function trim(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
// Calculate
|
||||
$percent = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($percent)) && (!is_string($percent))) {
|
||||
if (($percent < 0) || ($percent > 1)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$mArgs = [];
|
||||
foreach ($aArgs as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$mArgs[] = $arg;
|
||||
}
|
||||
}
|
||||
|
||||
$discard = floor(Counts::COUNT($mArgs) * $percent / 2);
|
||||
sort($mArgs);
|
||||
|
||||
for ($i = 0; $i < $discard; ++$i) {
|
||||
array_pop($mArgs);
|
||||
array_shift($mArgs);
|
||||
}
|
||||
|
||||
return Averages::average($mArgs);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Deviations
|
||||
{
|
||||
/**
|
||||
* DEVSQ.
|
||||
*
|
||||
* Returns the sum of squares of deviations of data points from their sample mean.
|
||||
*
|
||||
* Excel Function:
|
||||
* DEVSQ(value1[,value2[, ...]])
|
||||
*
|
||||
* @param mixed ...$args Data values
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function sumSquares(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
|
||||
$aMean = Averages::average($aArgs);
|
||||
if (!is_numeric($aMean)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
// Return value
|
||||
$returnValue = 0.0;
|
||||
$aCount = -1;
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
// Is it a numeric value?
|
||||
if (
|
||||
(is_bool($arg)) &&
|
||||
((!Functions::isCellValue($k)) ||
|
||||
(Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
|
||||
) {
|
||||
$arg = (int) $arg;
|
||||
}
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$returnValue += ($arg - $aMean) ** 2;
|
||||
++$aCount;
|
||||
}
|
||||
}
|
||||
|
||||
return $aCount === 0 ? Functions::VALUE() : $returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* KURT.
|
||||
*
|
||||
* Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
|
||||
* or flatness of a distribution compared with the normal distribution. Positive
|
||||
* kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
|
||||
* relatively flat distribution.
|
||||
*
|
||||
* @param array ...$args Data Series
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function kurtosis(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
$mean = Averages::average($aArgs);
|
||||
if (!is_numeric($mean)) {
|
||||
return Functions::DIV0();
|
||||
}
|
||||
$stdDev = StandardDeviations::STDEV($aArgs);
|
||||
|
||||
if ($stdDev > 0) {
|
||||
$count = $summer = 0;
|
||||
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
|
||||
} else {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$summer += (($arg - $mean) / $stdDev) ** 4;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count > 3) {
|
||||
return $summer * ($count * ($count + 1) /
|
||||
(($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 /
|
||||
(($count - 2) * ($count - 3)));
|
||||
}
|
||||
}
|
||||
|
||||
return Functions::DIV0();
|
||||
}
|
||||
|
||||
/**
|
||||
* SKEW.
|
||||
*
|
||||
* Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
|
||||
* of a distribution around its mean. Positive skewness indicates a distribution with an
|
||||
* asymmetric tail extending toward more positive values. Negative skewness indicates a
|
||||
* distribution with an asymmetric tail extending toward more negative values.
|
||||
*
|
||||
* @param array ...$args Data Series
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function skew(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArrayIndexed($args);
|
||||
$mean = Averages::average($aArgs);
|
||||
if (!is_numeric($mean)) {
|
||||
return Functions::DIV0();
|
||||
}
|
||||
$stdDev = StandardDeviations::STDEV($aArgs);
|
||||
if ($stdDev === 0.0 || is_string($stdDev)) {
|
||||
return Functions::DIV0();
|
||||
}
|
||||
|
||||
$count = $summer = 0;
|
||||
// Loop through arguments
|
||||
foreach ($aArgs as $k => $arg) {
|
||||
if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
|
||||
} elseif (!is_numeric($arg)) {
|
||||
return Functions::VALUE();
|
||||
} else {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$summer += (($arg - $mean) / $stdDev) ** 3;
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count > 2) {
|
||||
return $summer * ($count / (($count - 1) * ($count - 2)));
|
||||
}
|
||||
|
||||
return Functions::DIV0();
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,26 @@ class StandardNormal
|
|||
return Normal::inverse($value, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* GAUSS.
|
||||
*
|
||||
* Calculates the probability that a member of a standard normal population will fall between
|
||||
* the mean and z standard deviations from the mean.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function gauss($value)
|
||||
{
|
||||
$value = Functions::flattenSingleValue($value);
|
||||
if (!is_numeric($value)) {
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
return self::distribution($value, true) - 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZTEST.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Size
|
||||
{
|
||||
/**
|
||||
* LARGE.
|
||||
*
|
||||
* Returns the nth largest value in a data set. You can use this function to
|
||||
* select a value based on its relative standing.
|
||||
*
|
||||
* Excel Function:
|
||||
* LARGE(value1[,value2[, ...]],entry)
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function large(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
$entry = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($entry)) && (!is_string($entry))) {
|
||||
$entry = (int) floor($entry);
|
||||
|
||||
$mArgs = self::filter($aArgs);
|
||||
$count = Counts::COUNT($mArgs);
|
||||
--$entry;
|
||||
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
rsort($mArgs);
|
||||
|
||||
return $mArgs[$entry];
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* SMALL.
|
||||
*
|
||||
* Returns the nth smallest value in a data set. You can use this function to
|
||||
* select a value based on its relative standing.
|
||||
*
|
||||
* Excel Function:
|
||||
* SMALL(value1[,value2[, ...]],entry)
|
||||
*
|
||||
* @param mixed $args Data values
|
||||
*
|
||||
* @return float|string The result, or a string containing an error
|
||||
*/
|
||||
public static function small(...$args)
|
||||
{
|
||||
$aArgs = Functions::flattenArray($args);
|
||||
|
||||
$entry = array_pop($aArgs);
|
||||
|
||||
if ((is_numeric($entry)) && (!is_string($entry))) {
|
||||
$entry = (int) floor($entry);
|
||||
|
||||
$mArgs = self::filter($aArgs);
|
||||
$count = Counts::COUNT($mArgs);
|
||||
--$entry;
|
||||
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
sort($mArgs);
|
||||
|
||||
return $mArgs[$entry];
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $args Data values
|
||||
*/
|
||||
protected static function filter(array $args): array
|
||||
{
|
||||
$mArgs = [];
|
||||
|
||||
foreach ($args as $arg) {
|
||||
// Is it a numeric value?
|
||||
if ((is_numeric($arg)) && (!is_string($arg))) {
|
||||
$mArgs[] = $arg;
|
||||
}
|
||||
}
|
||||
|
||||
return $mArgs;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Standardize extends StatisticalValidations
|
||||
{
|
||||
/**
|
||||
* STANDARDIZE.
|
||||
*
|
||||
* Returns a normalized value from a distribution characterized by mean and standard_dev.
|
||||
*
|
||||
* @param float $value Value to normalize
|
||||
* @param float $mean Mean Value
|
||||
* @param float $stdDev Standard Deviation
|
||||
*
|
||||
* @return float|string Standardized value, or a string containing an error
|
||||
*/
|
||||
public static function execute($value, $mean, $stdDev)
|
||||
{
|
||||
$value = Functions::flattenSingleValue($value);
|
||||
$mean = Functions::flattenSingleValue($mean);
|
||||
$stdDev = Functions::flattenSingleValue($stdDev);
|
||||
|
||||
try {
|
||||
$value = self::validateFloat($value);
|
||||
$mean = self::validateFloat($mean);
|
||||
$stdDev = self::validateFloat($stdDev);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($stdDev <= 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
return ($value - $mean) / $stdDev;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DevSqTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerDEVSQ
|
||||
*
|
||||
* @param mixed $expectedResult
|
||||
*/
|
||||
public function testDEVSQ($expectedResult, ...$args): void
|
||||
{
|
||||
$result = Statistical::DEVSQ(...$args);
|
||||
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
|
||||
}
|
||||
|
||||
public function providerDEVSQ(): array
|
||||
{
|
||||
return require 'tests/data/Calculation/Statistical/DEVSQ.php';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
48,
|
||||
[4, 5, 8, 7, 11, 4, 3],
|
||||
],
|
||||
[
|
||||
47.5,
|
||||
[1, 3, 5, 2, 9, 7],
|
||||
],
|
||||
[
|
||||
79.875,
|
||||
[1, 3, 5, 2, 9, 7, 8, 10],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
['A', 'B', 'C', 'D'],
|
||||
],
|
||||
];
|
||||
|
|
@ -13,6 +13,10 @@ return [
|
|||
'#NUM!',
|
||||
2.5, -3, 0.5, 1, 3,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
2.5, 3, 0.0, 1, 3,
|
||||
],
|
||||
[
|
||||
'#N/A',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,4 +9,12 @@ return[
|
|||
0.532657874050135,
|
||||
[4, 5, 4, 4, 4, 4, 4, 2, 3, 5, 5, 3],
|
||||
],
|
||||
[
|
||||
'#DIV/0!',
|
||||
['A', 'B', 'C', 'D', 'E'],
|
||||
],
|
||||
[
|
||||
'#DIV/0!',
|
||||
[1, 2, 3, 'A', 'B', 'C', 'D', 'E'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,4 +17,12 @@ return [
|
|||
'#DIV/0!',
|
||||
[1, 1],
|
||||
],
|
||||
[
|
||||
'#DIV/0!',
|
||||
[1, 1],
|
||||
],
|
||||
[
|
||||
'#DIV/0!',
|
||||
['A', 'B', 'C', 'D', 'E'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,4 +9,6 @@ return [
|
|||
[-0.676, 63.81, 65.5, 2.5],
|
||||
['#NUM!', 1.1, -2.2, -3.3],
|
||||
['#VALUE!', 'NAN', 0.1, 0.2],
|
||||
['#VALUE!', 0.1, 'NAN', 0.2],
|
||||
['#VALUE!', 0.1, 0.2, 'NAN'],
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue