使用分段函数平滑实验数据

时间:2014-03-31 10:03:49

标签: java curve-fitting

我有一个单次测量与时间(约3000点)的数据集。我希望通过在其中拟合曲线来平滑数据。实验是一个多阶段的物理过程,所以我很确定单个多项式不适合整个集合。

因此,我正在观察一系列多项式。我想指定使用多少个多项式。在我看来这是一个相当简单的事情,我希望有一些预先建立的库来做到这一点。我在Apache Commons Math中看过org.apache.commons.math3.fitting.PolynomialFitter,但似乎只能使用单个多项式。

有人能建议最好的方法吗? Java首选,但我可以使用Python。

2 个答案:

答案 0 :(得分:2)

如果您正在寻找local regression,Commons Math会将其实现为LoessInterpolator。您将得到最终结果作为“样条”,即平滑的分段三次多项式序列。

答案 1 :(得分:1)

finmath lib中有一个名为curve的类,它实现了一些插值方案(线性,样条,akima等)。这些曲线可以将它们的点作为参数提供给求解器,然后您可以使用全局优化(如Levenberg Marquardt优化器)来最小化数据到曲线的距离(定义一些首选标准)。

这实际上是在" Curve Calibration"这是数学金融的应用。如果曲线中的点数(参数)与数据一样多,则可能非常合适。如果您的点数少于数据,则最适合您的标准。

Finfth lib中的Levenberg Marquardt是多线程的并且速度非常快(<<<<<<<<&n;&n;&n;

)<<<

免责声明:我是该图书馆的开发人员。

注意:我也喜欢commons-math,但是对于曲线拟合我还没有使用它(还),因为我需要(ed)特定于我的应用程序的某些拟合属性(数学金融)。

(编辑)

这是一个小型演示: (注意:此演示需要finmath-lib 1.2.13或mvn.finmath.net或{{3}处提供的当前1.2.12-SNAPSHOT (它与1.2.12不兼容)

package net.finmath.tests.marketdata.curves;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import org.junit.Test;

import net.finmath.marketdata.model.curves.Curve;
import net.finmath.marketdata.model.curves.CurveInterface;
import net.finmath.optimizer.LevenbergMarquardt;
import net.finmath.optimizer.SolverException;

/**
 * A short demo on how to use {@link net.finmath.marketdata.model.curves.Curve}.
 * 
 * @author Christian Fries
 */
public class CurveTest {

    private static NumberFormat numberFormat = new DecimalFormat("0.0000");

    /**
     * Run a short demo on how to use {@link net.finmath.marketdata.model.curves.Curve}.
     * 
     * @param args Not used.
     * @throws SolverException Thrown if optimizer fails.
     * @throws CloneNotSupportedException Thrown if curve cannot be cloned for optimization.
     */
    public static void main(String[] args) throws SolverException, CloneNotSupportedException {
        (new CurveTest()).testCurveFitting();
    }

    /**
     * Tests fitting of curve to given data.
     * 
     * @throws SolverException Thrown if optimizer fails.
     * @throws CloneNotSupportedException Thrown if curve cannot be cloned for optimization.
     */
    @Test
    public void testCurveFitting() throws SolverException, CloneNotSupportedException {

        /*
         * Build a curve (initial guess for our fitting problem, defines the times).
         */
        Curve.CurveBuilder curveBuilder = new Curve.CurveBuilder();

        curveBuilder.setInterpolationMethod(Curve.InterpolationMethod.LINEAR);
        curveBuilder.setExtrapolationMethod(Curve.ExtrapolationMethod.LINEAR);
        curveBuilder.setInterpolationEntity(Curve.InterpolationEntity.VALUE);

        // Add some points - which will not be fitted
        curveBuilder.addPoint(-1.0 /* time */, 1.0 /* value */, false /* isParameter */);
        curveBuilder.addPoint( 0.0 /* time */, 1.0 /* value */, false /* isParameter */);

        // Add some points - which will be fitted
        curveBuilder.addPoint( 0.5  /* time */, 2.0 /* value */, true /* isParameter */);
        curveBuilder.addPoint( 0.75 /* time */, 2.0 /* value */, true /* isParameter */);
        curveBuilder.addPoint( 1.0 /* time */, 2.0 /* value */, true /* isParameter */);
        curveBuilder.addPoint( 2.2 /* time */, 2.0 /* value */, true /* isParameter */);
        curveBuilder.addPoint( 3.0 /* time */, 2.0 /* value */, true /* isParameter */);

        final Curve curve = curveBuilder.build();

        /*
         * Create data to which the curve should be fitted to
         */
        final double[] givenTimes   = { 0.0,  0.5, 0.75, 1.0, 1.5, 1.75, 2.5 };
        final double[] givenValues  = { 3.5, 12.3, 13.2, 7.5, 5.5, 2.9,  4.4 };

        /*
         * Find a best fitting curve.
         */

        // Define the objective function
        LevenbergMarquardt optimizer = new LevenbergMarquardt(
                curve.getParameter()    /* initial parameters */,
                givenValues             /* target values */,
                100,                    /* max iterations */
                Runtime.getRuntime().availableProcessors() /* max number of threads */  
                ) {

            @Override
            public void setValues(double[] parameters, double[] values) throws SolverException {

                CurveInterface curveGuess = null;
                try {
                    curveGuess = curve.getCloneForParameter(parameters);
                } catch (CloneNotSupportedException e) {
                    throw new SolverException(e);
                }

                for(int valueIndex=0; valueIndex<values.length; valueIndex++) {
                    values[valueIndex] = curveGuess.getValue(givenTimes[valueIndex]);
                }
            }
        };

        // Fit the curve (find best parameters)
        optimizer.run();

        CurveInterface fittedCurve = curve.getCloneForParameter(optimizer.getBestFitParameters());

        // Print out fitted curve
        for(double time = -2.0; time < 5.0; time += 0.1) {
            System.out.println(numberFormat.format(time) + "\t" + numberFormat.format(fittedCurve.getValue(time)));
        }

        // Check fitted curve
        double errorSum = 0.0;
        for(int pointIndex = 0; pointIndex<givenTimes.length; pointIndex++) {
            errorSum += fittedCurve.getValue(givenTimes[pointIndex]) - givenValues[pointIndex];
        }
        System.out.println("Mean deviation: " + errorSum);

        /*
         * Test: With the given data, the fit cannot over come that at 0.0 we have an error of -2.5.
         * Hence we test if the mean deviation is -2.5 (the optimizer reduces the variance)
         */
        org.junit.Assert.assertTrue(Math.abs(errorSum - -2.5) < 1E-5);
    }
}