【发布时间】:2011-02-15 16:02:01
【问题描述】:
所以我已经阅读了计算图表趋势线的两个相关问题,但我仍然迷路了。
我有一个 xy 坐标数组,我想提出另一个 xy 坐标数组(可以是更少的坐标),它表示使用 PHP 的对数趋势线。
我将这些数组传递给 javascript 以在客户端绘制图表。
【问题讨论】:
标签: php math coordinates curve-fitting least-squares
所以我已经阅读了计算图表趋势线的两个相关问题,但我仍然迷路了。
我有一个 xy 坐标数组,我想提出另一个 xy 坐标数组(可以是更少的坐标),它表示使用 PHP 的对数趋势线。
我将这些数组传递给 javascript 以在客户端绘制图表。
【问题讨论】:
标签: php math coordinates curve-fitting least-squares
由于我们可以通过取x 值中的log 将对数函数转换为一条线,因此我们可以执行linear least squares 曲线拟合。事实上,这项工作已经为我们完成,并在Math World 提出了解决方案。
简而言之,我们得到了来自y = a + b * log(x) 等分布的$X 和$Y 值。最小二乘法将给出一些值aFit 和bFit,以最小化从参数曲线到给定数据点的距离。
以下是 PHP 中的示例实现:
首先,我将生成一些由$a 和$b 给出的具有已知基础分布的随机数据
// True parameter valaues
$a = 10;
$b = 5;
// Range of x values to generate
$x_min = 1;
$x_max = 10;
$nPoints = 50;
// Generate some random points on y = a * log(x) + b
$X = array();
$Y = array();
for($p = 0; $p < $nPoints; $p++){
$x = $p / $nPoints * ($x_max - $x_min) + $x_min;
$y = $a + $b * log($x);
$X[] = $x + rand(0, 200) / ($nPoints * $x_max);
$Y[] = $y + rand(0, 200) / ($nPoints * $x_max);
}
现在,这里是如何使用给出的方程来估计 $a 和 $b。
// Now convert to log-scale for X
$logX = array_map('log', $X);
// Now estimate $a and $b using equations from Math World
$n = count($X);
$square = create_function('$x', 'return pow($x,2);');
$x_squared = array_sum(array_map($square, $logX));
$xy = array_sum(array_map(create_function('$x,$y', 'return $x*$y;'), $logX, $Y));
$bFit = ($n * $xy - array_sum($Y) * array_sum($logX)) /
($n * $x_squared - pow(array_sum($logX), 2));
$aFit = (array_sum($Y) - $bFit * array_sum($logX)) / $n;
然后,您可以根据需要为您的 Javascript 生成点数:
$Yfit = array();
foreach($X as $x) {
$Yfit[] = $aFit + $bFit * log($x);
}
在这种情况下,代码估计 bFit = 5.17 和 aFit = 9.7,仅对 50 数据点非常接近。
对于下面评论中给出的示例数据,对数函数不能很好地拟合。
最小二乘解是y = -514.734835478 + 2180.51562281 * log(x),它本质上是该域中的一条线。
【讨论】:
【讨论】:
如果有人在使用 create_function 时遇到问题,以下是我编辑它的方法。 (虽然我没有使用日志,所以我确实把它们拿出来了。)
我还减少了计算次数并添加了 R2。到目前为止它似乎有效。
function lsq(){
$X = array(1,2,3,4,5);
$Y = array(.3,.2,.7,.9,.8);
// Now estimate $a and $b using equations from Math World
$n = count($X);
$mult_elem = function($x,$y){ //anon function mult array elements
$output=$x*$y; //will be called on each element
return $output;
};
$sumX2 = array_sum(array_map($mult_elem, $X, $X));
$sumXY = array_sum(array_map($mult_elem, $X, $Y));
$sumY = array_sum($Y);
$sumX = array_sum($X);
$bFit = ($n * $sumXY - $sumY * $sumX) /
($n * $sumX2 - pow($sumX, 2));
$aFit = ($sumY - $bFit * $sumX) / $n;
echo ' intercept ',$aFit,' ';
echo ' slope ',$bFit,' ' ;
//r2
$sumY2 = array_sum(array_map($mult_elem, $Y, $Y));
$top=($n*$sumXY-$sumY*$sumX);
$bottom=($n*$sumX2-$sumX*$sumX)*($n*$sumY2-$sumY*$sumY);
$r2=pow($top/sqrt($bottom),2);
echo ' r2 ',$r2;
}
【讨论】: