Trend.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
  3. class Trend
  4. {
  5. const TREND_LINEAR = 'Linear';
  6. const TREND_LOGARITHMIC = 'Logarithmic';
  7. const TREND_EXPONENTIAL = 'Exponential';
  8. const TREND_POWER = 'Power';
  9. const TREND_POLYNOMIAL_2 = 'Polynomial_2';
  10. const TREND_POLYNOMIAL_3 = 'Polynomial_3';
  11. const TREND_POLYNOMIAL_4 = 'Polynomial_4';
  12. const TREND_POLYNOMIAL_5 = 'Polynomial_5';
  13. const TREND_POLYNOMIAL_6 = 'Polynomial_6';
  14. const TREND_BEST_FIT = 'Bestfit';
  15. const TREND_BEST_FIT_NO_POLY = 'Bestfit_no_Polynomials';
  16. /**
  17. * Names of the best-fit Trend analysis methods.
  18. *
  19. * @var string[]
  20. */
  21. private static $trendTypes = [
  22. self::TREND_LINEAR,
  23. self::TREND_LOGARITHMIC,
  24. self::TREND_EXPONENTIAL,
  25. self::TREND_POWER,
  26. ];
  27. /**
  28. * Names of the best-fit Trend polynomial orders.
  29. *
  30. * @var string[]
  31. */
  32. private static $trendTypePolynomialOrders = [
  33. self::TREND_POLYNOMIAL_2,
  34. self::TREND_POLYNOMIAL_3,
  35. self::TREND_POLYNOMIAL_4,
  36. self::TREND_POLYNOMIAL_5,
  37. self::TREND_POLYNOMIAL_6,
  38. ];
  39. /**
  40. * Cached results for each method when trying to identify which provides the best fit.
  41. *
  42. * @var bestFit[]
  43. */
  44. private static $trendCache = [];
  45. public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [], $xValues = [], $const = true)
  46. {
  47. // Calculate number of points in each dataset
  48. $nY = count($yValues);
  49. $nX = count($xValues);
  50. // Define X Values if necessary
  51. if ($nX == 0) {
  52. $xValues = range(1, $nY);
  53. $nX = $nY;
  54. } elseif ($nY != $nX) {
  55. // Ensure both arrays of points are the same size
  56. trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR);
  57. }
  58. $key = md5($trendType . $const . serialize($yValues) . serialize($xValues));
  59. // Determine which Trend method has been requested
  60. switch ($trendType) {
  61. // Instantiate and return the class for the requested Trend method
  62. case self::TREND_LINEAR:
  63. case self::TREND_LOGARITHMIC:
  64. case self::TREND_EXPONENTIAL:
  65. case self::TREND_POWER:
  66. if (!isset(self::$trendCache[$key])) {
  67. $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
  68. self::$trendCache[$key] = new $className($yValues, $xValues, $const);
  69. }
  70. return self::$trendCache[$key];
  71. case self::TREND_POLYNOMIAL_2:
  72. case self::TREND_POLYNOMIAL_3:
  73. case self::TREND_POLYNOMIAL_4:
  74. case self::TREND_POLYNOMIAL_5:
  75. case self::TREND_POLYNOMIAL_6:
  76. if (!isset(self::$trendCache[$key])) {
  77. $order = substr($trendType, -1);
  78. self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues, $const);
  79. }
  80. return self::$trendCache[$key];
  81. case self::TREND_BEST_FIT:
  82. case self::TREND_BEST_FIT_NO_POLY:
  83. // If the request is to determine the best fit regression, then we test each Trend line in turn
  84. // Start by generating an instance of each available Trend method
  85. foreach (self::$trendTypes as $trendMethod) {
  86. $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
  87. $bestFit[$trendMethod] = new $className($yValues, $xValues, $const);
  88. $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
  89. }
  90. if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
  91. foreach (self::$trendTypePolynomialOrders as $trendMethod) {
  92. $order = substr($trendMethod, -1);
  93. $bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues, $const);
  94. if ($bestFit[$trendMethod]->getError()) {
  95. unset($bestFit[$trendMethod]);
  96. } else {
  97. $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
  98. }
  99. }
  100. }
  101. // Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class
  102. arsort($bestFitValue);
  103. $bestFitType = key($bestFitValue);
  104. return $bestFit[$bestFitType];
  105. default:
  106. return false;
  107. }
  108. }
  109. }