/*
* Copyright (c) 2012 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.optimize;
import java.util.Arrays;
import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.fitting.PolynomialFitter;
import org.apache.commons.math3.optim.nonlinear.vector.jacobian.LevenbergMarquardtOptimizer;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
/**
* Class to contain methods to fit univariate polynomials
*/
public class ApachePolynomial {
/**
* Fit a polynomial to given x and y data
* @param x
* @param y
* @param order order or highest degree of terms in polynomial
* @return coefficients of fitted polynomial (given in increasing degree of the terms)
*/
public static double[] polynomialFit(Dataset x, Dataset y, int order) {
double[] guess = new double[order + 1];
Arrays.fill(guess, 1);
return polynomialFit(x, y, guess);
}
/**
* Fit a polynomial to given x and y data
* @param x
* @param y
* @param guess initial values for coefficients (given in increasing degree of the terms)
* @return coefficients of fitted polynomial
*/
public static double[] polynomialFit(Dataset x, Dataset y, double... guess) {
PolynomialFitter fitter = new PolynomialFitter(new LevenbergMarquardtOptimizer());
for (int i = 0; i < y.getSize(); i++) {
fitter.addObservedPoint(1,x.getDouble(i),y.getDouble(i));
}
return fitter.fit(guess);
}
/**
* Calculate a polynomial filtered dataset using a specified window size and polynomial order.
*
* @param x The abscissa (generally energy/wavelength etc)
* @param y The ordinate to be smoothed
* @param windowSize The size of window used in the smoothing
* @param polyOrder The order of the polynomial fitted to the window
* @return result The smoothed data set
* @throws Exception
*/
public static Dataset getPolynomialSmoothed(final Dataset x, final Dataset y,
int windowSize, int polyOrder) throws Exception {
// Could probably do with more sanity check on relative size of
// window vs polynomial but doesn't seem to trip up
// So we'll see how it goes...
// integer divide window size so window is symmetric around point
int window = windowSize/2;
PolynomialFitter fitter = new PolynomialFitter(new LevenbergMarquardtOptimizer());
double dx = x.getDouble(1) - x.getDouble(0); // change in x for edge extrapolation
int xs = x.getSize();
Dataset result = DatasetFactory.zeros(y);
double[] guess = new double[polyOrder+1];
for (int idx = 0; idx < xs; idx++) {
fitter.clearObservations();
// Deal with edge cases:
// In both cases extend x edge by dx required for window size
// Pad y with first or last value
for (int idw = -window; idw < window+1; idw++) {
if (idx+idw < 0) {
fitter.addObservedPoint(1,x.getDouble(0)+(dx*(idx+idw)), y.getDouble(0));
} else if ((idx + idw) > (xs-1)) {
fitter.addObservedPoint(1,x.getDouble(xs-1) + dx*(idx + idw -(xs-1)), y.getDouble(xs-1));
} else {
fitter.addObservedPoint(1,x.getDouble(idx+idw), y.getDouble(idx+idw));
}
}
PolynomialFunction fitted = new PolynomialFunction(fitter.fit(guess));
result.set(fitted.value(x.getDouble(idx)), idx);
}
return result;
}
}