Нелинейная регрессия в C # - PullRequest
24 голосов
/ 17 октября 2011

Я ищу способ получения нелинейной (предпочтительно квадратичной) кривой, основанной на наборе двумерных данных, для целей прогнозирования.Прямо сейчас я использую мою собственную реализацию обычных наименьших квадратов (OLS) для получения линейного тренда, но мои тренды гораздо больше подходят для кривой модели.Данные, которые я анализирую, являются нагрузкой на систему со временем.

Вот уравнение, которое я использую для получения моих линейных коэффициентов:

Ordinary Least Squares (OLS) formula

У меня естьвзглянул на Math.NET Numerics и несколько других библиотек, но они либо обеспечивают интерполяцию вместо регрессии (которая мне не нужна), либо код просто не 'не работает каким-то образом.

Кто-нибудь знает какие-либо бесплатные библиотеки с открытым исходным кодом или примеры кода, которые могут создать коэффициенты для такой кривой?

Ответы [ 4 ]

26 голосов
/ 04 ноября 2011

Я использовал MathNet.Iridium , потому что он совместим с .NET 3.5 и VS2008.Метод основан на матрице Vandermonde .Затем я создал класс для хранения моей полиномиальной регрессии

using MathNet.Numerics.LinearAlgebra;

public class PolynomialRegression
{
    Vector x_data, y_data, coef;
    int order;

    public PolynomialRegression(Vector x_data, Vector y_data, int order)
    {
        if (x_data.Length != y_data.Length)
        {
            throw new IndexOutOfRangeException();
        }
        this.x_data = x_data;
        this.y_data = y_data;
        this.order = order;
        int N = x_data.Length;
        Matrix A = new Matrix(N, order + 1);
        for (int i = 0; i < N; i++)
        {
            A.SetRowVector( VandermondeRow(x_data[i]) , i);
        }

        // Least Squares of |y=A(x)*c| 
        //  tr(A)*y = tr(A)*A*c
        //  inv(tr(A)*A)*tr(A)*y = c
        Matrix At = Matrix.Transpose(A);
        Matrix y2 = new Matrix(y_data, N);
        coef = (At * A).Solve(At * y2).GetColumnVector(0);
    }

    Vector VandermondeRow(double x)
    {
        double[] row = new double[order + 1];
        for (int i = 0; i <= order; i++)
        {
            row[i] = Math.Pow(x, i);
        }
        return new Vector(row);
    }

    public double Fit(double x)
    {
        return Vector.ScalarProduct( VandermondeRow(x) , coef);
    }

    public int Order { get { return order; } }
    public Vector Coefficients { get { return coef; } }
    public Vector XData { get { return x_data; } }
    public Vector YData { get { return y_data; } }
}

, которую затем использую следующим образом:

using MathNet.Numerics.LinearAlgebra;

class Program
{
    static void Main(string[] args)
    {
        Vector x_data = new Vector(new double[] { 0, 1, 2, 3, 4 });
        Vector y_data = new Vector(new double[] { 1.0, 1.4, 1.6, 1.3, 0.9 });

        var poly = new PolynomialRegression(x_data, y_data, 2);

        Console.WriteLine("{0,6}{1,9}", "x", "y");
        for (int i = 0; i < 10; i++)
        {
            double x = (i * 0.5);
            double y = poly.Fit(x);

            Console.WriteLine("{0,6:F2}{1,9:F4}", x, y);
        }
    }
}

Расчетные коэффициенты [1,0.57,-0.15] с выводом:

    x        y
 0.00   1.0000
 0.50   1.2475
 1.00   1.4200
 1.50   1.5175
 2.00   1.5400
 2.50   1.4875
 3.00   1.3600
 3.50   1.1575
 4.00   0.8800
 4.50   0.5275

Что соответствует квадратичному результату Wolfram Alpha.Quadratic Equation Quadratic Fit

Изменить 1 Чтобы получить нужную посадку, попробуйте следующую инициализацию для x_data и y_data:

Matrix points = new Matrix( new double[,] {  {  1, 82.96 }, 
               {  2, 86.23 }, {  3, 87.09 }, {  4, 84.28 }, 
               {  5, 83.69 }, {  6, 89.18 }, {  7, 85.71 }, 
               {  8, 85.05 }, {  9, 85.58 }, { 10, 86.95 }, 
               { 11, 87.95 }, { 12, 89.44 }, { 13, 93.47 } } );
Vector x_data = points.GetColumnVector(0);
Vector y_data = points.GetColumnVector(1);

, котораяпроизводит следующие коэффициенты (от самой низкой мощности до самой высокой)

Coef=[85.892,-0.5542,0.074990]
     x        y
  0.00  85.8920
  1.00  85.4127
  2.00  85.0835
  3.00  84.9043
  4.00  84.8750
  5.00  84.9957
  6.00  85.2664
  7.00  85.6871
  8.00  86.2577
  9.00  86.9783
 10.00  87.8490
 11.00  88.8695
 12.00  90.0401
 13.00  91.3607
 14.00  92.8312
10 голосов
/ 07 октября 2012

@ ja72 код довольно хорош. Но я перенес его на текущую версию Math.NET (MathNet.Iridium сейчас не поддерживается, насколько я понимаю) и оптимизировал как размер кода, так и производительность (функция Math.Pow не используется в моем решении из-за низкая производительность).

public class PolynomialRegression
{
    private int _order;
    private Vector<double> _coefs;

    public PolynomialRegression(DenseVector xData, DenseVector yData, int order)
    {
        _order = order;
        int n = xData.Count;

        var vandMatrix = new DenseMatrix(xData.Count, order + 1);
        for (int i = 0; i < n; i++)
            vandMatrix.SetRow(i, VandermondeRow(xData[i]));

        // var vandMatrixT = vandMatrix.Transpose();
        // 1 variant:
        //_coefs = (vandMatrixT * vandMatrix).Inverse() * vandMatrixT * yData;
        // 2 variant:
        //_coefs = (vandMatrixT * vandMatrix).LU().Solve(vandMatrixT * yData);
        // 3 variant (most fast I think. Possible LU decomposion also can be replaced with one triangular matrix):
        _coefs = vandMatrix.TransposeThisAndMultiply(vandMatrix).LU().Solve(TransposeAndMult(vandMatrix, yData));
    }

    private Vector<double> VandermondeRow(double x)
    {
        double[] result = new double[_order + 1];
        double mult = 1;
        for (int i = 0; i <= _order; i++)
        {
            result[i] = mult;
            mult *= x;
        }
        return new DenseVector(result);
    }

    private static DenseVector TransposeAndMult(Matrix m, Vector v)
    {
        var result = new DenseVector(m.ColumnCount);
        for (int j = 0; j < m.RowCount; j++)
            for (int i = 0; i < m.ColumnCount; i++)
                result[i] += m[j, i] * v[j];
        return result;
    }

    public double Calculate(double x)
    {
        return VandermondeRow(x) * _coefs;
    }
}

Он также доступен на github: gist .

6 голосов
/ 04 ноября 2011

Я не думаю, что вы хотите нелинейной регрессии.Даже если вы используете квадратичную функцию, она все равно называется линейной регрессией.То, что вы хотите, называется многомерной регрессией.Если вам нужен квадратик, вы просто добавляете квадрат квадрата топора к зависимым переменным.

0 голосов
/ 17 октября 2011

Я бы взглянул на http://mathforum.org/library/drmath/view/53796.html, чтобы попытаться получить представление о том, как это можно сделать.

Тогда эта имеет хорошую реализацию, которая, я думаю, поможетвы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...