Mark Baker Posted December 20, 2008 Share Posted December 20, 2008 I'm trying to write a class that will return details for an exponential "best-fit", using a least squares fitting method, having modified each entry in the array of y values to its log. class exponentialBestFit { private $_xValues = array(); private $_yValues = array(); private $_goodnessOfFit = 1; private $_stdevOfResiduals = 0; private $_slope = 0; private $_increment = 0; public function getValueOfYForX($xValue) { return exp($xValue * $this->_slope + $this->_intersect); } // function getValueOfYForX() public function getSlope() { return exp($this->_slope); } // function getSlope() public function getIntersect() { return $this->_intersect; } // function getIntersect() public function getGoodnessOfFit() { return $this->_goodnessOfFit; } // function getYPointFromSlopeIntersect() public function getStdevOfResiduals() { return $this->_stdevOfResiduals; } // function getStdevOfResiduals() private function _calculateGoodnessOfFit() { // Calculate number of points $nY = count($this->_yValues); $meanY = array_sum($this->_yValues) / $nY; $SSreg = $SStot = 0.0; foreach($this->_xValues as $xKey => $xValue) { $bestFitY = $this->getValueOfYForX($xValue); $SSreg += ($this->_yValues[$xKey] - $bestFitY) * ($this->_yValues[$xKey] - $bestFitY); $SStot += ($this->_yValues[$xKey] - $meanY) * ($this->_yValues[$xKey] - $meanY); } $this->_stdevOfResiduals = sqrt($SSreg / ($nY - 2)); if (($SStot == 0.0) || ($SSreg == $SStot)) { $this->_goodnessOfFit = 1; } else { $this->_goodnessOfFit = 1 - ($SSreg / $SStot); } } // function _calculateGoodnessOfFit() private function _exponential_regression($yValues, $xValues=array()) { // Calculate number of points $nY = count($yValues); $nX = count($xValues); // Define X Values if necessary if ($nX == 0) { $xValues = range(1,$nY); $nX = $nY; } // Ensure both arrays of points are the same size if ($nY != $nX) { trigger_error("exponential_regression(): Number of elements in coordinate arrays do not match.", E_USER_ERROR); } $this->_xValues = $xValues; $this->_yValues = $yValues; foreach($yValues as $key => $value) { $yValues[$key] = log($yValues[$key]); } // calculate sums $x_sum = array_sum($xValues); $y_sum = array_sum($yValues); $xx_sum = $xy_sum = 0; for($i = 0; $i < $nY; $i++) { $xy_sum += $xValues[$i] * $yValues[$i]; $xx_sum += $xValues[$i] * $xValues[$i]; } // calculate slope $this->_slope = (($nY * $xy_sum) - ($x_sum * $y_sum)) / (($nY * $xx_sum) - ($x_sum * $x_sum)); // calculate intersect $this->_intersect = ($y_sum - ($this->_slope * $x_sum)) / $nY; $this->_calculateGoodnessOfFit(); foreach($xValues as $xValue) { $this->_yBestFitValues[] = $this->getValueOfYForX($xValue); } } // function _exponential_regression() function __construct($yValues, $xValues=array()) { $this->_exponential_regression($yValues, $xValues); } // function __construct() } // class exponentialBestFit The getSlope(), getGoodnessOfFit() and getStdevOfResiduals() return exactly what I'd expect; and when I graph out the results by reading getValueOfYForX($xValue) for a series of $xValues, I get exactly the results that I expect. However, I would have thought that i'd need to use return $this->_intersect * exp($this->_slope * $xValue); in getValueOfYForX(), but somehow I don't seem to be calculating the correct value for $this->_intersect Can anybody with better math than myself help identify where I'm going wrong in calculating $this->_intersect, and confirm that I would be able to use the above Y = A * exp(B * X) if $this->_intersect (A) was being calculated correctly? Quote Link to comment https://forums.phpfreaks.com/topic/137835-solved-exponential-best-fit/ Share on other sites More sharing options...
Mark Baker Posted December 20, 2008 Author Share Posted December 20, 2008 As an example, I'm feeding the following data to the constructor: x Values: 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8 y Values: 2.633, 2.784, 2.955, 3.149, 3.368, 3.617, 3.899, 4.218, 4.58, 4.99, 5.455, 5.982, 6.578, 7.255, 8.021, 8.889 Using the Excel LOGEST function, I would expect a slope of 1.77018 and an intersect of 0.003762 giving an expected Y = 0.003762 * exp(1.77018 * X) Using my class, I'm getting a slope of 1.17701774774 and an intersect of 0.824153597845 giving an expected Y = 0.824153597845 * exp(1.17701774774 * X) The resulting best fit (returned as exp(X * slope + intersect)) has Y values of 2.47352819716, 2.6835418419, 2.91138658759, 3.15857637472, 3.42675368412, 3.71770045062, 4.03334990333, 4.37579941115, 4.74732441905, 5.15039356747, 5.58768509549, 6.06210463674, 6.57680452616, 7.13520474608, 7.74101565068, 8.39826261987 for my initial values of X This matches my expected best fit curve when I plot it on the chart. If I return the best fit as Y = intersect * exp(X * slope), then I get return values 0.894127936973, 0.970043411525, 1.05240445056, 1.14175831144, 1.23869871611, 1.34386979619, 1.45797037296, 1.58175860077, 1.71605700465, 1.86175794574, 2.0198295506, 2.19132214412, 2.37737522846, 2.57922505464, 2.79821283693, 3.03579366472 Clearly quite different, and nowhere near my expected curve Quote Link to comment https://forums.phpfreaks.com/topic/137835-solved-exponential-best-fit/#findComment-720514 Share on other sites More sharing options...
Mark Baker Posted December 21, 2008 Author Share Posted December 21, 2008 My bad on two counts, firstly for trusting LOGEST to give me correct results when the dataset doesn't include x=0 or x=1... I knew there were discrepancies between the trend values displayed in a chart and output by LOGEST, but didn't realise LOGEST itself returned relative intersect values based on the nearest datapoint to 0/1 rather than absolutes. Second, it's not Y = intersect * exp(slope * X) for an exponential curve, it's Y= intersect * slope^x; For anybody who is interested, here's my corrected code for the class: class exponentialBestFit { private $_xValues = array(); private $_yValues = array(); private $_goodnessOfFit = 1; private $_stdevOfResiduals = 0; private $_slope = 0; private $_increment = 0; public function getValueOfYForX($xValue) { return $this->getIntersect() * pow($this->getSlope(),$xValue); } // function getValueOfYForX() public function getSlope() { return exp($this->_slope); } // function getSlope() public function getIntersect() { return exp($this->_intersect); } // function getIntersect() public function getGoodnessOfFit() { return $this->_goodnessOfFit; } // function getYPointFromSlopeIntersect() public function getStdevOfResiduals() { return $this->_stdevOfResiduals; } // function getStdevOfResiduals() private function _calculateGoodnessOfFit() { // Calculate number of points $nY = count($this->_yValues); $meanY = array_sum($this->_yValues) / $nY; $SSreg = $SStot = 0.0; foreach($this->_xValues as $xKey => $xValue) { $bestFitY = $this->getValueOfYForX($xValue); $SSreg += ($this->_yValues[$xKey] - $bestFitY) * ($this->_yValues[$xKey] - $bestFitY); $SStot += ($this->_yValues[$xKey] - $meanY) * ($this->_yValues[$xKey] - $meanY); } $this->_stdevOfResiduals = sqrt($SSreg / ($nY - 2)); if (($SStot == 0.0) || ($SSreg == $SStot)) { $this->_goodnessOfFit = 1; } else { $this->_goodnessOfFit = 1 - ($SSreg / $SStot); } } // function _calculateGoodnessOfFit() private function _exponential_regression($yValues, $xValues=array()) { // Calculate number of points $nY = count($yValues); $nX = count($xValues); // Define X Values if necessary if ($nX == 0) { $xValues = range(1,$nY); $nX = $nY; } // Ensure both arrays of points are the same size if ($nY != $nX) { trigger_error("exponential_regression(): Number of elements in coordinate arrays do not match.", E_USER_ERROR); } $this->_xValues = $xValues; $this->_yValues = $yValues; foreach($yValues as $key => $value) { $yValues[$key] = log($yValues[$key]); } // calculate sums $x_sum = array_sum($xValues); $y_sum = array_sum($yValues); $xx_sum = $xy_sum = 0; for($i = 0; $i < $nY; $i++) { $xy_sum += $xValues[$i] * $yValues[$i]; $xx_sum += $xValues[$i] * $xValues[$i]; } // calculate slope $this->_slope = (($nY * $xy_sum) - ($x_sum * $y_sum)) / (($nY * $xx_sum) - ($x_sum * $x_sum)); // calculate intersect $this->_intersect = ($y_sum - ($this->_slope * $x_sum)) / $nY; $this->_calculateGoodnessOfFit(); foreach($xValues as $xValue) { $this->_yBestFitValues[] = $this->getValueOfYForX($xValue); } } // function _exponential_regression() function __construct($yValues, $xValues=array()) { $this->_exponential_regression($yValues, $xValues); } // function __construct() } // class exponentialBestFit I'm putting together a bestfit package. I already have linear, and now exponential, and am hoping to do logarithmic, powers and polynomials as well... so I'm sure I'll be back with more questions when my own understanding of the math gets a bit flakey. Quote Link to comment https://forums.phpfreaks.com/topic/137835-solved-exponential-best-fit/#findComment-720646 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.