У меня есть набор данных для одного измерения по времени (около 3000 точек). Я хотел бы сгладить данные, установив через него кривую. Эксперимент представляет собой многоэтапный физический процесс, поэтому я уверен, что один полином не будет соответствовать всему набору.
Поэтому я смотрю на кусочную серию многочленов. Я хотел бы указать, сколько полиномов используется. Мне кажется, что это довольно простая вещь, и я надеялся, что для этого будет создана некоторая библиотека. Я видел org.apache.commons.math3.fitting.PolynomialFitter
в Apache Commons Math, но, похоже, работает только с одним полиномом.
Может ли кто-нибудь предложить лучший способ сделать это? Java предпочла, но я мог бы работать на Python.
В finmath lib существует класс, называемый кривой, который реализует некоторые интерполяционные схемы (линейные, сплайны, акимы и т.д.). Эти кривые могут предоставить свои точки в качестве параметров решателю, и затем вы можете использовать глобальную оптимизацию (например, оптимизатор Levenberg Marquardt), чтобы минимизировать расстояние ваших данных до кривой (определяя некоторую предпочтительную норму).
Фактически это делается в " Калибровке кривой ", которая является приложением из математического финансирования. Если у вас столько точек (параметров) на кривой, сколько данных, вы, вероятно, получите идеальную форму. Если у вас меньше очков, чем данных, вы получите наилучшее соответствие вашей норме.
Levenberg Marquardt в finmath lib многопоточный и очень быстрый (> 200 точек установлены в << 1 сек).
Видеть
Отказ от ответственности: Я/разработчик этой библиотеки.
Примечание: Мне также нравится commons-math, но для подгонки кривой я не использую его (пока), так как мне нужны (некоторые) подходящие свойства, характерные для моего приложения (математическое финансирование).
(Редактировать)
Вот небольшая демонстрация: (Примечание: для этой демонстрации требуется finmath-lib 1.2.13 или текущий 1.2.12-SNAPSHOT, доступный на mvn.finmath.net или github.com/finmath/finmath-lib (он несовместим с 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);
}
}
Если вы ищете местную регрессию, Commons Math реализует ее как LoessInterpolator
. Вы получите конечный результат как "сплайн", гладкую последовательность кусочно-кубических многочленов.