Saltar al contenido

Regresión no lineal en C#

Es imprescindible comprender el código de forma correcta antes de utilizarlo a tu trabajo y si ttienes algo que aportar puedes dejarlo en la sección de comentarios.

Solución:

Usé la versión MathNet.Iridium porque es compatible con .NET 3.5 y VS2008. El método se basa en la matriz de Vandermonde. Luego creé una clase para contener mi regresión polinomial

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 

    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;  

que luego lo uso así:

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,61,9", "x", "y");
        for (int i = 0; i < 10; i++)
        
            double x = (i * 0.5);
            double y = poly.Fit(x);

            Console.WriteLine("0,6:F21,9:F4", x, y);
        
    

Coeficientes calculados de [1,0.57,-0.15] con la salida:

    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

Lo que coincide con los resultados cuadráticos de Wolfram Alpha.
Ecuación cuadrática

Ajuste cuadrático

Editar 1
Para obtener el ajuste que desea, intente la siguiente inicialización para x_data y 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);

que produce los siguientes coeficientes (de menor potencia a mayor)

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

El código @ ja72 es bastante bueno. Pero lo porté en la versión actual de Math.NET (MathNet.Iridium no es compatible por ahora, según tengo entendido) y optimicé el tamaño y el rendimiento del código (por ejemplo, Math.Pow La función no se usa en mi solución debido a un rendimiento lento).

public class PolynomialRegression

    private int _order;
    private Vector _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 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++)
        
            double v_j = v[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;
    

También está disponible en github:gist.

No creo que quieras una regresión no lineal. Incluso si está utilizando una función cuadrática, todavía se llama regresión lineal. Lo que quieres se llama regresión multivariada. Si desea una cuadrática, simplemente agregue un término cuadrático a sus variables dependientes.

Sección de Reseñas y Valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *