2012-07-04 10 views
5

Jak używać Math Commons CurveFitter do dopasowania funkcji do zestawu danych? Powiedziano mi, aby używać CurveFitter z LevenbergMarquardtOptimizer i ParametricUnivariateFunction, ale nie wiem, co napisać w metodach Gradient i wartości parametrów ParametricUnivariateFunction. Poza tym, po ich napisaniu, jak uzyskać dopasowane parametry funkcji? Moja funkcja:Jak korzystać z 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); 
} 

Odpowiedz

11

Tak, jest to stara sprawa, ale wpadłem na tej samej kwestii w ostatnim czasie, a skończyło się na tym, aby zagłębić się w listach dyskusyjnych i kodu źródłowego Apache Commons Math to rozgryźć.

Ten interfejs API jest nadzwyczaj słabo udokumentowane, ale w bieżącym wersji Apache Commons Math (3.3+), istnieją dwie części, przy założeniu, że masz jedną zmienną z wieloma parametrami: Funkcja, aby pasował z (który realizuje ParametricUnivariateFunction) i instalator krzywej (który rozszerza AbstractCurveFitter).

Funkcja Fit

  • public double value(double t, double... parameters)
    • swoje równanie. Tutaj możesz umieścić swoją logikę fnc.
  • public double[] gradient(double t, double... parameters)
    • Zwraca tablicę częściowej pochodnej powyżej w odniesieniu do każdego z parametrów. This calculator może być pomocne, jeśli (jak ja) jesteś zardzewiały na swoim rachunku, ale każdy dobry system algebry komputerowej może obliczyć te wartości.

Curve Monter

  • protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points)
    • konfiguruje bandą boilerplate bzdura, i zwraca się problemem dla monter używać najmniejszych kwadratów.

Kładzenie to wszystko razem, oto przykład rozwiązania w konkretnym przypadku:

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

Jestem zdezorientowany co do zbierania punktów. Czy nie ma dla nich wartości X? Dlaczego cel zawiera tylko wartość Y? –

+0

Również w jaki sposób dodać ograniczenia dla parametrów (na przykład, że parametr a w f (x) = c * ln (a * x) musi zawsze być dodatni)? –

1

Wiem, że to pytanie jest dość stary i i80and robotę odpowiedzi na to, ale po prostu pomyślałem dodaj (dla przyszłych SO-erów), że istnieje całkiem prosty sposób obliczania pochodnych lub pochodnych częściowych za pomocą Apache Math (więc nie musisz robić własnego zróżnicowania w przypadku Jakobian Matrix). To jest DerivativeStructure.

Rozszerzanie i80and „s odpowiedź użyć klasy 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)); 
    } 
} 

I to wszystko.Wiem, że jest to dość skomplikowana/kłopotliwa klasa do wykorzystania, ale z pewnością przydaje się, gdy masz do czynienia z bardzo skomplikowanymi równaniami, które byłyby kłopotliwe w uzyskiwaniu częściowych pochodnych ręcznie (stało się to nie tak dawno temu), lub kiedy chcesz wyprowadzić pochodne cząstkowe, powiedz drugiemu lub trzeciemu rzędowi.

W przypadku drugiego, trzeciego, itd pochodnych rzędu, wszystko musisz zrobić, to:

// 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) 

Mam nadzieję, że to pomoże ktoś kiedyś.