математика — как получить наклон линии линейной регрессии с помощью C ++?

Мне нужно добиться спада линейной регрессии, аналогичного тому, как реализована функция Excel в приведенной ниже ссылке:

http://office.microsoft.com/en-gb/excel-help/slope-function-HP010342903.aspx

Есть ли в C ++ библиотека или простое закодированное решение, созданное кем-то, которое может это сделать?
Я реализовал код в соответствии с этой формулой, однако он не всегда дает мне правильные результаты (взяты отсюда http://easycalculation.com/statistics/learn-regression.php) ….

Slope(b) = (NΣXY - (ΣX)(ΣY)) / (NΣX2 - (ΣX)2)
= ((5)*(1159.7)-(311)*(18.6))/((5)*(19359)-(311)2)
= (5798.5 - 5784.6)/(96795 - 96721)
= 13.9/74
= 0.19

Если я попробую это по следующим векторам, я получу неправильные результаты (я должен был ожидать 0.305556):
х = 6,5,11,7,5,4,4
у = 2,3,9,1,8,7,5

заранее спасибо

5

Решение

Почему бы вам просто не написать простой код, подобный этому (наверняка, не лучшее решение, просто пример, основанный на справочной статье):

double slope(const vector<double>& x, const vector<double>& y){
if(x.size() != y.size()){
throw exception("...");
}
double n = x.size();

double avgX = accumulate(x.begin(), x.end(), 0.0) / n;
double avgY = accumulate(y.begin(), y.end(), 0.0) / n;

double numerator = 0.0;
double denominator = 0.0;

for(int i=0; i<n; ++i){
numerator += (x[i] - avgX) * (y[i] - avgY);
denominator += (x[i] - avgX) * (x[i] - avgX);
}

if(denominator == 0){
throw exception("...");
}

return numerator / denominator;
}

Обратите внимание, что третий аргумент функции накопления должен быть 0.0, а не 0, иначе компилятор выведет его тип как int, и есть большие шансы, что результат вызовов накопления будет неправильным (это на самом деле неправильно при использовании MSVC2010 и mingw-w64 при передаче 0 в качестве третьего параметра).

14

Другие решения

Вот реализация C ++ 11:

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

double slope(const std::vector<double>& x, const std::vector<double>& y) {
const auto n    = x.size();
const auto s_x  = std::accumulate(x.begin(), x.end(), 0.0);
const auto s_y  = std::accumulate(y.begin(), y.end(), 0.0);
const auto s_xx = std::inner_product(x.begin(), x.end(), x.begin(), 0.0);
const auto s_xy = std::inner_product(x.begin(), x.end(), y.begin(), 0.0);
const auto a    = (n * s_xy - s_x * s_y) / (n * s_xx - s_x * s_x);
return a;
}

int main() {
std::vector<double> x{6, 5, 11, 7, 5, 4, 4};
std::vector<double> y{2, 3, 9, 1, 8, 7, 5};
std::cout << slope(x, y) << '\n';  // outputs 0.305556
}

Вы можете добавить тест для математических требований (x.size() == y.size() а также x не является константой) или, как показано выше, предположим, что об этом позаботится пользователь.

16

Ниже приведена шаблонная функция, которую я использую для линейной регрессии (подгонки). Для данных требуется std :: vector

template <typename T>
std::vector<T> GetLinearFit(const std::vector<T>& data)
{
T xSum = 0, ySum = 0, xxSum = 0, xySum = 0, slope, intercept;
std::vector<T> xData;
for (long i = 0; i < data.size(); i++)
{
xData.push_back(static_cast<T>(i));
}
for (long i = 0; i < data.size(); i++)
{
xSum += xData[i];
ySum += data[i];
xxSum += xData[i] * xData[i];
xySum += xData[i] * data[i];
}
slope = (data.size() * xySum - xSum * ySum) / (data.size() * xxSum - xSum * xSum);
intercept = (ySum - slope * xSum) / data.size();
std::vector<T> res;
res.push_back(slope);
res.push_back(intercept);
return res;
}

Функция возвращает вектор с первым элементом, являющимся наклоном, а вторым элементом, являющимся перехватом вашей линейной регрессии.

Пример его использования:

std::vector<double> myData;
myData.push_back(1);
myData.push_back(3);
myData.push_back(4);
myData.push_back(2);
myData.push_back(5);

std::vector<double> linearReg = GetLinearFit(myData);
double slope = linearReg[0];
double intercept = linearReg[1];

Обратите внимание, что функция предполагает, что у вас есть серия чисел для вашей оси X (что мне и нужно). Вы можете изменить это в функции, если хотите.

5

Мне нужно было создать подобную функцию, но мне нужно было справиться с кучей почти вертикальных наклонов. Я начал с кода Кассио Нери, а затем изменил его, чтобы пересчитать наклоны, которые кажутся более крутыми, чем 1, после зеркального отражения каждой точки вокруг линии x = y (что можно легко сделать, переключая значения x и y). Затем он отразит его обратно и вернет более точный уклон.

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

double slope(const std::vector<double>& x, const std::vector<double>& y) {

const double n     = x.size();
const double s_x   = std::accumulate(x.begin(), x.end(), 0.0);
const double s_y   = std::accumulate(y.begin(), y.end(), 0.0);
const double s_xx  = std::inner_product(x.begin(), x.end(), x.begin(), 0.0);
const double s_xy  = std::inner_product(x.begin(), x.end(), y.begin(), 0.0);
const double numer = n * s_xy - s_x * s_y;  // The same regardless of inversion (both terms here are commutative)
const double denom = n * s_xx - s_x * s_x;  // Will change if inverted; use this for now
double       a;

if (denom == 0) a = 2;  // If slope is vertical, force variable inversion calculation
else a = numer / denom;

if (std::abs(a) > 1) {  // Redo with variable inversion if slope is steeper than 1
const double s_yy = std::inner_product(y.begin(), y.end(), y.begin(), 0.0);
const double new_denom = n * s_yy - s_y * s_y;
a = new_denom / numer;  // Invert the fraction because we've mirrored it around x=y
}
return a;
}

int main() {
std::vector<double> x{6, 5, 11, 7, 5, 4, 4};
std::vector<double> y{2, 3, 9, 1, 8, 7, 5};
std::cout << slope(x, y) << '\n';
}
1
По вопросам рекламы [email protected]