2012-07-04 13 views
5

¿Cómo uso Math Commons CurveFitter para adaptar una función a un conjunto de datos? Me dijeron que use CurveFitter con LevenbergMarquardtOptimizer y ParametricUnivariateFunction, pero no sé qué escribir en el gradiente ParametricUnivariateFunction y los métodos de valor. Además, después de escribirlos, ¿cómo obtener los parámetros de la función ajustada? Mi función:¿Cómo usar Java Math Commons CurveFitter?

public static double fnc(double t, double a, double b, double c){ 
    return a * Math.pow(t, b) * Math.exp(-c * t); 
} 

Respuesta

11

Por lo tanto, esta es una vieja pregunta, pero me encontré con el mismo problema hace poco, y acabó tener que ahondar en las listas de correo y el código fuente de Apache Commons Matemáticas para averiguarlo.

Esta API está muy poco documentada, pero en la versión actual de Apache Commons Matemáticas (3.3+), hay dos partes, suponiendo que tiene una sola variable con múltiples parámetros: la función de encajar con (que implementa ParametricUnivariateFunction) y el ajustador de curva (que extiende AbstractCurveFitter).

función para ajustar la

  • public double value(double t, double... parameters)
    • Su ecuación. Aquí es donde pondría su lógica fnc.
  • public double[] gradient(double t, double... parameters)
    • Devuelve una matriz de derivada parcial de los anteriores con respecto a cada uno de los parámetros. This calculator puede ser útil si (como yo) está oxidado en su cálculo, pero cualquier buen sistema de álgebra computarizada puede calcular estos valores.

Curva ajustador

  • protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points)
    • establece un montón de basura repetitivo, y devuelve un problema de mínimos cuadrados para el montador de usar.

Poniendo todo junto, aquí es una solución de ejemplo en su caso específico:

import java.util.*; 
import org.apache.commons.math3.analysis.ParametricUnivariateFunction; 
import org.apache.commons.math3.fitting.AbstractCurveFitter; 
import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder; 
import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem; 
import org.apache.commons.math3.fitting.WeightedObservedPoint; 
import org.apache.commons.math3.linear.DiagonalMatrix; 

class MyFunc implements ParametricUnivariateFunction { 
    public double value(double t, double... parameters) { 
     return parameters[0] * Math.pow(t, parameters[1]) * Math.exp(-parameters[2] * t); 
    } 

    // Jacobian matrix of the above. In this case, this is just an array of 
    // partial derivatives of the above function, with one element for each parameter. 
    public double[] gradient(double t, double... parameters) { 
     final double a = parameters[0]; 
     final double b = parameters[1]; 
     final double c = parameters[2]; 

     return new double[] { 
      Math.exp(-c*t) * Math.pow(t, b), 
      a * Math.exp(-c*t) * Math.pow(t, b) * Math.log(t), 
      a * (-Math.exp(-c*t)) * Math.pow(t, b+1) 
     }; 
    } 
} 

public class MyFuncFitter extends AbstractCurveFitter { 
    protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points) { 
     final int len = points.size(); 
     final double[] target = new double[len]; 
     final double[] weights = new double[len]; 
     final double[] initialGuess = { 1.0, 1.0, 1.0 }; 

     int i = 0; 
     for(WeightedObservedPoint point : points) { 
      target[i] = point.getY(); 
      weights[i] = point.getWeight(); 
      i += 1; 
     } 

     final AbstractCurveFitter.TheoreticalValuesFunction model = new 
      AbstractCurveFitter.TheoreticalValuesFunction(new MyFunc(), points); 

     return new LeastSquaresBuilder(). 
      maxEvaluations(Integer.MAX_VALUE). 
      maxIterations(Integer.MAX_VALUE). 
      start(initialGuess). 
      target(target). 
      weight(new DiagonalMatrix(weights)). 
      model(model.getModelFunction(), model.getModelFunctionJacobian()). 
      build(); 
    } 

    public static void main(String[] args) { 
     MyFuncFitter fitter = new MyFuncFitter(); 
     ArrayList<WeightedObservedPoint> points = new ArrayList<WeightedObservedPoint>(); 

     // Add points here; for instance, 
     WeightedObservedPoint point = new WeightedObservedPoint(1.0, 
      1.0, 
      1.0); 
     points.add(point); 

     final double coeffs[] = fitter.fit(points); 
     System.out.println(Arrays.toString(coeffs)); 
    } 
} 
+1

Estoy confundido acerca de la colección de puntos. ¿No hay X_value para ellos? ¿Por qué el objetivo solo contiene el valor Y? –

+0

También, ¿cómo agregaría restricciones a los parámetros (por ejemplo, que el parámetro a en f (x) = c * ln (a * x) debe ser siempre positivo)? –

1

