For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

425 lines
12 KiB

  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. }