Преобразовать взвешенный код линейной регрессии из Python в JS

Я пытаюсь преобразовать код, который user3658307 с сайта Math любезно предоставил мне (в формате Python) здесь. Я не очень хорош в математике, но я думаю, что я делаю что-то не так, поскольку мой вывод, кажется, увеличивается линейно вместо того, что должен делать исходный код Python. Что я делаю неправильно или кто-то более опытный с этим видом исчисления может помочь мне преобразовать его в Javascript? Спасибо!

Код Python:

import os, sys, numpy as np
from sklearn import linear_model
x = np.array([13,14,15,16,17,18]).reshape((6,1))
yRaw = [0.015,0.01,0.005,0.002,0.001,0.0005]
y = np.array([ np.log(v) for v in yRaw ])
w = [100] + ([1]*4) + [10] # Weight the various data points
print("Transformed data\n"+str(x)+"\n"+str(y))
# Fit linear function to transformed data
regr = linear_model.LinearRegression()
regr.fit(x,y,sample_weight=w)
print('Coefficient: \n', regr.coef_)
print('Intercept: \n', regr.intercept_)
# Look at function explicitly
a,b = regr.coef_[0],regr.intercept_
f = lambda x: np.exp( a*x + b ) 
print('Outputs of function')
for x in range(1,19): print(str(x)+": "+str(f(x)))

Мой код ( fiddle (использует num.js) линейная функция отсюда):

var code = function() {
    var x = nj.array([13, 14, 15, 16, 17, 18]).reshape(6, 1);
    var yRaw = [0.015, 0.01, 0.005, 0.002, 0.001, 0.0005];
    var y = nj.array(nj.log(yRaw));
    //var w = 100 + (1 * 4) + 10 // Weight the various data points        
    var w = [100, 1, 1, 1, 1, 10];
    console.log('Transformed data', x.selection.data, y.selection.data);

    var array = [];
    for (var i = 0; i < 6; i++)
        array.push([x.selection.data[i], y.selection.data[i]]);

    var regr = weightedLinearRegression(array, w);

    var a = regr.equation[0];
    var b = regr.equation[1];
    var f = (x) => { return a * x + b };
    for (var i = 1; i <= 19; i++)
        console.log(i, f(i));
}

var weightedLinearRegression = function (data, weights) {

    var sums = { xw: 0, x: 0, yw: 0, y: 0, a: 0, b: 0 };

    // compute the weighted averages
    for (var i = 0; i < data.length; i++) {
        sums.xw += data[i][0] * weights[i];
        sums.yw += data[i][1] * weights[i];
        sums.x += data[i][0];
        sums.y += data[i][1];
    }

    var weightedX = sums.xw / sums.x;
    var weightedY = sums.yw / sums.y;

    // compute the gradient and intercept
    for (var i = 0; i < data.length; i++) {
        sums.a += (data[i][1] - weightedY) * (data[i][0] - weightedX) * weights[i];
        sums.b += (data[i][0] - weightedX) * (data[i][0] - weightedX) * weights[i];
    }

    var gradient = sums.a / sums.b;
    var intercept = (weightedY - weightedX) * gradient;
    var string = 'y = ' + Math.round(gradient * 100) / 100 + 'x + ' + Math.round(intercept * 100) / 100;
    var results = [];

    //interpolate result
    for (var i = 0, len = data.length; i < len; i++) {
        var coordinate = [data[i][0], data[i][0] * gradient + intercept];
        results.push(coordinate);
    }

    return { equation: [gradient, intercept], points: results, string: string };

}

code();

Правильный вывод Vs. Мой вывод:

MY OUTPUT (WRONG)     | CORRECT OUTPUT
-------------------------------------------
1 -3.4140439207888633 | 1 51.2498600531
2 1.6212123570552457  | 2 26.0231972143
3 6.656468634899355   | 3 13.2138271705
4 11.691724912743464  | 4 6.70959940293
5 16.726981190587573  | 5 3.40694059086
6 21.76223746843168   | 6 1.72994593159
7 26.797493746275787  | 7 0.878416528378
8 31.8327500241199    | 8 0.446034516593
9 36.868006301964016  | 9 0.226483431909
10 41.903262579808114 | 10 0.115001738702
11 46.93851885765223  | 11 0.0583945580167
12 51.97377513549634  | 12 0.0296510682748
13 57.00903141334045  | 13 0.0150559552071
14 62.04428769118455  | 14 0.00764497876085
15 67.07954396902866  | 15 0.00388189918541
16 72.11480024687278  | 16 0.00197111617404
17 77.15005652471689  | 17 0.00100087580485
18 82.185312802561    | 18 0.00050821579668

Любая помощь очень ценится. Спасибо!:)

1 ответ

Решение

Хорошо, после долгих исследований мне наконец удалось это сделать! К счастью, у кого-то еще была та же проблема, что и у меня, и я создал здесь класс для C#, который я преобразовал в JS. Я оставлю это здесь для будущих читателей, которые хотят взвешенную функцию линейной регрессии в JS без каких-либо внешних библиотек. Я не уверен, что внутренности правы (я отстой в математике), но это дает мне результат, который мне нужен:)

let linearRegressionWeighted = (values) => {

    let sumFn = (input) => {
        var total = 0;
        for (var i = 0; i < input.length; i++) {
            if (isNaN(input[i])) {
                continue;
            }
            total += Number(input[i]);
        }
        return total;
    }

    let sum = sumFn(values.map(x => x.weight));
    let xAvg = sumFn(values.map(x => x.x * x.weight)) / sum;
    let yAvg = sumFn(values.map(x => x.y * x.weight)) / sum;
    let sumXY = sumFn(values.map(x => (x.x - xAvg) * (x.y - yAvg) * x.weight));
    let sumXX = sumFn(values.map(x => Math.pow(x.x - xAvg, 2) * x.weight));

    let m = sumXX == 0 ? 0 : (sumXY / sumXX);
    let b = yAvg - m * xAvg;
    return {
        coefficient: m,
        intercept: b
    };
}

let r = linearRegressionWeighted(
    [
        { x: 13, y: Math.log(0.015), weight: 100 },
        { x: 14, y: Math.log(0.01), weight: 1 },
        { x: 15, y: Math.log(0.005), weight: 1 },
        { x: 16, y: Math.log(0.002), weight: 1 },
        { x: 17, y: Math.log(0.001), weight: 1 },
        { x: 18, y: Math.log(0.0005), weight: 10 },
    ]
);

let a = r.coefficient;
let b = r.intercept;
let f = (x) => Math.exp(a * x + b);

for (let i = 1; i < 19; i++)
    console.log(i, f(i));
Другие вопросы по тегам