001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math.analysis.interpolation;
018
019 import static org.junit.Assert.assertEquals;
020 import static org.junit.Assert.assertTrue;
021 import static org.junit.Assert.fail;
022
023 import org.apache.commons.math.MathException;
024 import org.junit.Test;
025
026 /**
027 * Test of the LoessInterpolator class.
028 */
029 public class LoessInterpolatorTest {
030
031 @Test
032 public void testOnOnePoint() throws MathException {
033 double[] xval = {0.5};
034 double[] yval = {0.7};
035 double[] res = new LoessInterpolator().smooth(xval, yval);
036 assertEquals(1, res.length);
037 assertEquals(0.7, res[0], 0.0);
038 }
039
040 @Test
041 public void testOnTwoPoints() throws MathException {
042 double[] xval = {0.5, 0.6};
043 double[] yval = {0.7, 0.8};
044 double[] res = new LoessInterpolator().smooth(xval, yval);
045 assertEquals(2, res.length);
046 assertEquals(0.7, res[0], 0.0);
047 assertEquals(0.8, res[1], 0.0);
048 }
049
050 @Test
051 public void testOnStraightLine() throws MathException {
052 double[] xval = {1,2,3,4,5};
053 double[] yval = {2,4,6,8,10};
054 LoessInterpolator li = new LoessInterpolator(0.6, 2);
055 double[] res = li.smooth(xval, yval);
056 assertEquals(5, res.length);
057 for(int i = 0; i < 5; ++i) {
058 assertEquals(yval[i], res[i], 1e-8);
059 }
060 }
061
062 @Test
063 public void testOnDistortedSine() throws MathException {
064 int numPoints = 100;
065 double[] xval = new double[numPoints];
066 double[] yval = new double[numPoints];
067 double xnoise = 0.1;
068 double ynoise = 0.2;
069
070 generateSineData(xval, yval, xnoise, ynoise);
071
072 LoessInterpolator li = new LoessInterpolator(0.3, 4);
073
074 double[] res = li.smooth(xval, yval);
075
076 // Check that the resulting curve differs from
077 // the "real" sine less than the jittered one
078
079 double noisyResidualSum = 0;
080 double fitResidualSum = 0;
081
082 for(int i = 0; i < numPoints; ++i) {
083 double expected = Math.sin(xval[i]);
084 double noisy = yval[i];
085 double fit = res[i];
086
087 noisyResidualSum += Math.pow(noisy - expected, 2);
088 fitResidualSum += Math.pow(fit - expected, 2);
089 }
090
091 assertTrue(fitResidualSum < noisyResidualSum);
092 }
093
094 @Test
095 public void testIncreasingBandwidthIncreasesSmoothness() throws MathException {
096 int numPoints = 100;
097 double[] xval = new double[numPoints];
098 double[] yval = new double[numPoints];
099 double xnoise = 0.1;
100 double ynoise = 0.1;
101
102 generateSineData(xval, yval, xnoise, ynoise);
103
104 // Check that variance decreases as bandwidth increases
105
106 double[] bandwidths = {0.1, 0.5, 1.0};
107 double[] variances = new double[bandwidths.length];
108 for (int i = 0; i < bandwidths.length; i++) {
109 double bw = bandwidths[i];
110
111 LoessInterpolator li = new LoessInterpolator(bw, 4);
112
113 double[] res = li.smooth(xval, yval);
114
115 for (int j = 1; j < res.length; ++j) {
116 variances[i] += Math.pow(res[j] - res[j-1], 2);
117 }
118 }
119
120 for(int i = 1; i < variances.length; ++i) {
121 assertTrue(variances[i] < variances[i-1]);
122 }
123 }
124
125 @Test
126 public void testIncreasingRobustnessItersIncreasesSmoothnessWithOutliers() throws MathException {
127 int numPoints = 100;
128 double[] xval = new double[numPoints];
129 double[] yval = new double[numPoints];
130 double xnoise = 0.1;
131 double ynoise = 0.1;
132
133 generateSineData(xval, yval, xnoise, ynoise);
134
135 // Introduce a couple of outliers
136 yval[numPoints/3] *= 100;
137 yval[2 * numPoints/3] *= -100;
138
139 // Check that variance decreases as the number of robustness
140 // iterations increases
141
142 double[] variances = new double[4];
143 for (int i = 0; i < 4; i++) {
144 LoessInterpolator li = new LoessInterpolator(0.3, i);
145
146 double[] res = li.smooth(xval, yval);
147
148 for (int j = 1; j < res.length; ++j) {
149 variances[i] += Math.abs(res[j] - res[j-1]);
150 }
151 }
152
153 for(int i = 1; i < variances.length; ++i) {
154 assertTrue(variances[i] < variances[i-1]);
155 }
156 }
157
158 @Test
159 public void testUnequalSizeArguments() {
160 try {
161 new LoessInterpolator().smooth(new double[] {1,2,3}, new double[] {1,2,3,4});
162 fail();
163 } catch(MathException e) {
164 // Expected
165 }
166 }
167
168 @Test
169 public void testEmptyData() {
170 try {
171 new LoessInterpolator().smooth(new double[] {}, new double[] {});
172 fail();
173 } catch(MathException e) {
174 // Expected
175 }
176 }
177
178 @Test
179 public void testNonStrictlyIncreasing() {
180 try {
181 new LoessInterpolator().smooth(new double[] {4,3,1,2}, new double[] {3,4,5,6});
182 fail();
183 } catch(MathException e) {
184 // Expected
185 }
186 try {
187 new LoessInterpolator().smooth(new double[] {1,2,2,3}, new double[] {3,4,5,6});
188 fail();
189 } catch(MathException e) {
190 // Expected
191 }
192 }
193
194 @Test
195 public void testNotAllFiniteReal() {
196 try {
197 new LoessInterpolator().smooth(new double[] {1,2,Double.NaN}, new double[] {3,4,5});
198 fail();
199 } catch(MathException e) {
200 // Expected
201 }
202 try {
203 new LoessInterpolator().smooth(new double[] {1,2,Double.POSITIVE_INFINITY}, new double[] {3,4,5});
204 fail();
205 } catch(MathException e) {
206 // Expected
207 }
208 try {
209 new LoessInterpolator().smooth(new double[] {1,2,Double.NEGATIVE_INFINITY}, new double[] {3,4,5});
210 fail();
211 } catch(MathException e) {
212 // Expected
213 }
214 try {
215 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NaN});
216 fail();
217 } catch(MathException e) {
218 // Expected
219 }
220 try {
221 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.POSITIVE_INFINITY});
222 fail();
223 } catch(MathException e) {
224 // Expected
225 }
226 try {
227 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NEGATIVE_INFINITY});
228 fail();
229 } catch(MathException e) {
230 // Expected
231 }
232 }
233
234 @Test
235 public void testInsufficientBandwidth() {
236 try {
237 LoessInterpolator li = new LoessInterpolator(0.1, 3);
238 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});
239 fail();
240 } catch(MathException e) {
241 // Expected
242 }
243 }
244
245 @Test
246 public void testCompletelyIncorrectBandwidth() {
247 try {
248 new LoessInterpolator(-0.2, 3);
249 fail();
250 } catch(MathException e) {
251 // Expected
252 }
253 try {
254 new LoessInterpolator(1.1, 3);
255 fail();
256 } catch(MathException e) {
257 // Expected
258 }
259 }
260
261 private void generateSineData(double[] xval, double[] yval, double xnoise, double ynoise) {
262 double dx = 2 * Math.PI / xval.length;
263 double x = 0;
264 for(int i = 0; i < xval.length; ++i) {
265 xval[i] = x;
266 yval[i] = Math.sin(x) + (2 * Math.random() - 1) * ynoise;
267 x += dx * (1 + (2 * Math.random() - 1) * xnoise);
268 }
269 }
270
271 }