首页 > 解决方案 > 线性回归结果不如预期,经过简单测试

问题描述

我有一个从 Ray Wenderlich 借来的函数,用于对一系列点进行线性回归:

extension Array where Element == CGFloat {
    // A closed form solution
    fileprivate var average: CGFloat {
        return self.reduce(0, +) / CGFloat(self.count)
    }
}

extension CGFloat {
    fileprivate static func multiply(_ a: [CGFloat], _ b: [CGFloat]) -> [CGFloat] {
        return zip(a,b).map(*)
    }

    static func linearRegression(a: [CGFloat], b: [CGFloat]) -> (_ a: CGFloat) -> CGFloat {
        let sum1 = CGFloat.multiply(b, a).average - a.average * b.average
        let sum2 = CGFloat.multiply(a, a).average - pow(a.average, 2)
        let slope = sum1 / sum2
        let intercept = b.average - slope * a.average
        return { x in intercept + slope * x }
    }
}

我添加了一个简单的测试以确保它给我我期望的结果:

class CGFloatExtensionsTests: XCTestCase {
    func testLinearRegression() {
        let points = [
            CGPoint(x: 1, y: 2),
            CGPoint(x: 2, y: 1),
            CGPoint(x: 3, y: 4),
            CGPoint(x: 4, y: 3)
        ]

        let linearRegression = CGFloat.linearRegression(a: points.map({$0.x}), b: points.map({$0.y}))

        let y1 = linearRegression(1)
        let y2 = linearRegression(3)

        XCTAssertEqual(y1, 1, accuracy: 0.0001)
        XCTAssertEqual(y2, 3, accuracy: 0.0001)
    }
}

所以我平等地放置点,并期望线性回归线应该穿过这些点的中间。然而,这两个测试都失败了,第一个预期结果是1.6,第二个是2.8

为了证明..蓝色是要点。绿色是我预期的线性回归线,黄色是实际的线性回归线。

在此处输入图像描述

我对此的期望/理解是否不正确?算法不正确吗?

标签: swiftalgorithmlinear-regressioncgpoint

解决方案


你的期望是错误的。简单的线性回归是直线

y = a * x + b

最小化观测 y 值和插值 y 值之差的平方和,即 (a, b) 确定为

sum( (a * xi + b - yi)^2, i=1,...,n)

尽可能小。为了你的价值观

(1, 2), (2, 1), (3, 4), (4, 3)

这是行

y = 0.6 x + 1

差的平方和为 3.2。对于这条线y = x,平方差之和更大,即 4.0。


推荐阅读