如何计算PHP中的趋势线?

时间:2010-05-04 20:48:21

标签: php math coordinates curve-fitting least-squares

所以我已经阅读了计算图表趋势线的两个相关问题,但我仍然迷失了。

我有一个xy坐标数组,我想提出另一个xy坐标数组(可以是更少的坐标),它们代表使用PHP的对数趋势线。

我将这些数组传递给javascript以在客户端绘制图表。

3 个答案:

答案 0 :(得分:29)

对数最小二乘

由于我们可以通过获取log值的x将对数函数转换为线,因此我们可以执行linear least squares曲线拟合。事实上,我们已经为我们完成了工作,并在Math World提出了解决方案。

简而言之,我们提供的$X$Y值来自y = a + b * log(x)这样的分布。最小二乘法将给出一些值aFitbFit,以最小化从参数曲线到给定数据点的距离。

以下是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.17aFit = 9.7,这对于50个数据点非常接近。

alt text

对于下面评论中给出的示例数据,对数函数不适合。

alt text

最小二乘解是y = -514.734835478 + 2180.51562281 * log(x),它基本上是该域中的一条线。

答案 1 :(得分:3)

答案 2 :(得分:0)

万一任何人的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;
}