首页 文章

C#中的非线性回归

提问于
浏览
24

我正在寻找一种基于2D数据集生成非线性(最好是二次)曲线的方法,用于预测目的 . 现在我正在使用我自己的普通最小二乘(OLS)实现来产生线性趋势,但我的趋势更适合曲线模型 . 我正在分析的数据是系统负载随着时间的推移 .

这是我用来生成线性系数的等式:

Ordinary Least Squares (OLS) formula

我已经看过Math.NET Numerics和其他一些库,但它们要么提供插值而不是回归(这对我来说没用),或者代码不能以某种方式工作 .

任何人都知道任何可以产生这种曲线系数的免费开源库或代码示例吗?

4 回答

  • 6

    我使用了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结果 .
    Quadratic Equation

    Quadratic Fit

    Edit 1 要获得所需的拟合,请尝试以下初始化 x_datay_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

    @ 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上找到 .

  • 26

    我不认为你想要非线性回归 . 即使您使用二次函数,它仍然称为线性回归 . 你想要的是多变量回归 . 如果你想要一个二次方,你只需要为你的因变量添加一个x平方项 .

  • 0

    我会看看http://mathforum.org/library/drmath/view/53796.html,试着了解它是如何完成的 .

    然后this有一个很好的实现,我认为会帮助你 .

相关问题