r_squared.js

/**
 * [决定系数(R²)](http://en.wikipedia.org/wiki/Coefficient_of_determination)
 * 用于评估回归函数`f`对数据的拟合程度,计算预测值与实际值的平方差异和。
 *
 * @param {Array<Array<number>>} x 输入数据:二维数组结构,每项格式为[自变量, 因变量]
 * @param {Function} func 回归函数(接受自变量值返回预测值)
 * @returns {number} R²值(范围[0,1])
 * @example
 * var samples = [[0, 0], [1, 1]];
 * var regressionLine = linearRegressionLine(linearRegression(samples));
 * rSquared(samples, regressionLine); // = 1 表示完美拟合
 */
function rSquared(x, func) {
    // 数据量不足时直接返回完美拟合
    if (x.length < 2) {
        return 1;
    }

    // 计算实际数据的因变量平均值(用于计算总平方和SST)
    let sum = 0;
    for (let i = 0; i < x.length; i++) {
        sum += x[i][1];
    }
    const average = sum / x.length;

    // 计算总平方和(SST):各数据点与均值的平方差异和
    let sumOfSquares = 0;
    for (let j = 0; j < x.length; j++) {
        sumOfSquares += Math.pow(average - x[j][1], 2);
    }

    // 计算误差平方和(SSE):预测值与实际值的平方差异和
    let err = 0;
    for (let k = 0; k < x.length; k++) {
        err += Math.pow(x[k][1] - func(x[k][0]), 2);
    }

    // 计算R²值:1 - (SSE/SST)
    // 当误差增大时,其与总平方和的比率上升,导致R²值降低
    return 1 - err / sumOfSquares;
}

export default rSquared;