标签归档:最小二乘法

PHP 拟合函数计算趋势线

    // 拟合算法(最小二乘法)
    // 输入: $values = [2, 3, 5, 4, 7, 8, 3, 2, 6];
    // 输出: $values = [3.578, 3.794, 4.011, 4.228, 4.444, 4.661, 4.878, 5.094, 5.311];
    // 斜率(正切值): k = y / x;   =>   5.311 - 3.578 / 9 = 0.1925,最终结果是负无穷 -> 0 -> 正无穷
    // k=0 说明无斜率,与X轴平行, k=1说明斜度为45度,大于1说明斜度大于45度(分正负)
    // atan(k)获取弧度角,弧度换算为角度:atan(k) * (180 / M_PI);
    public function trendLine(array $values)
    {
        $n = count($values);
        $sumX = 0;
        $sumY = 0.0;
        $sumXX = 0;
        $sumXY = 0.0;
        for($i = 1; $i <= $n; $i++) {
            $sumX += $i;
            $sumY += $values[$i - 1];
            $sumXX += $i * $i;
            $sumXY += $i * $values[$i - 1];
        }
        // 求a,b
        $b = ($n * $sumXY - $sumX * $sumY) / ($n * $sumXX - $sumX * $sumX);
        $a = ($sumY - $b * $sumX) / $n;
        // 返回趋势线y值
        $ys = [];
        for($i = 1; $i <= $n; $i++) {
            $ys[$i - 1] = round($b * $i + $a, 3);
        }
        // 斜率(正切值)
        $slope = ($ys[$n -1] - $ys[0]) / $n;
        // 弧度
        $radians = atan($slope);
        // 角度(1度 = 180 / M_PI = 57.297弧度)
        $degree = $radians * 57.297;

        return [
            'slope' => $slope,
            'radians' => $radians,
            'degree' => $degree,
            'values' => $ys
        ];
    }

至于最小二乘法的公式和原理可以搜索一下。根据这个计算的结果,可以得到一条直线的趋势线,通常我们顺便计算一下斜率以及夹角大小(弧度角和角度)。