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:
Mark Baker 2021-04-29 14:34:50 +02:00 committed by GitHub
parent 160ae59751
commit e4973fa041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 581 additions and 251 deletions

View File

@ -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

View File

@ -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' => [

View File

@ -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)
{

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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.
*

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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';
}
}

View File

@ -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'],
],
];

View File

@ -13,6 +13,10 @@ return [
'#NUM!',
2.5, -3, 0.5, 1, 3,
],
[
'#NUM!',
2.5, 3, 0.0, 1, 3,
],
[
'#N/A',
],

View File

@ -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'],
],
];

View File

@ -17,4 +17,12 @@ return [
'#DIV/0!',
[1, 1],
],
[
'#DIV/0!',
[1, 1],
],
[
'#DIV/0!',
['A', 'B', 'C', 'D', 'E'],
],
];

View File

@ -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'],
];