web-dev-qa-db-ja.com

C#での非線形回帰

予測の目的で、2Dデータセットに基づいて非線形(できれば2次)曲線を生成する方法を探しています。現在、線形トレンドを生成するために通常の最小二乗(OLS)の独自の実装を使用していますが、私のトレンドは曲線モデルにはるかに適しています。私が分析しているデータは、時間の経過に伴うシステム負荷です。

線形係数を生成するために使用している方程式は次のとおりです。

Ordinary Least Squares (OLS) formula

Math.NET Numericsと他のいくつかのライブラリを見てきましたが、それらはregression(私には役に立たない)の代わりにinterpolationを提供するか、コードが何らかの方法で機能しないだけです。

そのような曲線の係数を生成できる無料のオープンソースライブラリまたはコードサンプルを知っている人はいますか?

24
Polynomial

MathNet.Iridium リリースを使用しました。これは、.NET3.5およびVS2008と互換性があるためです。この方法は、 ファンデルモンド 行列に基づいています。次に、多項式回帰を保持するクラスを作成しました

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

quadratic WolframAlphaの結果と一致します。 Quadratic EquationQuadratic Fit

編集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
26
ja72

@ 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 でも利用できます。

10
Ivan Kochurkin

非線形回帰は必要ないと思います。二次関数を使用している場合でも、線形回帰と呼ばれます。必要なのは多変量回帰と呼ばれます。二次式が必要な場合は、従属変数にxの2乗項を追加するだけです。

6
Tim

http://mathforum.org/library/drmath/view/53796.html を見て、それがどのように行われるかについてのアイデアを得ようとします。

次に this には、役立つと思う素晴らしい実装があります。

0