Math_Stats
[ class tree: Math_Stats ] [ index: Math_Stats ] [ all elements ]

Source for file Stats.php

Documentation is available at Stats.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Jesus M. Castagnetto <jmcastagnetto@php.net>                |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Stats.php 303981 2010-10-04 12:07:56Z clockwerx $
  20. //
  21.  
  22. include_once 'PEAR.php';
  23.  
  24. /**
  25.  * @package Math_Stats
  26.  */
  27.  
  28. // Constants for defining the statistics to calculate /*{{{*/
  29. /**
  30.  * STATS_BASIC to generate the basic descriptive statistics
  31.  */
  32. define('STATS_BASIC'1);
  33. /**
  34.  * STATS_FULL to generate also higher moments, mode, median, etc.
  35.  */
  36. define('STATS_FULL'2);
  37. /*}}}*/
  38.  
  39. // Constants describing the data set format /*{{{*/
  40. /**
  41.  * STATS_DATA_SIMPLE for an array of numeric values. This is the default.
  42.  * e.g. $data = array(2,3,4,5,1,1,6);
  43.  */
  44. define('STATS_DATA_SIMPLE'0);
  45. /**
  46.  * STATS_DATA_CUMMULATIVE for an associative array of frequency values,
  47.  * where in each array entry, the index is the data point and the
  48.  * value the count (frequency):
  49.  * e.g. $data = array(3=>4, 2.3=>5, 1.25=>6, 0.5=>3)
  50.  */
  51. define('STATS_DATA_CUMMULATIVE'1);
  52. /*}}}*/
  53.  
  54. // Constants defining how to handle nulls /*{{{*/
  55. /**
  56.  * STATS_REJECT_NULL, reject data sets with null values. This is the default.
  57.  * Any non-numeric value is considered a null in this context.
  58.  */
  59. define('STATS_REJECT_NULL'-1);
  60. /**
  61.  * STATS_IGNORE_NULL, ignore null values and prune them from the data.
  62.  * Any non-numeric value is considered a null in this context.
  63.  */
  64. define('STATS_IGNORE_NULL'-2);
  65. /**
  66.  * STATS_USE_NULL_AS_ZERO, assign the value of 0 (zero) to null values.
  67.  * Any non-numeric value is considered a null in this context.
  68.  */
  69. define('STATS_USE_NULL_AS_ZERO'-3);
  70. /*}}}*/
  71.  
  72. /**
  73.  * A class to calculate descriptive statistics from a data set.
  74.  * Data sets can be simple arrays of data, or a cummulative hash.
  75.  * The second form is useful when passing large data set,
  76.  * for example the data set:
  77.  *
  78.  * <pre>
  79.  * $data1 = array (1,2,1,1,1,1,3,3,4.1,3,2,2,4.1,1,1,2,3,3,2,2,1,1,2,2);
  80.  * </pre>
  81.  *
  82.  * can be epxressed more compactly as:
  83.  *
  84.  * <pre>
  85.  * $data2 = array('1'=>9, '2'=>8, '3'=>5, '4.1'=>2);
  86.  * </pre>
  87.  *
  88.  * Example of use:
  89.  *
  90.  * <pre>
  91.  * include_once 'Math/Stats.php';
  92.  * $s = new Math_Stats();
  93.  * $s->setData($data1);
  94.  * // or
  95.  * // $s->setData($data2, STATS_DATA_CUMMULATIVE);
  96.  * $stats = $s->calcBasic();
  97.  * echo 'Mean: '.$stats['mean'].' StDev: '.$stats['stdev'].' <br />\n';
  98.  * 
  99.  * // using data with nulls
  100.  * // first ignoring them:
  101.  * $data3 = array(1.2, 'foo', 2.4, 3.1, 4.2, 3.2, null, 5.1, 6.2);
  102.  * $s->setNullOption(STATS_IGNORE_NULL);
  103.  * $s->setData($data3);
  104.  * $stats3 = $s->calcFull();
  105.  *
  106.  * // and then assuming nulls == 0
  107.  * $s->setNullOption(STATS_USE_NULL_AS_ZERO);
  108.  * $s->setData($data3);
  109.  * $stats3 = $s->calcFull();
  110.  * </pre>
  111.  *
  112.  * Originally this class was part of NumPHP (Numeric PHP package)
  113.  *
  114.  * @author  Jesus M. Castagnetto <jmcastagnetto@php.net>
  115.  * @version 0.9
  116.  * @access  public
  117.  * @package Math_Stats
  118.  */
  119. class Math_Stats {/*{{{*/
  120.     // properties /*{{{*/
  121.     
  122.     /**
  123.      * The simple or cummulative data set.
  124.      * Null by default.
  125.      *
  126.      * @access  private
  127.      * @var array 
  128.      */
  129.     var $_data = null;
  130.  
  131.     /**
  132.      * Expanded data set. Only set when cummulative data
  133.      * is being used. Null by default.
  134.      *
  135.      * @access  private
  136.      * @var array 
  137.      */
  138.     var $_dataExpanded = null;
  139.  
  140.     /**
  141.      * Flag for data type, one of STATS_DATA_SIMPLE or
  142.      * STATS_DATA_CUMMULATIVE. Null by default.
  143.      *
  144.      * @access  private
  145.      * @var int 
  146.      */
  147.     var $_dataOption = null;
  148.  
  149.     /**
  150.      * Flag for null handling options. One of STATS_REJECT_NULL,
  151.      * STATS_IGNORE_NULL or STATS_USE_NULL_AS_ZERO
  152.      *
  153.      * @access  private
  154.      * @var int 
  155.      */
  156.     var $_nullOption;
  157.  
  158.     /**
  159.      * Array for caching result values, should be reset
  160.      * when using setData()
  161.      *
  162.      * @access private
  163.      * @var array 
  164.      */
  165.     var $_calculatedValues = array();
  166.  
  167.     /*}}}*/
  168.     
  169.     /**
  170.      * Constructor for the class
  171.      *
  172.      * @access  public
  173.      * @param   optional    int $nullOption how to handle null values
  174.      * @return  object  Math_Stats 
  175.      */
  176.     function Math_Stats($nullOption=STATS_REJECT_NULL{/*{{{*/
  177.         $this->_nullOption $nullOption;
  178.     }/*}}}*/
  179.  
  180.     /**
  181.      * Sets and verifies the data, checking for nulls and using
  182.      * the current null handling option
  183.      *
  184.      * @access public
  185.      * @param   array   $arr    the data set
  186.      * @param   optional    int $opt    data format: STATS_DATA_CUMMULATIVE or STATS_DATA_SIMPLE (default)
  187.      * @return  mixed   true on success, a PEAR_Error object otherwise
  188.      */
  189.     function setData($arr$opt=STATS_DATA_SIMPLE{/*{{{*/
  190.         if (!is_array($arr)) {
  191.             return PEAR::raiseError('invalid data, an array of numeric data was expected');
  192.         }
  193.         $this->_data = null;
  194.         $this->_dataExpanded = null;
  195.         $this->_dataOption = null;
  196.         $this->_calculatedValues = array();
  197.         if ($opt == STATS_DATA_SIMPLE{
  198.             $this->_dataOption $opt;
  199.             $this->_data array_values($arr);
  200.         else if ($opt == STATS_DATA_CUMMULATIVE{
  201.             $this->_dataOption $opt;
  202.             $this->_data $arr;
  203.             $this->_dataExpanded = array();
  204.         
  205.         return $this->_validate();
  206.     }/*}}}*/
  207.  
  208.     /**
  209.      * Returns the data which might have been modified
  210.      * according to the current null handling options.
  211.      *
  212.      * @access  public
  213.      * @param boolean $expanded whether to return a expanded list, default is false
  214.      * @return  mixed   array of data on success, a PEAR_Error object otherwise
  215.      * @see _validate()
  216.      */
  217.     function getData($expanded=false{/*{{{*/
  218.         if ($this->_data == null{
  219.             return PEAR::raiseError('data has not been set');
  220.         }
  221.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE && $expanded{
  222.             return $this->_dataExpanded;
  223.         else {
  224.             return $this->_data;
  225.         }
  226.     }/*}}}*/
  227.  
  228.     /**
  229.      * Sets the null handling option.
  230.      * Must be called before assigning a new data set containing null values
  231.      * 
  232.      * @access  public
  233.      * @return  mixed   true on success, a PEAR_Error object otherwise
  234.      * @see _validate()
  235.      */
  236.     function setNullOption($nullOption{/*{{{*/
  237.         if ($nullOption == STATS_REJECT_NULL
  238.             || $nullOption == STATS_IGNORE_NULL
  239.             || $nullOption == STATS_USE_NULL_AS_ZERO{
  240.             $this->_nullOption $nullOption;
  241.             return true;
  242.         else {
  243.             return PEAR::raiseError('invalid null handling option expecting: '.
  244.                         'STATS_REJECT_NULL, STATS_IGNORE_NULL or STATS_USE_NULL_AS_ZERO');
  245.         }
  246.     }/*}}}*/
  247.  
  248.     /**
  249.      * Transforms the data by substracting each entry from the mean and
  250.      * dividing by its standard deviation. This will reset all pre-calculated
  251.      * values to their original (unset) defaults.
  252.      *
  253.      * @access public
  254.      * @return mixed true on success, a PEAR_Error object otherwise
  255.      * @see mean()
  256.      * @see stDev()
  257.      * @see setData()
  258.      */
  259.     function studentize({/*{{{*/
  260.         $mean $this->mean();
  261.         if (PEAR::isError($mean)) {
  262.             return $mean;
  263.         }
  264.         $std $this->stDev();
  265.         if (PEAR::isError($std)) {
  266.             return $std;
  267.         }
  268.         if ($std == 0{
  269.             return PEAR::raiseError('cannot studentize data, standard deviation is zero.');
  270.         }
  271.         $arr  = array();
  272.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  273.             foreach ($this->_data as $val=>$freq{
  274.                 $newval ($val $mean$std;
  275.                 $arr["$newval"$freq;
  276.             }
  277.         else {
  278.             foreach ($this->_data as $val{
  279.                 $newval ($val $mean$std;
  280.                 $arr[$newval;
  281.             }
  282.         }
  283.         return $this->setData($arr$this->_dataOption);
  284.     }/*}}}*/
  285.  
  286.     /**
  287.      * Transforms the data by substracting each entry from the mean.
  288.      * This will reset all pre-calculated values to their original (unset) defaults.
  289.      *
  290.      * @access public
  291.      * @return mixed true on success, a PEAR_Error object otherwise
  292.      * @see mean()
  293.      * @see setData()
  294.      */
  295.     function center({/*{{{*/
  296.         $mean $this->mean();
  297.         if (PEAR::isError($mean)) {
  298.             return $mean;
  299.         }
  300.         $arr  = array();
  301.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  302.             foreach ($this->_data as $val=>$freq{
  303.                 $newval $val $mean;
  304.                 $arr["$newval"$freq;
  305.             }
  306.         else {
  307.             foreach ($this->_data as $val{
  308.                 $newval $val $mean;
  309.                 $arr[$newval;
  310.             }
  311.         }
  312.         return $this->setData($arr$this->_dataOption);
  313.     }/*}}}*/
  314.        
  315.     /**
  316.      * Calculates the basic or full statistics for the data set
  317.      * 
  318.      * @access  public
  319.      * @param   int $mode   one of STATS_BASIC or STATS_FULL
  320.      * @param boolean $returnErrorObject whether the raw PEAR_Error (when true, default),
  321.      *                   or only the error message will be returned (when false), if an error happens.
  322.      * @return  mixed   an associative array of statistics on success, a PEAR_Error object otherwise
  323.      * @see calcBasic()
  324.      * @see calcFull()
  325.      */ 
  326.     function calc($mode$returnErrorObject=true{/*{{{*/
  327.         if ($this->_data == null{
  328.             return PEAR::raiseError('data has not been set');
  329.         }
  330.         if ($mode == STATS_BASIC{
  331.             return $this->calcBasic($returnErrorObject);
  332.         elseif ($mode == STATS_FULL{
  333.             return $this->calcFull($returnErrorObject);
  334.         else {
  335.             return PEAR::raiseError('incorrect mode, expected STATS_BASIC or STATS_FULL');
  336.         }
  337.     }/*}}}*/
  338.  
  339.     /**
  340.      * Calculates a basic set of statistics
  341.      *
  342.      * @access  public
  343.      * @param boolean $returnErrorObject whether the raw PEAR_Error (when true, default),
  344.      *                   or only the error message will be returned (when false), if an error happens.
  345.      * @return  mixed   an associative array of statistics on success, a PEAR_Error object otherwise
  346.      * @see calc()
  347.      * @see calcFull()
  348.      */
  349.     function calcBasic($returnErrorObject=true{/*{{{*/
  350.             return array (
  351.                 'min' => $this->__format($this->min()$returnErrorObject),
  352.                 'max' => $this->__format($this->max()$returnErrorObject),
  353.                 'sum' => $this->__format($this->sum()$returnErrorObject),
  354.                 'sum2' => $this->__format($this->sum2()$returnErrorObject),
  355.                 'count' => $this->__format($this->count()$returnErrorObject),
  356.                 'mean' => $this->__format($this->mean()$returnErrorObject),
  357.                 'stdev' => $this->__format($this->stDev()$returnErrorObject),
  358.                 'variance' => $this->__format($this->variance()$returnErrorObject),
  359.                 'range' => $this->__format($this->range()$returnErrorObject)
  360.             );
  361.     }/*}}}*/
  362.  
  363.     /**
  364.      * Calculates a full set of statistics
  365.      *
  366.      * @access  public
  367.      * @param boolean $returnErrorObject whether the raw PEAR_Error (when true, default),
  368.      *                   or only the error message will be returned (when false), if an error happens.
  369.      * @return  mixed   an associative array of statistics on success, a PEAR_Error object otherwise
  370.      * @see calc()
  371.      * @see calcBasic()
  372.      */
  373.     function calcFull($returnErrorObject=true{/*{{{*/
  374.             return array (
  375.                 'min' => $this->__format($this->min()$returnErrorObject),
  376.                 'max' => $this->__format($this->max()$returnErrorObject),
  377.                 'sum' => $this->__format($this->sum()$returnErrorObject),
  378.                 'sum2' => $this->__format($this->sum2()$returnErrorObject),
  379.                 'count' => $this->__format($this->count()$returnErrorObject),
  380.                 'mean' => $this->__format($this->mean()$returnErrorObject),
  381.                 'median' => $this->__format($this->median()$returnErrorObject),
  382.                 'mode' => $this->__format($this->mode()$returnErrorObject),
  383.                 'midrange' => $this->__format($this->midrange()$returnErrorObject),
  384.                 'geometric_mean' => $this->__format($this->geometricMean()$returnErrorObject),
  385.                 'harmonic_mean' => $this->__format($this->harmonicMean()$returnErrorObject),
  386.                 'stdev' => $this->__format($this->stDev()$returnErrorObject),
  387.                 'absdev' => $this->__format($this->absDev()$returnErrorObject),
  388.                 'variance' => $this->__format($this->variance()$returnErrorObject),
  389.                 'range' => $this->__format($this->range()$returnErrorObject),
  390.                 'std_error_of_mean' => $this->__format($this->stdErrorOfMean()$returnErrorObject),
  391.                 'skewness' => $this->__format($this->skewness()$returnErrorObject),
  392.                 'kurtosis' => $this->__format($this->kurtosis()$returnErrorObject),
  393.                 'coeff_of_variation' => $this->__format($this->coeffOfVariation()$returnErrorObject),
  394.                 'sample_central_moments' => array (
  395.                             1 => $this->__format($this->sampleCentralMoment(1)$returnErrorObject),
  396.                             2 => $this->__format($this->sampleCentralMoment(2)$returnErrorObject),
  397.                             3 => $this->__format($this->sampleCentralMoment(3)$returnErrorObject),
  398.                             4 => $this->__format($this->sampleCentralMoment(4)$returnErrorObject),
  399.                             5 => $this->__format($this->sampleCentralMoment(5)$returnErrorObject)
  400.                             ),
  401.                 'sample_raw_moments' => array (
  402.                             1 => $this->__format($this->sampleRawMoment(1)$returnErrorObject),
  403.                             2 => $this->__format($this->sampleRawMoment(2)$returnErrorObject),
  404.                             3 => $this->__format($this->sampleRawMoment(3)$returnErrorObject),
  405.                             4 => $this->__format($this->sampleRawMoment(4)$returnErrorObject),
  406.                             5 => $this->__format($this->sampleRawMoment(5)$returnErrorObject)
  407.                             ),
  408.                 'frequency' => $this->__format($this->frequency()$returnErrorObject),
  409.                 'quartiles' => $this->__format($this->quartiles()$returnErrorObject),
  410.                 'interquartile_range' => $this->__format($this->interquartileRange()$returnErrorObject),
  411.                 'interquartile_mean' => $this->__format($this->interquartileMean()$returnErrorObject),
  412.                 'quartile_deviation' => $this->__format($this->quartileDeviation()$returnErrorObject),
  413.                 'quartile_variation_coefficient' => $this->__format($this->quartileVariationCoefficient()$returnErrorObject),
  414.                 'quartile_skewness_coefficient' => $this->__format($this->quartileSkewnessCoefficient()$returnErrorObject)
  415.             );
  416.     }/*}}}*/
  417.  
  418.     /**
  419.      * Calculates the minimum of a data set.
  420.      * Handles cummulative data sets correctly$this->_data[0]
  421.      *
  422.      * @access  public
  423.      * @return  mixed   the minimum value on success, a PEAR_Error object otherwise
  424.      * @see calc()
  425.      * @see max()
  426.      */
  427.     function min({/*{{{*/
  428.         if ($this->_data == null{
  429.             return PEAR::raiseError('data has not been set');
  430.         }
  431.         if (!array_key_exists('min'$this->_calculatedValues)) {
  432.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  433.                 $min min(array_keys($this->_data));
  434.             else {
  435.                 $min min($this->_data);
  436.             }
  437.             $this->_calculatedValues['min'$min;
  438.         }
  439.         return $this->_calculatedValues['min'];
  440.     }/*}}}*/
  441.  
  442.     /**
  443.      * Calculates the maximum of a data set.
  444.      * Handles cummulative data sets correctly
  445.      *
  446.      * @access  public
  447.      * @return  mixed   the maximum value on success, a PEAR_Error object otherwise
  448.      * @see calc()
  449.      * @see min()
  450.      */
  451.     function max({/*{{{*/
  452.         if ($this->_data == null{
  453.             return PEAR::raiseError('data has not been set');
  454.         }
  455.         if (!array_key_exists('max'$this->_calculatedValues)) {
  456.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  457.                 $max max(array_keys($this->_data));
  458.             else {
  459.                 $max max($this->_data);
  460.             }
  461.             $this->_calculatedValues['max'$max;
  462.         }
  463.         return $this->_calculatedValues['max'];
  464.     }/*}}}*/
  465.  
  466.     /**
  467.      * Calculates SUM { xi }
  468.      * Handles cummulative data sets correctly
  469.      *
  470.      * @access  public
  471.      * @return  mixed   the sum on success, a PEAR_Error object otherwise
  472.      * @see calc()
  473.      * @see sum2()
  474.      * @see sumN()
  475.      */
  476.     function sum({/*{{{*/
  477.         if (!array_key_exists('sum'$this->_calculatedValues)) {
  478.             $sum $this->sumN(1);
  479.             if (PEAR::isError($sum)) {
  480.                 return $sum;
  481.             else {
  482.                 $this->_calculatedValues['sum'$sum;
  483.             }
  484.         }
  485.         return $this->_calculatedValues['sum'];
  486.     }/*}}}*/
  487.  
  488.     /**
  489.      * Calculates SUM { (xi)^2 }
  490.      * Handles cummulative data sets correctly
  491.      *
  492.      * @access  public
  493.      * @return  mixed   the sum on success, a PEAR_Error object otherwise
  494.      * @see calc()
  495.      * @see sum()
  496.      * @see sumN()
  497.      */
  498.     function sum2({/*{{{*/
  499.         if (!array_key_exists('sum2'$this->_calculatedValues)) {
  500.             $sum2 $this->sumN(2);
  501.             if (PEAR::isError($sum2)) {
  502.                 return $sum2;
  503.             else {
  504.                 $this->_calculatedValues['sum2'$sum2;
  505.             }
  506.         }
  507.         return $this->_calculatedValues['sum2'];
  508.     }/*}}}*/
  509.  
  510.     /**
  511.      * Calculates SUM { (xi)^n }
  512.      * Handles cummulative data sets correctly
  513.      *
  514.      * @access  public
  515.      * @param   numeric $n  the exponent
  516.      * @return  mixed   the sum on success, a PEAR_Error object otherwise
  517.      * @see calc()
  518.      * @see sum()
  519.      * @see sum2()
  520.      */
  521.     function sumN($n{/*{{{*/
  522.         if ($this->_data == null{
  523.             return PEAR::raiseError('data has not been set');
  524.         }
  525.         $sumN = 0;
  526.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  527.             foreach($this->_data as $val=>$freq{
  528.                 $sumN += $freq pow((double)$val(double)$n);
  529.             }
  530.         else {
  531.             foreach($this->_data as $val{
  532.                 $sumN += pow((double)$val(double)$n);
  533.             }
  534.         }
  535.         return $sumN;
  536.     }/*}}}*/
  537.  
  538.     /**
  539.      * Calculates PROD { (xi) }, (the product of all observations)
  540.      * Handles cummulative data sets correctly
  541.      *
  542.      * @access  public
  543.      * @return  numeric|array|PEAR_Error the product as a number or an array of numbers
  544.      *                                     (if there is numeric overflow) on success,
  545.      *                                     a PEAR_Error object otherwise
  546.      * @see productN()
  547.      */
  548.     function product({/*{{{*/
  549.         if (!array_key_exists('product'$this->_calculatedValues)) {
  550.             $product $this->productN(1);
  551.             if (PEAR::isError($product)) {
  552.                 return $product;
  553.             else {
  554.                 $this->_calculatedValues['product'$product;
  555.             }
  556.         }
  557.         return $this->_calculatedValues['product'];
  558.     }/*}}}*/
  559.  
  560.     /**
  561.      * Calculates PROD { (xi)^n }, which is the product of all observations
  562.      * Handles cummulative data sets correctly
  563.      *
  564.      * @access  public
  565.      * @param   numeric $n  the exponent
  566.      * @return  numeric|array|PEAR_Error the product as a number or an array of numbers
  567.      *                                     (if there is numeric overflow) on success,
  568.      *                                     a PEAR_Error object otherwise
  569.      * @see product()
  570.      */
  571.     function productN($n{/*{{{*/
  572.         if ($this->_data == null{
  573.             return PEAR::raiseError('data has not been set');
  574.         }
  575.         $prodN = 1.0;
  576.         $partial = array();
  577.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  578.             foreach($this->_data as $val=>$freq{
  579.                 if ($val == 0{
  580.                     return 0.0;
  581.                 }
  582.                 $prodN *= $freq pow((double)$val(double)$n);
  583.                 if ($prodN > 10000*$n{
  584.                     $partial[$prodN;
  585.                     $prodN = 1.0;
  586.                 }
  587.             }
  588.         else {
  589.             foreach($this->_data as $val{
  590.                 if ($val == 0{
  591.                     return 0.0;
  592.                 }
  593.                 $prodN *= pow((double)$val(double)$n);
  594.                 if ($prodN > 10*$n{
  595.                     $partial[$prodN;
  596.                     $prodN = 1.0;
  597.                 }
  598.             }
  599.         }
  600.         if (!empty($partial)) {
  601.             $partial[$prodN;
  602.             // try to reduce to a single value
  603.             $tmp = 1.0;
  604.             foreach ($partial as $val{
  605.                 $tmp *= $val;
  606.                 // cannot reduce, return an array
  607.                 if (is_infinite($tmp)) {
  608.                     return $partial;
  609.                 }
  610.             }
  611.             return $tmp;
  612.         else {
  613.             return $prodN;
  614.         }
  615.  
  616.     }/*}}}*/
  617.  
  618.     /**
  619.      * Calculates the number of data points in the set
  620.      * Handles cummulative data sets correctly
  621.      *
  622.      * @access  public
  623.      * @return  mixed   the count on success, a PEAR_Error object otherwise
  624.      * @see calc()
  625.      */
  626.     function count({/*{{{*/
  627.         if ($this->_data == null{
  628.             return PEAR::raiseError('data has not been set');
  629.         }
  630.         if (!array_key_exists('count'$this->_calculatedValues)) {
  631.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  632.                 $count count($this->_dataExpanded);
  633.             else {
  634.                 $count count($this->_data);
  635.             }
  636.             $this->_calculatedValues['count'$count;
  637.         }
  638.         return $this->_calculatedValues['count'];
  639.     }/*}}}*/
  640.  
  641.     /**
  642.      * Calculates the mean (average) of the data points in the set
  643.      * Handles cummulative data sets correctly
  644.      *
  645.      * @access  public
  646.      * @return  mixed   the mean value on success, a PEAR_Error object otherwise
  647.      * @see calc()
  648.      * @see sum()
  649.      * @see count()
  650.      */
  651.     function mean({/*{{{*/
  652.         if (!array_key_exists('mean'$this->_calculatedValues)) {
  653.             $sum $this->sum();
  654.             if (PEAR::isError($sum)) {
  655.                 return $sum;
  656.             }
  657.             $count $this->count();
  658.             if (PEAR::isError($count)) {
  659.                 return $count;
  660.             }
  661.             $this->_calculatedValues['mean'$sum $count;
  662.         }
  663.         return $this->_calculatedValues['mean'];
  664.     }/*}}}*/
  665.  
  666.     /**
  667.      * Calculates the range of the data set = max - min
  668.      *
  669.      * @access public
  670.      * @return mixed the value of the range on success, a PEAR_Error object otherwise.
  671.      */
  672.     function range({/*{{{*/
  673.         if (!array_key_exists('range'$this->_calculatedValues)) {
  674.             $min $this->min();
  675.             if (PEAR::isError($min)) {
  676.                 return $min;
  677.             }
  678.             $max $this->max();
  679.             if (PEAR::isError($max)) {
  680.                 return $max;
  681.             }
  682.             $this->_calculatedValues['range'$max $min;
  683.         }
  684.         return $this->_calculatedValues['range'];
  685.  
  686.     }/*}}}*/
  687.  
  688.     /**
  689.      * Calculates the variance (unbiased) of the data points in the set
  690.      * Handles cummulative data sets correctly
  691.      *
  692.      * @access  public
  693.      * @return  mixed   the variance value on success, a PEAR_Error object otherwise
  694.      * @see calc()
  695.      * @see __sumdiff()
  696.      * @see count()
  697.      */
  698.     function variance({/*{{{*/
  699.         if (!array_key_exists('variance'$this->_calculatedValues)) {
  700.             $variance $this->__calcVariance();
  701.             if (PEAR::isError($variance)) {
  702.                 return $variance;
  703.             }
  704.             $this->_calculatedValues['variance'$variance;
  705.         }
  706.         return $this->_calculatedValues['variance'];
  707.     }/*}}}*/
  708.  
  709.     /**
  710.      * Calculates the standard deviation (unbiased) of the data points in the set
  711.      * Handles cummulative data sets correctly
  712.      *
  713.      * @access  public
  714.      * @return  mixed   the standard deviation on success, a PEAR_Error object otherwise
  715.      * @see calc()
  716.      * @see variance()
  717.      */
  718.     function stDev({/*{{{*/
  719.         if (!array_key_exists('stDev'$this->_calculatedValues)) {
  720.             $variance $this->variance();
  721.             if (PEAR::isError($variance)) {
  722.                 return $variance;
  723.             }
  724.             $this->_calculatedValues['stDev'sqrt($variance);
  725.         }
  726.         return $this->_calculatedValues['stDev'];
  727.     }/*}}}*/
  728.  
  729.     /**
  730.      * Calculates the variance (unbiased) of the data points in the set
  731.      * given a fixed mean (average) value. Not used in calcBasic(), calcFull()
  732.      * or calc().
  733.      * Handles cummulative data sets correctly
  734.      *
  735.      * @access  public
  736.      * @param   numeric $mean   the fixed mean value
  737.      * @return  mixed   the variance on success, a PEAR_Error object otherwise
  738.      * @see __sumdiff()
  739.      * @see count()
  740.      * @see variance()
  741.      */
  742.     function varianceWithMean($mean{/*{{{*/
  743.         return $this->__calcVariance($mean);
  744.     }/*}}}*/
  745.     
  746.     /**
  747.      * Calculates the standard deviation (unbiased) of the data points in the set
  748.      * given a fixed mean (average) value. Not used in calcBasic(), calcFull()
  749.      * or calc().
  750.      * Handles cummulative data sets correctly
  751.      *
  752.      * @access  public
  753.      * @param   numeric $mean   the fixed mean value
  754.      * @return  mixed   the standard deviation on success, a PEAR_Error object otherwise
  755.      * @see varianceWithMean()
  756.      * @see stDev()
  757.      */
  758.     function stDevWithMean($mean{/*{{{*/
  759.         $varianceWM $this->varianceWithMean($mean);
  760.         if (PEAR::isError($varianceWM)) {
  761.             return $varianceWM;
  762.         }
  763.         return sqrt($varianceWM);
  764.     }/*}}}*/
  765.  
  766.     /**
  767.      * Calculates the absolute deviation of the data points in the set
  768.      * Handles cummulative data sets correctly
  769.      *
  770.      * @access  public
  771.      * @return  mixed   the absolute deviation on success, a PEAR_Error object otherwise
  772.      * @see calc()
  773.      * @see __sumabsdev()
  774.      * @see count()
  775.      * @see absDevWithMean()
  776.      */
  777.     function absDev({/*{{{*/
  778.         if (!array_key_exists('absDev'$this->_calculatedValues)) {
  779.             $absDev $this->__calcAbsoluteDeviation();
  780.             if (PEAR::isError($absDev)) {
  781.                 return $absDev;
  782.             }
  783.             $this->_calculatedValues['absDev'$absDev;
  784.         }
  785.         return $this->_calculatedValues['absDev'];
  786.     }/*}}}*/
  787.  
  788.     /**
  789.      * Calculates the absolute deviation of the data points in the set
  790.      * given a fixed mean (average) value. Not used in calcBasic(), calcFull()
  791.      * or calc().
  792.      * Handles cummulative data sets correctly
  793.      *
  794.      * @access  public
  795.      * @param   numeric $mean   the fixed mean value
  796.      * @return  mixed   the absolute deviation on success, a PEAR_Error object otherwise
  797.      * @see __sumabsdev()
  798.      * @see absDev()
  799.      */
  800.     function absDevWithMean($mean{/*{{{*/
  801.         return $this->__calcAbsoluteDeviation($mean);
  802.     }/*}}}*/
  803.  
  804.     /**
  805.      * Calculates the skewness of the data distribution in the set
  806.      * The skewness measures the degree of asymmetry of a distribution,
  807.      * and is related to the third central moment of a distribution.
  808.      * A normal distribution has a skewness = 0
  809.      * A distribution with a tail off towards the high end of the scale
  810.      * (positive skew) has a skewness > 0
  811.      * A distribution with a tail off towards the low end of the scale
  812.      * (negative skew) has a skewness < 0
  813.      * Handles cummulative data sets correctly
  814.      *
  815.      * @access  public
  816.      * @return  mixed   the skewness value on success, a PEAR_Error object otherwise
  817.      * @see __sumdiff()
  818.      * @see count()
  819.      * @see stDev()
  820.      * @see calc()
  821.      */
  822.     function skewness({/*{{{*/
  823.         if (!array_key_exists('skewness'$this->_calculatedValues)) {
  824.             $count $this->count();
  825.             if (PEAR::isError($count)) {
  826.                 return $count;
  827.             }
  828.             $stDev $this->stDev();
  829.             if (PEAR::isError($stDev)) {
  830.                 return $stDev;
  831.             }
  832.             $sumdiff3 $this->__sumdiff(3);
  833.             if (PEAR::isError($sumdiff3)) {
  834.                 return $sumdiff3;
  835.             }
  836.             $this->_calculatedValues['skewness'($sumdiff3 ($count pow($stDev3)));
  837.         }
  838.         return $this->_calculatedValues['skewness'];
  839.     }/*}}}*/
  840.  
  841.     /**
  842.      * Calculates the kurtosis of the data distribution in the set
  843.      * The kurtosis measures the degrees of peakedness of a distribution.
  844.      * It is also called the "excess" or "excess coefficient", and is
  845.      * a normalized form of the fourth central moment of a distribution.
  846.      * A normal distributions has kurtosis = 0
  847.      * A narrow and peaked (leptokurtic) distribution has a
  848.      * kurtosis > 0
  849.      * A flat and wide (platykurtic) distribution has a kurtosis < 0
  850.      * Handles cummulative data sets correctly
  851.      *
  852.      * @access  public
  853.      * @return  mixed   the kurtosis value on success, a PEAR_Error object otherwise
  854.      * @see __sumdiff()
  855.      * @see count()
  856.      * @see stDev()
  857.      * @see calc()
  858.      */
  859.     function kurtosis({/*{{{*/
  860.         if (!array_key_exists('kurtosis'$this->_calculatedValues)) {
  861.             $count $this->count();
  862.             if (PEAR::isError($count)) {
  863.                 return $count;
  864.             }
  865.             $stDev $this->stDev();
  866.             if (PEAR::isError($stDev)) {
  867.                 return $stDev;
  868.             }
  869.             $sumdiff4 $this->__sumdiff(4);
  870.             if (PEAR::isError($sumdiff4)) {
  871.                 return $sumdiff4;
  872.             }
  873.             $this->_calculatedValues['kurtosis'($sumdiff4 ($count pow($stDev4))) - 3;
  874.         }
  875.         return $this->_calculatedValues['kurtosis'];
  876.     }/*}}}*/
  877.  
  878.     /**
  879.      * Calculates the median of a data set.
  880.      * The median is the value such that half of the points are below it
  881.      * in a sorted data set.
  882.      * If the number of values is odd, it is the middle item.
  883.      * If the number of values is even, is the average of the two middle items.
  884.      * Handles cummulative data sets correctly
  885.      *
  886.      * @access  public
  887.      * @return  mixed   the median value on success, a PEAR_Error object otherwise
  888.      * @see count()
  889.      * @see calc()
  890.      */
  891.     function median({/*{{{*/
  892.         if ($this->_data == null{
  893.             return PEAR::raiseError('data has not been set');
  894.         }
  895.         if (!array_key_exists('median'$this->_calculatedValues)) {
  896.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  897.                 $arr =$this->_dataExpanded;
  898.             else {
  899.                 $arr =$this->_data;
  900.             }
  901.             $n $this->count();
  902.             if (PEAR::isError($n)) {
  903.                 return $n;
  904.             }
  905.             $h intval($n / 2);
  906.             if ($n % 2 == 0{
  907.                 $median ($arr[$h$arr[$h - 1]/ 2;
  908.             else {
  909.                 $median $arr[$h];
  910.             }
  911.             $this->_calculatedValues['median'$median;
  912.         }
  913.         return $this->_calculatedValues['median'];
  914.     }/*}}}*/
  915.  
  916.     /**
  917.      * Calculates the mode of a data set.
  918.      * The mode is the value with the highest frequency in the data set.
  919.      * There can be more than one mode.
  920.      * Handles cummulative data sets correctly
  921.      *
  922.      * @access  public
  923.      * @return  mixed   an array of mode value on success, a PEAR_Error object otherwise
  924.      * @see frequency()
  925.      * @see calc()
  926.      */
  927.     function mode({/*{{{*/
  928.         if ($this->_data == null{
  929.             return PEAR::raiseError('data has not been set');
  930.         }
  931.         if (!array_key_exists('mode'$this->_calculatedValues)) {
  932.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  933.                 $arr $this->_data;
  934.             else {
  935.                 $arr $this->frequency();
  936.             }
  937.             arsort($arr);
  938.             $mcount = 1;
  939.             foreach ($arr as $val=>$freq{
  940.                 if ($mcount == 1{
  941.                     $mode = array($val);
  942.                     $mfreq $freq;
  943.                     $mcount++;
  944.                     continue;
  945.                 }
  946.                 if ($mfreq == $freq)
  947.                     $mode[$val;
  948.                 if ($mfreq $freq)
  949.                     break;
  950.             }
  951.             $this->_calculatedValues['mode'$mode;
  952.         }
  953.         return $this->_calculatedValues['mode'];
  954.     }/*}}}*/
  955.  
  956.     /**
  957.      * Calculates the midrange of a data set.
  958.      * The midrange is the average of the minimum and maximum of the data set.
  959.      * Handles cummulative data sets correctly
  960.      *
  961.      * @access  public
  962.      * @return  mixed   the midrange value on success, a PEAR_Error object otherwise
  963.      * @see min()
  964.      * @see max()
  965.      * @see calc()
  966.      */
  967.     function midrange({/*{{{*/
  968.         if (!array_key_exists('midrange'$this->_calculatedValues)) {
  969.             $min $this->min();
  970.             if (PEAR::isError($min)) {
  971.                 return $min;
  972.             }
  973.             $max $this->max();
  974.             if (PEAR::isError($max)) {
  975.                 return $max;
  976.             }
  977.             $this->_calculatedValues['midrange'(($max $min/ 2);
  978.         }
  979.         return $this->_calculatedValues['midrange'];
  980.     }/*}}}*/
  981.   
  982.     /**
  983.      * Calculates the geometrical mean of the data points in the set
  984.      * Handles cummulative data sets correctly
  985.      *
  986.      * @access public
  987.      * @return mixed the geometrical mean value on success, a PEAR_Error object otherwise
  988.      * @see calc()
  989.      * @see product()
  990.      * @see count()
  991.      */
  992.     function geometricMean({/*{{{*/
  993.         if (!array_key_exists('geometricMean'$this->_calculatedValues)) {
  994.             $count $this->count();
  995.             if (PEAR::isError($count)) {
  996.                 return $count;
  997.             }
  998.             $prod $this->product();
  999.             if (PEAR::isError($prod)) {
  1000.                 return $prod;
  1001.             }
  1002.             if (is_array($prod)) {
  1003.                 $geomMean = 1.0;
  1004.                 foreach($prod as $val{
  1005.                     $geomMean *= pow($val1/$count);
  1006.                 }
  1007.                 $this->_calculatedValues['geometricMean'$geomMean;
  1008.             else {
  1009.                 if ($prod == 0.0{
  1010.                     return 0.0;
  1011.                 }
  1012.                 if ($prod < 0{
  1013.                     return PEAR::raiseError('The product of the data set is negative, geometric mean undefined.');
  1014.                 }
  1015.                 $this->_calculatedValues['geometricMean'pow($prod 1 / $count);
  1016.             }
  1017.         }
  1018.         return $this->_calculatedValues['geometricMean'];
  1019.     }/*}}}*/
  1020.  
  1021.     /**
  1022.      * Calculates the harmonic mean of the data points in the set
  1023.      * Handles cummulative data sets correctly
  1024.      *
  1025.      * @access public
  1026.      * @return mixed the harmonic mean value on success, a PEAR_Error object otherwise
  1027.      * @see calc()
  1028.      * @see count()
  1029.      */
  1030.     function harmonicMean({/*{{{*/
  1031.         if ($this->_data == null{
  1032.             return PEAR::raiseError('data has not been set');
  1033.         }
  1034.         if (!array_key_exists('harmonicMean'$this->_calculatedValues)) {
  1035.             $count $this->count();
  1036.             if (PEAR::isError($count)) {
  1037.                 return $count;
  1038.             }
  1039.             $invsum = 0.0;
  1040.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  1041.                 foreach($this->_data as $val=>$freq{
  1042.                     if ($val == 0{
  1043.                         return PEAR::raiseError('cannot calculate a '.
  1044.                                 'harmonic mean with data values of zero.');
  1045.                     }
  1046.                     $invsum += $freq $val;
  1047.                 }
  1048.             else {
  1049.                 foreach($this->_data as $val{
  1050.                     if ($val == 0{
  1051.                         return PEAR::raiseError('cannot calculate a '.
  1052.                                 'harmonic mean with data values of zero.');
  1053.                     }
  1054.                     $invsum += 1 / $val;
  1055.                 }
  1056.             }
  1057.             $this->_calculatedValues['harmonicMean'$count $invsum;
  1058.         }
  1059.         return $this->_calculatedValues['harmonicMean'];
  1060.     }/*}}}*/
  1061.  
  1062.     /**
  1063.      * Calculates the nth central moment (m{n}) of a data set.
  1064.      *
  1065.      * The definition of a sample central moment is:
  1066.      *
  1067.      *     m{n} = 1/N * SUM { (xi - avg)^n }
  1068.      *
  1069.      * where: N = sample size, avg = sample mean.
  1070.      *
  1071.      * @access public
  1072.      * @param integer $n moment to calculate
  1073.      * @return mixed the numeric value of the moment on success, PEAR_Error otherwise
  1074.      */
  1075.     function sampleCentralMoment($n{/*{{{*/
  1076.         if (!is_int($n|| $n < 1{
  1077.             return PEAR::raiseError('moment must be a positive integer >= 1.');
  1078.         }
  1079.         
  1080.         if ($n == 1{
  1081.             return 0;
  1082.         }
  1083.         $count $this->count();
  1084.         if (PEAR::isError($count)) {
  1085.             return $count;
  1086.         }
  1087.         if ($count == 0{
  1088.             return PEAR::raiseError("Cannot calculate {$n}th sample moment, ".
  1089.                     'there are zero data entries');
  1090.         }
  1091.         $sum $this->__sumdiff($n);
  1092.         if (PEAR::isError($sum)) {
  1093.             return $sum;
  1094.         }
  1095.         return ($sum $count);
  1096.     }/*}}}*/
  1097.  
  1098.     /**
  1099.      * Calculates the nth raw moment (m{n}) of a data set.
  1100.      *
  1101.      * The definition of a sample central moment is:
  1102.      *
  1103.      *     m{n} = 1/N * SUM { xi^n }
  1104.      *
  1105.      * where: N = sample size, avg = sample mean.
  1106.      * 
  1107.      * @access public
  1108.      * @param integer $n moment to calculate
  1109.      * @return mixed the numeric value of the moment on success, PEAR_Error otherwise
  1110.      */
  1111.     function sampleRawMoment($n{/*{{{*/
  1112.         if (!is_int($n|| $n < 1{
  1113.             return PEAR::raiseError('moment must be a positive integer >= 1.');
  1114.         }
  1115.         
  1116.         $count $this->count();
  1117.         if (PEAR::isError($count)) {
  1118.             return $count;
  1119.         }
  1120.         if ($count == 0{
  1121.             return PEAR::raiseError("Cannot calculate {$n}th raw moment, ".
  1122.                     'there are zero data entries.');
  1123.         }
  1124.         $sum $this->sumN($n);
  1125.         if (PEAR::isError($sum)) {
  1126.             return $sum;
  1127.         }
  1128.         return ($sum $count);
  1129.     }/*}}}*/
  1130.  
  1131.  
  1132.     /**
  1133.      * Calculates the coefficient of variation of a data set.
  1134.      * The coefficient of variation measures the spread of a set of data
  1135.      * as a proportion of its mean. It is often expressed as a percentage.
  1136.      * Handles cummulative data sets correctly
  1137.      *
  1138.      * @access  public
  1139.      * @return  mixed   the coefficient of variation on success, a PEAR_Error object otherwise
  1140.      * @see stDev()
  1141.      * @see mean()
  1142.      * @see calc()
  1143.      */
  1144.     function coeffOfVariation({/*{{{*/
  1145.         if (!array_key_exists('coeffOfVariation'$this->_calculatedValues)) {
  1146.             $mean $this->mean();
  1147.             if (PEAR::isError($mean)) {
  1148.                 return $mean;
  1149.             }
  1150.             if ($mean == 0.0{
  1151.                 return PEAR::raiseError('cannot calculate the coefficient '.
  1152.                         'of variation, mean of sample is zero');
  1153.             }
  1154.             $stDev $this->stDev();
  1155.             if (PEAR::isError($stDev)) {
  1156.                 return $stDev;
  1157.             }
  1158.  
  1159.             $this->_calculatedValues['coeffOfVariation'$stDev $mean;
  1160.         }
  1161.         return $this->_calculatedValues['coeffOfVariation'];
  1162.     }/*}}}*/
  1163.  
  1164.     /**
  1165.      * Calculates the standard error of the mean.
  1166.      * It is the standard deviation of the sampling distribution of
  1167.      * the mean. The formula is:
  1168.      *
  1169.      * S.E. Mean = SD / (N)^(1/2)
  1170.      *
  1171.      * This formula does not assume a normal distribution, and shows
  1172.      * that the size of the standard error of the mean is inversely
  1173.      * proportional to the square root of the sample size.
  1174.      *
  1175.      * @access  public
  1176.      * @return  mixed   the standard error of the mean on success, a PEAR_Error object otherwise
  1177.      * @see stDev()
  1178.      * @see count()
  1179.      * @see calc()
  1180.      */
  1181.     function stdErrorOfMean({/*{{{*/
  1182.         if (!array_key_exists('stdErrorOfMean'$this->_calculatedValues)) {
  1183.             $count $this->count();
  1184.             if (PEAR::isError($count)) {
  1185.                 return $count;
  1186.             }
  1187.             $stDev $this->stDev();
  1188.             if (PEAR::isError($stDev)) {
  1189.                 return $stDev;
  1190.             }
  1191.             $this->_calculatedValues['stdErrorOfMean'$stDev sqrt($count);
  1192.         }
  1193.         return $this->_calculatedValues['stdErrorOfMean'];
  1194.     }/*}}}*/
  1195.  
  1196.     /**
  1197.      * Calculates the value frequency table of a data set.
  1198.      * Handles cummulative data sets correctly
  1199.      *
  1200.      * @access  public
  1201.      * @return  mixed   an associative array of value=>frequency items on success, a PEAR_Error object otherwise
  1202.      * @see min()
  1203.      * @see max()
  1204.      * @see calc()
  1205.      */
  1206.     function frequency({/*{{{*/
  1207.         if ($this->_data == null{
  1208.             return PEAR::raiseError('data has not been set');
  1209.         }
  1210.         if (!array_key_exists('frequency'$this->_calculatedValues)) {
  1211.             if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  1212.                 $freq $this->_data;
  1213.             else {
  1214.                 $freq = array();
  1215.                 foreach ($this->_data as $val{
  1216.                     if (!isset($freq["$val"])) {
  1217.                         $freq["$val"= 0;
  1218.                     }
  1219.                     $freq["$val"]++;
  1220.                 }
  1221.                 ksort($freq);
  1222.             }
  1223.             $this->_calculatedValues['frequency'$freq;
  1224.         }
  1225.         return $this->_calculatedValues['frequency'];
  1226.     }/*}}}*/
  1227.  
  1228.     /**
  1229.      * The quartiles are defined as the values that divide a sorted
  1230.      * data set into four equal-sized subsets, and correspond to the
  1231.      * 25th, 50th, and 75th percentiles.
  1232.      *
  1233.      * @access public
  1234.      * @return mixed an associative array of quartiles on success, a PEAR_Error otherwise
  1235.      * @see percentile()
  1236.      */
  1237.     function quartiles({/*{{{*/
  1238.         if (!array_key_exists('quartiles'$this->_calculatedValues)) {
  1239.             $q1 $this->percentile(25);
  1240.             if (PEAR::isError($q1)) {
  1241.                 return $q1;
  1242.             }
  1243.             $q2 $this->percentile(50);
  1244.             if (PEAR::isError($q2)) {
  1245.                 return $q2;
  1246.             }
  1247.             $q3 $this->percentile(75);
  1248.             if (PEAR::isError($q3)) {
  1249.                 return $q3;
  1250.             }
  1251.             $this->_calculatedValues['quartiles'= array (
  1252.                                         '25' => $q1,
  1253.                                         '50' => $q2,
  1254.                                         '75' => $q3
  1255.                                         );
  1256.         }
  1257.         return $this->_calculatedValues['quartiles'];
  1258.     }/*}}}*/
  1259.     
  1260.     /**
  1261.      * The interquartile mean is defined as the mean of the values left
  1262.      * after discarding the lower 25% and top 25% ranked values, i.e.:
  1263.      *
  1264.      *  interquart mean = mean(<P(25),P(75)>)
  1265.      *
  1266.      *  where: P = percentile
  1267.      * 
  1268.      * @todo need to double check the equation
  1269.      * @access public
  1270.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1271.      * @see quartiles()
  1272.      */
  1273.     function interquartileMean({/*{{{*/
  1274.         if (!array_key_exists('interquartileMean'$this->_calculatedValues)) {
  1275.             $quart $this->quartiles();
  1276.             if (PEAR::isError($quart)) {
  1277.                 return $quart;
  1278.             }
  1279.             $q3 $quart['75'];
  1280.             $q1 $quart['25'];
  1281.             $sum = 0;
  1282.             $n = 0;
  1283.             foreach ($this->getData(trueas $val{
  1284.                 if ($val >= $q1 && $val <= $q3{
  1285.                     $sum += $val;
  1286.                     $n++;
  1287.                 }
  1288.             }
  1289.             if ($n == 0{
  1290.                 return PEAR::raiseError('error calculating interquartile mean, '.
  1291.                                         'empty interquartile range of values.');
  1292.             }
  1293.             $this->_calculatedValues['interquartileMean'$sum $n;
  1294.         }
  1295.         return $this->_calculatedValues['interquartileMean'];
  1296.     }/*}}}*/
  1297.  
  1298.     /**
  1299.      * The interquartile range is the distance between the 75th and 25th
  1300.      * percentiles. Basically the range of the middle 50% of the data set,
  1301.      * and thus is not affected by outliers or extreme values.
  1302.      *
  1303.      *  interquart range = P(75) - P(25)
  1304.      *
  1305.      *  where: P = percentile
  1306.      *
  1307.      * @access public
  1308.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1309.      * @see quartiles()
  1310.      */
  1311.     function interquartileRange({/*{{{*/
  1312.         if (!array_key_exists('interquartileRange'$this->_calculatedValues)) {
  1313.             $quart $this->quartiles();
  1314.             if (PEAR::isError($quart)) {
  1315.                 return $quart;
  1316.             }
  1317.             $q3 $quart['75'];
  1318.             $q1 $quart['25'];
  1319.             $this->_calculatedValues['interquartileRange'$q3 $q1;
  1320.         }
  1321.         return $this->_calculatedValues['interquartileRange'];
  1322.     }/*}}}*/
  1323.     
  1324.     /**
  1325.      * The quartile deviation is half of the interquartile range value
  1326.      *
  1327.      *  quart dev = (P(75) - P(25)) / 2
  1328.      *
  1329.      *  where: P = percentile
  1330.      *  
  1331.      * @access public
  1332.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1333.      * @see quartiles()
  1334.      * @see interquartileRange()
  1335.      */
  1336.     function quartileDeviation({/*{{{*/
  1337.         if (!array_key_exists('quartileDeviation'$this->_calculatedValues)) {
  1338.             $iqr $this->interquartileRange();
  1339.             if (PEAR::isError($iqr)) {
  1340.                 return $iqr;
  1341.             }
  1342.             $this->_calculatedValues['quartileDeviation'$iqr / 2;
  1343.         }
  1344.         return $this->_calculatedValues['quartileDeviation'];
  1345.     }/*}}}*/
  1346.  
  1347.     /**
  1348.      * The quartile variation coefficient is defined as follows:
  1349.      *
  1350.      *  quart var coeff = 100 * (P(75) - P(25)) / (P(75) + P(25))
  1351.      *
  1352.      *  where: P = percentile
  1353.      * 
  1354.      * @todo need to double check the equation
  1355.      * @access public
  1356.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1357.      * @see quartiles()
  1358.      */
  1359.     function quartileVariationCoefficient({/*{{{*/
  1360.         if (!array_key_exists('quartileVariationCoefficient'$this->_calculatedValues)) {
  1361.             $quart $this->quartiles();
  1362.             if (PEAR::isError($quart)) {
  1363.                 return $quart;
  1364.             }
  1365.             $q3 $quart['75'];
  1366.             $q1 $quart['25'];
  1367.             $d $q3 $q1;
  1368.             $s $q3 $q1;
  1369.             $this->_calculatedValues['quartileVariationCoefficient'= 100 * $d $s;
  1370.         }
  1371.         return $this->_calculatedValues['quartileVariationCoefficient'];
  1372.     }/*}}}*/
  1373.  
  1374.     /**
  1375.      * The quartile skewness coefficient (also known as Bowley Skewness),
  1376.      * is defined as follows:
  1377.      *
  1378.      *  quart skewness coeff = (P(25) - 2*P(50) + P(75)) / (P(75) - P(25))
  1379.      *
  1380.      *  where: P = percentile
  1381.      * 
  1382.      * @todo need to double check the equation
  1383.      * @access public
  1384.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1385.      * @see quartiles()
  1386.      */
  1387.     function quartileSkewnessCoefficient({/*{{{*/
  1388.         if (!array_key_exists('quartileSkewnessCoefficient'$this->_calculatedValues)) {
  1389.             $quart $this->quartiles();
  1390.             if (PEAR::isError($quart)) {
  1391.                 return $quart;
  1392.             }
  1393.             $q3 $quart['75'];
  1394.             $q2 $quart['50'];
  1395.             $q1 $quart['25'];
  1396.             $d $q3 - 2*$q2 $q1;
  1397.             $s $q3 $q1;
  1398.             $this->_calculatedValues['quartileSkewnessCoefficient'$d $s;
  1399.         }
  1400.         return $this->_calculatedValues['quartileSkewnessCoefficient'];
  1401.     }/*}}}*/
  1402.  
  1403.     /**
  1404.      * The pth percentile is the value such that p% of the a sorted data set
  1405.      * is smaller than it, and (100 - p)% of the data is larger.
  1406.      *
  1407.      * A quick algorithm to pick the appropriate value from a sorted data
  1408.      * set is as follows:
  1409.      *
  1410.      * - Count the number of values: n
  1411.      * - Calculate the position of the value in the data list: i = p * (n + 1)
  1412.      * - if i is an integer, return the data at that position
  1413.      * - if i < 1, return the minimum of the data set
  1414.      * - if i > n, return the maximum of the data set
  1415.      * - otherwise, average the entries at adjacent positions to i
  1416.      *
  1417.      * The median is the 50th percentile value.
  1418.      *
  1419.      * @todo need to double check generality of the algorithm
  1420.      *
  1421.      * @access public
  1422.      * @param numeric $p the percentile to estimate, e.g. 25 for 25th percentile
  1423.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1424.      * @see quartiles()
  1425.      * @see median()
  1426.      */
  1427.     function percentile($p{/*{{{*/
  1428.         $count $this->count();
  1429.         if (PEAR::isError($count)) {
  1430.             return $count;
  1431.         }
  1432.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  1433.             $data =$this->_dataExpanded;
  1434.         else {
  1435.             $data =$this->_data;
  1436.         }
  1437.         $obsidx $p ($count + 1/ 100;
  1438.         if (intval($obsidx== $obsidx{
  1439.             return $data[($obsidx - 1)];
  1440.         elseif ($obsidx < 1{
  1441.             return $data[0];
  1442.         elseif ($obsidx $count{
  1443.             return $data[($count - 1)];
  1444.         else {
  1445.             $left floor($obsidx - 1);
  1446.             $right ceil($obsidx - 1);
  1447.             return ($data[$left$data[$right]/ 2;
  1448.         }
  1449.     }/*}}}*/
  1450.  
  1451.     // private methods
  1452.  
  1453.     /**
  1454.      * Utility function to calculate: SUM { (xi - mean)^n }
  1455.      * 
  1456.      * @access private
  1457.      * @param   numeric $power  the exponent
  1458.      * @param   optional    double   $mean   the data set mean value
  1459.      * @return  mixed   the sum on success, a PEAR_Error object otherwise
  1460.      *
  1461.      * @see stDev()
  1462.      * @see variaceWithMean();
  1463.      * @see skewness();
  1464.      * @see kurtosis();
  1465.      */
  1466.     function __sumdiff($power$mean=null{/*{{{*/
  1467.         if ($this->_data == null{
  1468.             return PEAR::raiseError('data has not been set');
  1469.         }
  1470.         if (is_null($mean)) {
  1471.             $mean $this->mean();
  1472.             if (PEAR::isError($mean)) {
  1473.                 return $mean;
  1474.             }
  1475.         }
  1476.         $sdiff = 0;
  1477.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  1478.             foreach ($this->_data as $val=>$freq{
  1479.                 $sdiff += $freq pow((double)($val $mean)(double)$power);
  1480.             }
  1481.         else {
  1482.             foreach ($this->_data as $val)
  1483.                 $sdiff += pow((double)($val $mean)(double)$power);
  1484.         }
  1485.         return $sdiff;
  1486.     }/*}}}*/
  1487.  
  1488.     /**
  1489.      * Utility function to calculate the variance with or without
  1490.      * a fixed mean
  1491.      *
  1492.      * @access private
  1493.      * @param $mean the fixed mean to use, null as default
  1494.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1495.      * @see variance()
  1496.      * @see varianceWithMean()
  1497.      */
  1498.     function __calcVariance($mean = null{/*{{{*/
  1499.         if ($this->_data == null{
  1500.             return PEAR::raiseError('data has not been set');
  1501.         }
  1502.         $sumdiff2 $this->__sumdiff(2$mean);
  1503.         if (PEAR::isError($sumdiff2)) {
  1504.             return $sumdiff2;
  1505.         }
  1506.         $count $this->count();
  1507.         if (PEAR::isError($count)) {
  1508.             return $count;
  1509.         }
  1510.         if ($count == 1{
  1511.             return PEAR::raiseError('cannot calculate variance of a singe data point');
  1512.         }
  1513.         return  ($sumdiff2 ($count - 1));
  1514.     }/*}}}*/
  1515.  
  1516.     /**
  1517.      * Utility function to calculate the absolute deviation with or without
  1518.      * a fixed mean
  1519.      *
  1520.      * @access private
  1521.      * @param $mean the fixed mean to use, null as default
  1522.      * @return mixed a numeric value on success, a PEAR_Error otherwise
  1523.      * @see absDev()
  1524.      * @see absDevWithMean()
  1525.      */
  1526.     function __calcAbsoluteDeviation($mean = null{/*{{{*/
  1527.         if ($this->_data == null{
  1528.             return PEAR::raiseError('data has not been set');
  1529.         }
  1530.         $count $this->count();
  1531.         if (PEAR::isError($count)) {
  1532.             return $count;
  1533.         }
  1534.         $sumabsdev $this->__sumabsdev($mean);
  1535.         if (PEAR::isError($sumabsdev)) {
  1536.             return $sumabsdev;
  1537.         }
  1538.         return $sumabsdev $count;
  1539.     }/*}}}*/
  1540.  
  1541.     /**
  1542.      * Utility function to calculate: SUM { | xi - mean | }
  1543.      *
  1544.      * @access  private
  1545.      * @param   optional    double   $mean   the mean value for the set or population
  1546.      * @return  mixed   the sum on success, a PEAR_Error object otherwise
  1547.      *
  1548.      * @see absDev()
  1549.      * @see absDevWithMean()
  1550.      */
  1551.     function __sumabsdev($mean=null{/*{{{*/
  1552.         if ($this->_data == null{
  1553.             return PEAR::raiseError('data has not been set');
  1554.         }
  1555.         if (is_null($mean)) {
  1556.             $mean $this->mean();
  1557.         }
  1558.         $sdev = 0;
  1559.         if ($this->_dataOption == STATS_DATA_CUMMULATIVE{
  1560.             foreach ($this->_data as $val=>$freq{
  1561.                 $sdev += $freq abs($val $mean);
  1562.             }
  1563.         else {
  1564.             foreach ($this->_data as $val{
  1565.                 $sdev += abs($val $mean);
  1566.             }
  1567.         }
  1568.         return $sdev;
  1569.     }/*}}}*/
  1570.  
  1571.     /**
  1572.      * Utility function to format a PEAR_Error to be used by calc(),
  1573.      * calcBasic() and calcFull()
  1574.      * 
  1575.      * @access private
  1576.      * @param mixed $v value to be formatted
  1577.      * @param boolean $returnErrorObject whether the raw PEAR_Error (when true, default),
  1578.      *                   or only the error message will be returned (when false)
  1579.      * @return mixed if the value is a PEAR_Error object, and $useErrorObject
  1580.      *               is false, then a string with the error message will be returned,
  1581.      *               otherwise the value will not be modified and returned as passed.
  1582.      */
  1583.     function __format($v$useErrorObject=true{/*{{{*/
  1584.         if (PEAR::isError($v&& $useErrorObject == false{
  1585.             return $v->getMessage();
  1586.         else {
  1587.             return $v;
  1588.         }
  1589.     }/*}}}*/
  1590.  
  1591.     /**
  1592.      * Utility function to validate the data and modify it
  1593.      * according to the current null handling option
  1594.      *
  1595.      * @access  private
  1596.      * @return  mixed true on success, a PEAR_Error object otherwise
  1597.      * 
  1598.      * @see setData()
  1599.      */
  1600.     function _validate({/*{{{*/
  1601.         $cummulativeData ($this->_dataOption == STATS_DATA_CUMMULATIVE);
  1602.         foreach ($this->_data as $key=>$value{
  1603.             $d ($cummulativeData$key $value;
  1604.             $v ($cummulativeData$value $key;
  1605.             if (!is_numeric($d)) {
  1606.                 switch ($this->_nullOption{
  1607.                     case STATS_IGNORE_NULL :
  1608.                         unset($this->_data["$key"]);
  1609.                         break;
  1610.                     case STATS_USE_NULL_AS_ZERO:
  1611.                         if ($cummulativeData{
  1612.                             unset($this->_data["$key"]);
  1613.                             // TODO: shift up?
  1614.                             if (!isset($this->_data[0])) {
  1615.                                 $this->_data[0= 0;
  1616.                             }
  1617.                             $this->_data[0+= $v;
  1618.                         else {
  1619.                             $this->_data[$key= 0;
  1620.                         }
  1621.                         break;
  1622.                     case STATS_REJECT_NULL :
  1623.                     default:
  1624.                         return PEAR::raiseError('data rejected, contains NULL values');
  1625.                         break;
  1626.                 }
  1627.             }
  1628.         }
  1629.  
  1630.         // expand cummulative data
  1631.         if ($cummulativeData{
  1632.             ksort($this->_data);
  1633.             $this->_dataExpanded = array();
  1634.             // code below avoids using array_pad, because in PHP 4 that
  1635.             // function has a hard-coded limit of 1048576 array items
  1636.             // see php-src/ext/standard/array.c)
  1637.  
  1638.             //$array_pad_magic_limit = 1048576;
  1639.             foreach ($this->_data as $val=>$freq{
  1640.                 // try an ugly kludge
  1641.                 for ($k=0; $k $freq$k++{
  1642.                     $this->_dataExpanded[$val;
  1643.                 }
  1644.                 /* the code below causes a core dump
  1645.                 $valArr = array_fill(0, $freq, $val);
  1646.                 $this->_dataExpanded = array_merge($this->_dataExpanded, $valArr);
  1647.                 */
  1648.                 /* the code below gives incorrect values
  1649.                 // kludge to cover for array_pad's *features*
  1650.                 $newcount = count($this->_dataExpanded) + $freq;
  1651.                 while ($newcount > $array_pad_magic_limit) {
  1652.                     $this->_dataExpanded = array_pad($this->_dataExpanded, $array_pad_magic_limit, $val);
  1653.                     $newcount -= $array_pad_magic_limit;
  1654.                 }
  1655.                 $this->_dataExpanded = array_pad($this->_dataExpanded, $newcount, $val);
  1656.                 */
  1657.             }
  1658.             //sort($this->_dataExpanded);
  1659.         else {
  1660.             sort($this->_data);
  1661.         }
  1662.         return true;
  1663.     }/*}}}*/
  1664.  
  1665. }/*}}}*/
  1666.  
  1667. // vim: ts=4:sw=4:et:
  1668. // vim6: fdl=1: fdm=marker:
  1669.  
  1670. ?>

Documentation generated on Mon, 11 Mar 2019 15:39:18 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.