【问题标题】:How to use Java Math Commons CurveFitter?如何使用 Java Math Commons CurveFitter?
【发布时间】:2012-07-05 07:32:36
【问题描述】:

如何使用 Math Commons CurveFitter 将函数拟合到一组数据?有人告诉我将 CurveFitter 与 LevenbergMarquardtOptimizerParametricUnivariateFunction 一起使用,但我不知道在 ParametricUnivariateFunction 梯度和值方法中要写什么。另外,写完之后,如何获取拟合的函数参数呢?我的功能:

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

【问题讨论】:

    标签: java math curve-fitting


    【解决方案1】:

    所以,这是一个老问题,但我最近遇到了同样的问题,最终不得不深入研究邮件列表和 Apache Commons Math 源代码来解决这个问题。

    此 API 的文档记录非常差,但在 当前 版本的 Apache Common Math (3.3+) 中,假设您有一个带有多个参数的变量,则分为两部分:要拟合的函数with(实现 ParametricUnivariateFunction)和曲线拟合器(扩展 AbstractCurveFitter)。

    适合的功能

    • public double value(double t, double... parameters)
      • 你的方程式。这是您放置 fnc 逻辑的地方。
    • public double[] gradient(double t, double... parameters)
      • 返回上述关于每个参数的偏导数数组。 This calculator 如果您(像我一样)对微积分不熟悉,可能会有所帮助,但是任何好的计算机代数系统都可以计算这些值。

    曲线拟合器

    • protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points)
      • 设置一堆样板废话,并返回一个最小二乘问题供装配工使用。

    综上所述,这是您特定情况下的示例解决方案:

    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));
        }
    }
    

    【讨论】:

    • 我对积分收集感到困惑。他们没有X_value吗?为什么目标只包含 Y 值?
    • 另外,我将如何对参数添加约束(例如 f(x) = cln(ax) 中的参数 a 必须始终为正)?跨度>
    【解决方案2】:

    我知道这个问题已经很老了,i80and 很好地回答了这个问题,但我只是想补充一下(对于未来的 SO-ers),有一种非常简单的方法可以使用 Apache Math 计算导数或偏导数(所以你不必为雅可比矩阵做你自己的微分)。这是DerivativeStructure

    扩展i80and 的答案以使用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));
        }
    }
    

    就是这样。我知道这是一个使用起来非常复杂/令人困惑的类,但是当您处理非常复杂的方程时,它肯定会派上用场,而手动获得偏导数会很麻烦(这发生在我不久前),或者当您想导出偏导数时,请说二阶或三阶。

    对于二阶、三阶等阶导数,您只需:

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

    希望这对某些人有所帮助。

    【讨论】:

      猜你喜欢
      • 2012-04-23
      • 2015-01-21
      • 2021-12-12
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      • 2016-02-28
      • 2020-08-29
      • 1970-01-01
      相关资源
      最近更新 更多