Кубическая регрессия (линия наилучшего соответствия) в JavaScript
У меня сейчас самое плохое время, когда я пытаюсь найти код JavaScript, который мог бы позволить мне делать кубические регрессии. Я бы написал это сам, но мое понимание математики полиномов, ну, в общем, неоптимально.
Итак, вот что я ищу. Учитывая ввод массива массивов, где внутренний массив будет [x,y], функция выдаст мне вывод в виде массива с четырьмя параметрами - [a, b, c, d], где a, b, c и d - параметры уравнения y = ax^3 + bx^2 + cx + d.
Пример: входные данные - это массив [[2,5],[5,10],[07,15],[12,20],[20,25],[32,30],[50,35]].
Что по сути является представлением таблицы:
| х | у | |-----------------| | 02 | 05 | | 05 | 10 | | 07 | 15 | | 12 | 20 | | 20 | 25 | | 32 | 30 | | 50 | 35 |
Теперь результат будет [0.000575085,-0.058861065,2.183957502,1.127605507]. Это параметры a, b, c и d кубической функции.
(К вашему сведению, вывод, который я получил, используя функцию LINEST в Excel и запустив ее на указанном выше наборе чисел, используя функцию массива {1,2,3}).
Как это можно сделать? Огромное спасибо заранее за любые указания.
Лучший, Том
2 ответа
Вот настоящий, работающий кусочек кода для решения этой кубики с помощью библиотеки numeric.js uncmin
минимизатор без ограничений как задача наименьших квадратов ( здесь jsbin):
var data_x = [2,5,7,12,20,32,50];
var data_y = [5,10,15,20,25,30,35];
var cubic = function(params,x) {
return params[0] * x*x*x +
params[1] * x*x +
params[2] * x +
params[3];
};
var objective = function(params) {
var total = 0.0;
for(var i=0; i < data_x.length; ++i) {
var resultThisDatum = cubic(params, data_x[i]);
var delta = resultThisDatum - data_y[i];
total += (delta*delta);
}
return total;
};
var initial = [1,1,1,1];
var minimiser = numeric.uncmin(objective,initial);
console.log("initial:");
for(var j=0; j<initial.length; ++j) {
console.log(initial[j]);
}
console.log("minimiser:");
for(var j=0; j<minimiser.solution.length; ++j) {
console.log(minimiser.solution[j]);
}
Я получаю результаты:
0.0005750849851827991
-0.05886106462847641
2.1839575020602164
1.1276055079334206
Чтобы объяснить: у нас есть функция "кубическая", которая оценивает общую кубическую функцию для набора параметров params
и значение x
, Эта функция упакована для создания целевой функции, которая принимает набор параметров и запускает каждое значение x из нашего набора данных через целевую функцию и вычисляет сумму квадратов. Эта функция передается uncmin
из numeric.js с набором начальных значений; uncmin
делает тяжелую работу и возвращает объект, чей solution
свойство содержит оптимизированный набор параметров.
Чтобы сделать это без глобальных переменных (непослушный!), Вы можете создать фабрику целевых функций:
var makeObjective = function(targetFunc,xlist,ylist) {
var objective = function(params) {
var total = 0.0;
for(var i=0; i < xlist.length; ++i) {
var resultThisDatum = targetFunc(params, xlist[i]);
var delta = resultThisDatum - ylist[i];
total += (delta*delta);
}
return total;
};
return objective;
};
Которые вы можете использовать для производства целевых функций:
var objective = makeObjective(cubic, data_x, data_y); // then carry on as before
Знание того, как это сделать, практически помогло бы многим людям, поэтому я рад, что это произошло.
Изменить: разъяснение по cubic
var cubic = function(params,x) {
return params[0] * x*x*x +
params[1] * x*x +
params[2] * x +
params[3];
};
Cubic определяется как функция, которая принимает массив параметров params
и значение x
, Дано params
мы можем определить функцию f(x)
, Для кубика это f(x) = a x^3 + b x^2 + c x + d
так что есть 4 параметра ([0]
в [3]
), и с учетом этих 4 значений параметров мы имеем одну функцию f(x)
с 1 входом x
,
Код структурирован, чтобы позволить вам заменить cubic
с другой функцией той же структуры; возможно linear
с 2 параметрами:
var linear = function(params, x) {
return params[0]*x + params[1];
};
Остальная часть кода будет смотреть на длину params
чтобы узнать, сколько параметров нужно изменить.
Обратите внимание, что весь этот фрагмент кода пытается найти набор значений параметров, которые создают кривую, которая наилучшим образом соответствует всем данным; если вы хотите найти соответствие для последних 4 точек некоторых данных, вы должны передать только эти значения в data_x
а также data_y
,
Я бы сформулировал это как проблему наименьших квадратов. Пусть M будет матрица n× 4, сформированная так:
x_1^3 x_1^2 x_1 1
x_2^3 x_2^2 x_2 1
⋮ ⋮ ⋮
x_n^3 x_n^2 x_n 1
Затем вычислите матрицу 4×4 A=MT⋅M и вектор столбцов 4×1 b=MT⋅y и решите линейную систему уравнений Aξ=b. Результирующий вектор ξ будет содержать ваши коэффициенты от a до d.
Приведенное выше описание позволяет легко понять, что происходит, математически. Однако для реализации, особенно для очень больших n, вышеупомянутый подход может оказаться невозможным. В этих случаях вы можете построить A и b напрямую, без явного построения M. Например, A1,2=sum(x_i^3 * x_i^2 for all i)
, Таким образом, вы можете перебирать все i и добавлять соответствующие значения в соответствующие записи матрицы и вектора.