Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
   4  
   5  use PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix;
   6  
   7  class PolynomialBestFit extends BestFit
   8  {
   9      /**
  10       * Algorithm type to use for best-fit
  11       * (Name of this Trend class).
  12       *
  13       * @var string
  14       */
  15      protected $bestFitType = 'polynomial';
  16  
  17      /**
  18       * Polynomial order.
  19       *
  20       * @var int
  21       */
  22      protected $order = 0;
  23  
  24      /**
  25       * Return the order of this polynomial.
  26       *
  27       * @return int
  28       */
  29      public function getOrder()
  30      {
  31          return $this->order;
  32      }
  33  
  34      /**
  35       * Return the Y-Value for a specified value of X.
  36       *
  37       * @param float $xValue X-Value
  38       *
  39       * @return float Y-Value
  40       */
  41      public function getValueOfYForX($xValue)
  42      {
  43          $retVal = $this->getIntersect();
  44          $slope = $this->getSlope();
  45          foreach ($slope as $key => $value) {
  46              if ($value != 0.0) {
  47                  $retVal += $value * $xValue ** ($key + 1);
  48              }
  49          }
  50  
  51          return $retVal;
  52      }
  53  
  54      /**
  55       * Return the X-Value for a specified value of Y.
  56       *
  57       * @param float $yValue Y-Value
  58       *
  59       * @return float X-Value
  60       */
  61      public function getValueOfXForY($yValue)
  62      {
  63          return ($yValue - $this->getIntersect()) / $this->getSlope();
  64      }
  65  
  66      /**
  67       * Return the Equation of the best-fit line.
  68       *
  69       * @param int $dp Number of places of decimal precision to display
  70       *
  71       * @return string
  72       */
  73      public function getEquation($dp = 0)
  74      {
  75          $slope = $this->getSlope($dp);
  76          $intersect = $this->getIntersect($dp);
  77  
  78          $equation = 'Y = ' . $intersect;
  79          foreach ($slope as $key => $value) {
  80              if ($value != 0.0) {
  81                  $equation .= ' + ' . $value . ' * X';
  82                  if ($key > 0) {
  83                      $equation .= '^' . ($key + 1);
  84                  }
  85              }
  86          }
  87  
  88          return $equation;
  89      }
  90  
  91      /**
  92       * Return the Slope of the line.
  93       *
  94       * @param int $dp Number of places of decimal precision to display
  95       *
  96       * @return string
  97       */
  98      public function getSlope($dp = 0)
  99      {
 100          if ($dp != 0) {
 101              $coefficients = [];
 102              foreach ($this->slope as $coefficient) {
 103                  $coefficients[] = round($coefficient, $dp);
 104              }
 105  
 106              return $coefficients;
 107          }
 108  
 109          return $this->slope;
 110      }
 111  
 112      public function getCoefficients($dp = 0)
 113      {
 114          return array_merge([$this->getIntersect($dp)], $this->getSlope($dp));
 115      }
 116  
 117      /**
 118       * Execute the regression and calculate the goodness of fit for a set of X and Y data values.
 119       *
 120       * @param int $order Order of Polynomial for this regression
 121       * @param float[] $yValues The set of Y-values for this regression
 122       * @param float[] $xValues The set of X-values for this regression
 123       */
 124      private function polynomialRegression($order, $yValues, $xValues): void
 125      {
 126          // calculate sums
 127          $x_sum = array_sum($xValues);
 128          $y_sum = array_sum($yValues);
 129          $xx_sum = $xy_sum = $yy_sum = 0;
 130          for ($i = 0; $i < $this->valueCount; ++$i) {
 131              $xy_sum += $xValues[$i] * $yValues[$i];
 132              $xx_sum += $xValues[$i] * $xValues[$i];
 133              $yy_sum += $yValues[$i] * $yValues[$i];
 134          }
 135          /*
 136           *    This routine uses logic from the PHP port of polyfit version 0.1
 137           *    written by Michael Bommarito and Paul Meagher
 138           *
 139           *    The function fits a polynomial function of order $order through
 140           *    a series of x-y data points using least squares.
 141           *
 142           */
 143          $A = [];
 144          $B = [];
 145          for ($i = 0; $i < $this->valueCount; ++$i) {
 146              for ($j = 0; $j <= $order; ++$j) {
 147                  $A[$i][$j] = $xValues[$i] ** $j;
 148              }
 149          }
 150          for ($i = 0; $i < $this->valueCount; ++$i) {
 151              $B[$i] = [$yValues[$i]];
 152          }
 153          $matrixA = new Matrix($A);
 154          $matrixB = new Matrix($B);
 155          $C = $matrixA->solve($matrixB);
 156  
 157          $coefficients = [];
 158          for ($i = 0; $i < $C->getRowDimension(); ++$i) {
 159              $r = $C->get($i, 0);
 160              if (abs($r) <= 10 ** (-9)) {
 161                  $r = 0;
 162              }
 163              $coefficients[] = $r;
 164          }
 165  
 166          $this->intersect = array_shift($coefficients);
 167          $this->slope = $coefficients;
 168  
 169          $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
 170          foreach ($this->xValues as $xKey => $xValue) {
 171              $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
 172          }
 173      }
 174  
 175      /**
 176       * Define the regression and calculate the goodness of fit for a set of X and Y data values.
 177       *
 178       * @param int $order Order of Polynomial for this regression
 179       * @param float[] $yValues The set of Y-values for this regression
 180       * @param float[] $xValues The set of X-values for this regression
 181       * @param bool $const
 182       */
 183      public function __construct($order, $yValues, $xValues = [], $const = true)
 184      {
 185          parent::__construct($yValues, $xValues);
 186  
 187          if (!$this->error) {
 188              if ($order < $this->valueCount) {
 189                  $this->bestFitType .= '_' . $order;
 190                  $this->order = $order;
 191                  $this->polynomialRegression($order, $yValues, $xValues);
 192                  if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
 193                      $this->error = true;
 194                  }
 195              } else {
 196                  $this->error = true;
 197              }
 198          }
 199      }
 200  }