blob: 7cdea6eca87469f07a1f6acd5b97f184bc8233bb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.analysis.interpolation;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.exception.OutOfRangeException;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.NoDataException;
import org.apache.commons.math3.exception.NonMonotonicSequenceException;
import org.apache.commons.math3.exception.NotFiniteNumberException;
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.junit.Assert;
import org.junit.Test;
/**
* Test of the LoessInterpolator class.
*/
public class LoessInterpolatorTest {
@Test
public void testOnOnePoint() {
double[] xval = {0.5};
double[] yval = {0.7};
double[] res = new LoessInterpolator().smooth(xval, yval);
Assert.assertEquals(1, res.length);
Assert.assertEquals(0.7, res[0], 0.0);
}
@Test
public void testOnTwoPoints() {
double[] xval = {0.5, 0.6};
double[] yval = {0.7, 0.8};
double[] res = new LoessInterpolator().smooth(xval, yval);
Assert.assertEquals(2, res.length);
Assert.assertEquals(0.7, res[0], 0.0);
Assert.assertEquals(0.8, res[1], 0.0);
}
@Test
public void testOnStraightLine() {
double[] xval = {1,2,3,4,5};
double[] yval = {2,4,6,8,10};
LoessInterpolator li = new LoessInterpolator(0.6, 2, 1e-12);
double[] res = li.smooth(xval, yval);
Assert.assertEquals(5, res.length);
for(int i = 0; i < 5; ++i) {
Assert.assertEquals(yval[i], res[i], 1e-8);
}
}
@Test
public void testOnDistortedSine() {
int numPoints = 100;
double[] xval = new double[numPoints];
double[] yval = new double[numPoints];
double xnoise = 0.1;
double ynoise = 0.2;
generateSineData(xval, yval, xnoise, ynoise);
LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
double[] res = li.smooth(xval, yval);
// Check that the resulting curve differs from
// the "real" sine less than the jittered one
double noisyResidualSum = 0;
double fitResidualSum = 0;
for(int i = 0; i < numPoints; ++i) {
double expected = FastMath.sin(xval[i]);
double noisy = yval[i];
double fit = res[i];
noisyResidualSum += FastMath.pow(noisy - expected, 2);
fitResidualSum += FastMath.pow(fit - expected, 2);
}
Assert.assertTrue(fitResidualSum < noisyResidualSum);
}
@Test
public void testIncreasingBandwidthIncreasesSmoothness() {
int numPoints = 100;
double[] xval = new double[numPoints];
double[] yval = new double[numPoints];
double xnoise = 0.1;
double ynoise = 0.1;
generateSineData(xval, yval, xnoise, ynoise);
// Check that variance decreases as bandwidth increases
double[] bandwidths = {0.1, 0.5, 1.0};
double[] variances = new double[bandwidths.length];
for (int i = 0; i < bandwidths.length; i++) {
double bw = bandwidths[i];
LoessInterpolator li = new LoessInterpolator(bw, 4, 1e-12);
double[] res = li.smooth(xval, yval);
for (int j = 1; j < res.length; ++j) {
variances[i] += FastMath.pow(res[j] - res[j-1], 2);
}
}
for(int i = 1; i < variances.length; ++i) {
Assert.assertTrue(variances[i] < variances[i-1]);
}
}
@Test
public void testIncreasingRobustnessItersIncreasesSmoothnessWithOutliers() {
int numPoints = 100;
double[] xval = new double[numPoints];
double[] yval = new double[numPoints];
double xnoise = 0.1;
double ynoise = 0.1;
generateSineData(xval, yval, xnoise, ynoise);
// Introduce a couple of outliers
yval[numPoints/3] *= 100;
yval[2 * numPoints/3] *= -100;
// Check that variance decreases as the number of robustness
// iterations increases
double[] variances = new double[4];
for (int i = 0; i < 4; i++) {
LoessInterpolator li = new LoessInterpolator(0.3, i, 1e-12);
double[] res = li.smooth(xval, yval);
for (int j = 1; j < res.length; ++j) {
variances[i] += FastMath.abs(res[j] - res[j-1]);
}
}
for(int i = 1; i < variances.length; ++i) {
Assert.assertTrue(variances[i] < variances[i-1]);
}
}
@Test(expected=DimensionMismatchException.class)
public void testUnequalSizeArguments() {
new LoessInterpolator().smooth(new double[] {1,2,3}, new double[] {1,2,3,4});
}
@Test(expected=NoDataException.class)
public void testEmptyData() {
new LoessInterpolator().smooth(new double[] {}, new double[] {});
}
@Test(expected=NonMonotonicSequenceException.class)
public void testNonStrictlyIncreasing1() {
new LoessInterpolator().smooth(new double[] {4,3,1,2}, new double[] {3,4,5,6});
}
@Test(expected=NonMonotonicSequenceException.class)
public void testNonStrictlyIncreasing2() {
new LoessInterpolator().smooth(new double[] {1,2,2,3}, new double[] {3,4,5,6});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal1() {
new LoessInterpolator().smooth(new double[] {1,2,Double.NaN}, new double[] {3,4,5});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal2() {
new LoessInterpolator().smooth(new double[] {1,2,Double.POSITIVE_INFINITY}, new double[] {3,4,5});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal3() {
new LoessInterpolator().smooth(new double[] {1,2,Double.NEGATIVE_INFINITY}, new double[] {3,4,5});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal4() {
new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NaN});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal5() {
new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.POSITIVE_INFINITY});
}
@Test(expected=NotFiniteNumberException.class)
public void testNotAllFiniteReal6() {
new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NEGATIVE_INFINITY});
}
@Test(expected=NumberIsTooSmallException.class)
public void testInsufficientBandwidth() {
LoessInterpolator li = new LoessInterpolator(0.1, 3, 1e-12);
li.smooth(new double[] {1,2,3,4,5,6,7,8,9,10,11,12}, new double[] {1,2,3,4,5,6,7,8,9,10,11,12});
}
@Test(expected=OutOfRangeException.class)
public void testCompletelyIncorrectBandwidth1() {
new LoessInterpolator(-0.2, 3, 1e-12);
}
@Test(expected=OutOfRangeException.class)
public void testCompletelyIncorrectBandwidth2() {
new LoessInterpolator(1.1, 3, 1e-12);
}
@Test
public void testMath296withoutWeights() {
double[] xval = {
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0};
double[] yval = {
0.47, 0.48, 0.55, 0.56, -0.08, -0.04, -0.07, -0.07,
-0.56, -0.46, -0.56, -0.52, -3.03, -3.08, -3.09,
-3.04, 3.54, 3.46, 3.36, 3.35};
// Output from R, rounded to .001
double[] yref = {
0.461, 0.499, 0.541, 0.308, 0.175, -0.042, -0.072,
-0.196, -0.311, -0.446, -0.557, -1.497, -2.133,
-3.08, -3.09, -0.621, 0.982, 3.449, 3.389, 3.336
};
LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
double[] res = li.smooth(xval, yval);
Assert.assertEquals(xval.length, res.length);
for(int i = 0; i < res.length; ++i) {
Assert.assertEquals(yref[i], res[i], 0.02);
}
}
private void generateSineData(double[] xval, double[] yval, double xnoise, double ynoise) {
double dx = 2 * FastMath.PI / xval.length;
double x = 0;
for(int i = 0; i < xval.length; ++i) {
xval[i] = x;
yval[i] = FastMath.sin(x) + (2 * FastMath.random() - 1) * ynoise;
x += dx * (1 + (2 * FastMath.random() - 1) * xnoise);
}
}
}