Sé que bastante viejo y i80and hizo un excelente trabajo de responder a esta, pero me acaba de ocurrir a esta pregunta agregue (para futuros SO-ers) que hay una manera bastante fácil de calcular derivadas o derivadas parciales con Apache Math (para que no tenga que hacer su propia diferenciación para la matriz jacobiana). Es el DerivativeStructure.

Extendiendo respuesta i80and 's utilizar la clase DerivativeStructure:

//Everything stays the same except for the Jacobian Matrix 

import java.util.*; 
import org.apache.commons.math3.analysis.ParametricUnivariateFunction; 
import org.apache.commons.math3.fitting.AbstractCurveFitter; 
import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder; 
import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem; 
import org.apache.commons.math3.fitting.WeightedObservedPoint; 
import org.apache.commons.math3.linear.DiagonalMatrix; 
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure; 

class MyFunc implements ParametricUnivariateFunction { 
    public double value(double t, double... parameters) { 
     return parameters[0] * Math.pow(t, parameters[1]) * Math.exp(-parameters[2] * t); 
    } 

    // Jacobian matrix of the above. In this case, this is just an array of 
    // partial derivatives of the above function, with one element for each parameter. 
    public double[] gradient(double t, double... parameters) { 
     final double a = parameters[0]; 
     final double b = parameters[1]; 
     final double c = parameters[2]; 

     // Jacobian Matrix Edit 

     // Using Derivative Structures... 
     // constructor takes 4 arguments - the number of parameters in your 
     // equation to be differentiated (3 in this case), the order of 
     // differentiation for the DerivativeStructure, the index of the 
     // parameter represented by the DS, and the value of the parameter itself 
     DerivativeStructure aDev = new DerivativeStructure(3, 1, 0, a); 
     DerivativeStructure bDev = new DerivativeStructure(3, 1, 1, b); 
     DerivativeStructure cDev = new DerivativeStructure(3, 1, 2, c); 

     // define the equation to be differentiated using another DerivativeStructure 
     DerivativeStructure y = aDev.multiply(DerivativeStructure.pow(t, bDev)) 
       .multiply(cDev.negate().multiply(t).exp()); 

     // then return the partial derivatives required 
     // notice the format, 3 arguments for the method since 3 parameters were 
     // specified first order derivative of the first parameter, then the second, 
     // then the third 
     return new double[] { 
       y.getPartialDerivative(1, 0, 0), 
       y.getPartialDerivative(0, 1, 0), 
       y.getPartialDerivative(0, 0, 1) 
     }; 

    } 
} 

public class MyFuncFitter extends AbstractCurveFitter { 
    protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points) { 
     final int len = points.size(); 
     final double[] target = new double[len]; 
     final double[] weights = new double[len]; 
     final double[] initialGuess = { 1.0, 1.0, 1.0 }; 

     int i = 0; 
     for(WeightedObservedPoint point : points) { 
      target[i] = point.getY(); 
      weights[i] = point.getWeight(); 
      i += 1; 
     } 

     final AbstractCurveFitter.TheoreticalValuesFunction model = new 
       AbstractCurveFitter.TheoreticalValuesFunction(new MyFunc(), points); 

     return new LeastSquaresBuilder(). 
       maxEvaluations(Integer.MAX_VALUE). 
       maxIterations(Integer.MAX_VALUE). 
       start(initialGuess). 
       target(target). 
       weight(new DiagonalMatrix(weights)). 
       model(model.getModelFunction(), model.getModelFunctionJacobian()). 
       build(); 
    } 

    public static void main(String[] args) { 
     MyFuncFitter fitter = new MyFuncFitter(); 
     ArrayList<WeightedObservedPoint> points = new ArrayList<WeightedObservedPoint>(); 

     // Add points here; for instance, 
     WeightedObservedPoint point = new WeightedObservedPoint(1.0, 
       1.0, 
       1.0); 
     points.add(point); 

     final double coeffs[] = fitter.fit(points); 
     System.out.println(Arrays.toString(coeffs)); 
    } 
} 

y eso es todo.Sé que es una clase bastante intrincada/confusa de usar, pero definitivamente es útil cuando se trata de ecuaciones muy complicadas que serían problemáticas para obtener derivadas parciales de la mano (esto me pasó no hace mucho), o cuando desee derivar derivados parciales, diga al segundo o tercer orden.

En el caso de los derivados segundo, tercero, etcétera orden, todo lo que tiene que hacer es:

// specify the required order as the second argument, say second order so 2 
DerivativeStructure aDev = new DerivativeStructure(3, 2, 0, a);   
DerivativeStructure bDev = new DerivativeStructure(3, 2, 1, b); 
DerivativeStructure cDev = new DerivativeStructure(3, 2, 2, c); 

// and then specify the order again here 
y.getPartialDerivative(2, 0, 0), 
y.getPartialDerivative(0, 2, 0), 
y.getPartialDerivative(0, 0, 2) 

Con suerte, esto ayuda a alguien en algún momento.