bestFitClass.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <?php
  2. /**
  3. * PHPExcel_Best_Fit
  4. *
  5. * Copyright (c) 2006 - 2015 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Shared_Trend
  23. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. class PHPExcel_Best_Fit
  28. {
  29. /**
  30. * Indicator flag for a calculation error
  31. *
  32. * @var boolean
  33. **/
  34. protected $error = false;
  35. /**
  36. * Algorithm type to use for best-fit
  37. *
  38. * @var string
  39. **/
  40. protected $bestFitType = 'undetermined';
  41. /**
  42. * Number of entries in the sets of x- and y-value arrays
  43. *
  44. * @var int
  45. **/
  46. protected $valueCount = 0;
  47. /**
  48. * X-value dataseries of values
  49. *
  50. * @var float[]
  51. **/
  52. protected $xValues = array();
  53. /**
  54. * Y-value dataseries of values
  55. *
  56. * @var float[]
  57. **/
  58. protected $yValues = array();
  59. /**
  60. * Flag indicating whether values should be adjusted to Y=0
  61. *
  62. * @var boolean
  63. **/
  64. protected $adjustToZero = false;
  65. /**
  66. * Y-value series of best-fit values
  67. *
  68. * @var float[]
  69. **/
  70. protected $yBestFitValues = array();
  71. protected $goodnessOfFit = 1;
  72. protected $stdevOfResiduals = 0;
  73. protected $covariance = 0;
  74. protected $correlation = 0;
  75. protected $SSRegression = 0;
  76. protected $SSResiduals = 0;
  77. protected $DFResiduals = 0;
  78. protected $f = 0;
  79. protected $slope = 0;
  80. protected $slopeSE = 0;
  81. protected $intersect = 0;
  82. protected $intersectSE = 0;
  83. protected $xOffset = 0;
  84. protected $yOffset = 0;
  85. public function getError()
  86. {
  87. return $this->error;
  88. }
  89. public function getBestFitType()
  90. {
  91. return $this->bestFitType;
  92. }
  93. /**
  94. * Return the Y-Value for a specified value of X
  95. *
  96. * @param float $xValue X-Value
  97. * @return float Y-Value
  98. */
  99. public function getValueOfYForX($xValue)
  100. {
  101. return false;
  102. }
  103. /**
  104. * Return the X-Value for a specified value of Y
  105. *
  106. * @param float $yValue Y-Value
  107. * @return float X-Value
  108. */
  109. public function getValueOfXForY($yValue)
  110. {
  111. return false;
  112. }
  113. /**
  114. * Return the original set of X-Values
  115. *
  116. * @return float[] X-Values
  117. */
  118. public function getXValues()
  119. {
  120. return $this->xValues;
  121. }
  122. /**
  123. * Return the Equation of the best-fit line
  124. *
  125. * @param int $dp Number of places of decimal precision to display
  126. * @return string
  127. */
  128. public function getEquation($dp = 0)
  129. {
  130. return false;
  131. }
  132. /**
  133. * Return the Slope of the line
  134. *
  135. * @param int $dp Number of places of decimal precision to display
  136. * @return string
  137. */
  138. public function getSlope($dp = 0)
  139. {
  140. if ($dp != 0) {
  141. return round($this->slope, $dp);
  142. }
  143. return $this->slope;
  144. }
  145. /**
  146. * Return the standard error of the Slope
  147. *
  148. * @param int $dp Number of places of decimal precision to display
  149. * @return string
  150. */
  151. public function getSlopeSE($dp = 0)
  152. {
  153. if ($dp != 0) {
  154. return round($this->slopeSE, $dp);
  155. }
  156. return $this->slopeSE;
  157. }
  158. /**
  159. * Return the Value of X where it intersects Y = 0
  160. *
  161. * @param int $dp Number of places of decimal precision to display
  162. * @return string
  163. */
  164. public function getIntersect($dp = 0)
  165. {
  166. if ($dp != 0) {
  167. return round($this->intersect, $dp);
  168. }
  169. return $this->intersect;
  170. }
  171. /**
  172. * Return the standard error of the Intersect
  173. *
  174. * @param int $dp Number of places of decimal precision to display
  175. * @return string
  176. */
  177. public function getIntersectSE($dp = 0)
  178. {
  179. if ($dp != 0) {
  180. return round($this->intersectSE, $dp);
  181. }
  182. return $this->intersectSE;
  183. }
  184. /**
  185. * Return the goodness of fit for this regression
  186. *
  187. * @param int $dp Number of places of decimal precision to return
  188. * @return float
  189. */
  190. public function getGoodnessOfFit($dp = 0)
  191. {
  192. if ($dp != 0) {
  193. return round($this->goodnessOfFit, $dp);
  194. }
  195. return $this->goodnessOfFit;
  196. }
  197. public function getGoodnessOfFitPercent($dp = 0)
  198. {
  199. if ($dp != 0) {
  200. return round($this->goodnessOfFit * 100, $dp);
  201. }
  202. return $this->goodnessOfFit * 100;
  203. }
  204. /**
  205. * Return the standard deviation of the residuals for this regression
  206. *
  207. * @param int $dp Number of places of decimal precision to return
  208. * @return float
  209. */
  210. public function getStdevOfResiduals($dp = 0)
  211. {
  212. if ($dp != 0) {
  213. return round($this->stdevOfResiduals, $dp);
  214. }
  215. return $this->stdevOfResiduals;
  216. }
  217. public function getSSRegression($dp = 0)
  218. {
  219. if ($dp != 0) {
  220. return round($this->SSRegression, $dp);
  221. }
  222. return $this->SSRegression;
  223. }
  224. public function getSSResiduals($dp = 0)
  225. {
  226. if ($dp != 0) {
  227. return round($this->SSResiduals, $dp);
  228. }
  229. return $this->SSResiduals;
  230. }
  231. public function getDFResiduals($dp = 0)
  232. {
  233. if ($dp != 0) {
  234. return round($this->DFResiduals, $dp);
  235. }
  236. return $this->DFResiduals;
  237. }
  238. public function getF($dp = 0)
  239. {
  240. if ($dp != 0) {
  241. return round($this->f, $dp);
  242. }
  243. return $this->f;
  244. }
  245. public function getCovariance($dp = 0)
  246. {
  247. if ($dp != 0) {
  248. return round($this->covariance, $dp);
  249. }
  250. return $this->covariance;
  251. }
  252. public function getCorrelation($dp = 0)
  253. {
  254. if ($dp != 0) {
  255. return round($this->correlation, $dp);
  256. }
  257. return $this->correlation;
  258. }
  259. public function getYBestFitValues()
  260. {
  261. return $this->yBestFitValues;
  262. }
  263. protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const)
  264. {
  265. $SSres = $SScov = $SScor = $SStot = $SSsex = 0.0;
  266. foreach ($this->xValues as $xKey => $xValue) {
  267. $bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
  268. $SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
  269. if ($const) {
  270. $SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
  271. } else {
  272. $SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
  273. }
  274. $SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
  275. if ($const) {
  276. $SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
  277. } else {
  278. $SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
  279. }
  280. }
  281. $this->SSResiduals = $SSres;
  282. $this->DFResiduals = $this->valueCount - 1 - $const;
  283. if ($this->DFResiduals == 0.0) {
  284. $this->stdevOfResiduals = 0.0;
  285. } else {
  286. $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
  287. }
  288. if (($SStot == 0.0) || ($SSres == $SStot)) {
  289. $this->goodnessOfFit = 1;
  290. } else {
  291. $this->goodnessOfFit = 1 - ($SSres / $SStot);
  292. }
  293. $this->SSRegression = $this->goodnessOfFit * $SStot;
  294. $this->covariance = $SScov / $this->valueCount;
  295. $this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - pow($sumX, 2)) * ($this->valueCount * $sumY2 - pow($sumY, 2)));
  296. $this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex);
  297. $this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2));
  298. if ($this->SSResiduals != 0.0) {
  299. if ($this->DFResiduals == 0.0) {
  300. $this->f = 0.0;
  301. } else {
  302. $this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals);
  303. }
  304. } else {
  305. if ($this->DFResiduals == 0.0) {
  306. $this->f = 0.0;
  307. } else {
  308. $this->f = $this->SSRegression / $this->DFResiduals;
  309. }
  310. }
  311. }
  312. protected function leastSquareFit($yValues, $xValues, $const)
  313. {
  314. // calculate sums
  315. $x_sum = array_sum($xValues);
  316. $y_sum = array_sum($yValues);
  317. $meanX = $x_sum / $this->valueCount;
  318. $meanY = $y_sum / $this->valueCount;
  319. $mBase = $mDivisor = $xx_sum = $xy_sum = $yy_sum = 0.0;
  320. for ($i = 0; $i < $this->valueCount; ++$i) {
  321. $xy_sum += $xValues[$i] * $yValues[$i];
  322. $xx_sum += $xValues[$i] * $xValues[$i];
  323. $yy_sum += $yValues[$i] * $yValues[$i];
  324. if ($const) {
  325. $mBase += ($xValues[$i] - $meanX) * ($yValues[$i] - $meanY);
  326. $mDivisor += ($xValues[$i] - $meanX) * ($xValues[$i] - $meanX);
  327. } else {
  328. $mBase += $xValues[$i] * $yValues[$i];
  329. $mDivisor += $xValues[$i] * $xValues[$i];
  330. }
  331. }
  332. // calculate slope
  333. // $this->slope = (($this->valueCount * $xy_sum) - ($x_sum * $y_sum)) / (($this->valueCount * $xx_sum) - ($x_sum * $x_sum));
  334. $this->slope = $mBase / $mDivisor;
  335. // calculate intersect
  336. // $this->intersect = ($y_sum - ($this->slope * $x_sum)) / $this->valueCount;
  337. if ($const) {
  338. $this->intersect = $meanY - ($this->slope * $meanX);
  339. } else {
  340. $this->intersect = 0;
  341. }
  342. $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, $meanX, $meanY, $const);
  343. }
  344. /**
  345. * Define the regression
  346. *
  347. * @param float[] $yValues The set of Y-values for this regression
  348. * @param float[] $xValues The set of X-values for this regression
  349. * @param boolean $const
  350. */
  351. public function __construct($yValues, $xValues = array(), $const = true)
  352. {
  353. // Calculate number of points
  354. $nY = count($yValues);
  355. $nX = count($xValues);
  356. // Define X Values if necessary
  357. if ($nX == 0) {
  358. $xValues = range(1, $nY);
  359. $nX = $nY;
  360. } elseif ($nY != $nX) {
  361. // Ensure both arrays of points are the same size
  362. $this->error = true;
  363. return false;
  364. }
  365. $this->valueCount = $nY;
  366. $this->xValues = $xValues;
  367. $this->yValues = $yValues;
  368. }
  369. }