1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.analysis.interpolation;
18
19 import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
20 import org.apache.commons.math4.legacy.exception.NoDataException;
21 import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
22 import org.apache.commons.math4.legacy.exception.NotFiniteNumberException;
23 import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
24 import org.apache.commons.math4.legacy.exception.OutOfRangeException;
25 import org.apache.commons.math4.core.jdkmath.JdkMath;
26 import org.junit.Assert;
27 import org.junit.Test;
28
29
30
31
32 public class LoessInterpolatorTest {
33
34 @Test
35 public void testOnOnePoint() {
36 double[] xval = {0.5};
37 double[] yval = {0.7};
38 double[] res = new LoessInterpolator().smooth(xval, yval);
39 Assert.assertEquals(1, res.length);
40 Assert.assertEquals(0.7, res[0], 0.0);
41 }
42
43 @Test
44 public void testOnTwoPoints() {
45 double[] xval = {0.5, 0.6};
46 double[] yval = {0.7, 0.8};
47 double[] res = new LoessInterpolator().smooth(xval, yval);
48 Assert.assertEquals(2, res.length);
49 Assert.assertEquals(0.7, res[0], 0.0);
50 Assert.assertEquals(0.8, res[1], 0.0);
51 }
52
53 @Test
54 public void testOnStraightLine() {
55 double[] xval = {1,2,3,4,5};
56 double[] yval = {2,4,6,8,10};
57 LoessInterpolator li = new LoessInterpolator(0.6, 2, 1e-12);
58 double[] res = li.smooth(xval, yval);
59 Assert.assertEquals(5, res.length);
60 for(int i = 0; i < 5; ++i) {
61 Assert.assertEquals(yval[i], res[i], 1e-8);
62 }
63 }
64
65 @Test
66 public void testOnDistortedSine() {
67 int numPoints = 100;
68 double[] xval = new double[numPoints];
69 double[] yval = new double[numPoints];
70 double xnoise = 0.1;
71 double ynoise = 0.2;
72
73 generateSineData(xval, yval, xnoise, ynoise);
74
75 LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
76
77 double[] res = li.smooth(xval, yval);
78
79
80
81
82 double noisyResidualSum = 0;
83 double fitResidualSum = 0;
84
85 for(int i = 0; i < numPoints; ++i) {
86 double expected = JdkMath.sin(xval[i]);
87 double noisy = yval[i];
88 double fit = res[i];
89
90 noisyResidualSum += JdkMath.pow(noisy - expected, 2);
91 fitResidualSum += JdkMath.pow(fit - expected, 2);
92 }
93
94 Assert.assertTrue(fitResidualSum < noisyResidualSum);
95 }
96
97 @Test
98 public void testIncreasingBandwidthIncreasesSmoothness() {
99 int numPoints = 100;
100 double[] xval = new double[numPoints];
101 double[] yval = new double[numPoints];
102 double xnoise = 0.1;
103 double ynoise = 0.1;
104
105 generateSineData(xval, yval, xnoise, ynoise);
106
107
108
109 double[] bandwidths = {0.1, 0.5, 1.0};
110 double[] variances = new double[bandwidths.length];
111 for (int i = 0; i < bandwidths.length; i++) {
112 double bw = bandwidths[i];
113
114 LoessInterpolator li = new LoessInterpolator(bw, 4, 1e-12);
115
116 double[] res = li.smooth(xval, yval);
117
118 for (int j = 1; j < res.length; ++j) {
119 variances[i] += JdkMath.pow(res[j] - res[j-1], 2);
120 }
121 }
122
123 for(int i = 1; i < variances.length; ++i) {
124 Assert.assertTrue(variances[i] < variances[i-1]);
125 }
126 }
127
128 @Test
129 public void testIncreasingRobustnessItersIncreasesSmoothnessWithOutliers() {
130 int numPoints = 100;
131 double[] xval = new double[numPoints];
132 double[] yval = new double[numPoints];
133 double xnoise = 0.1;
134 double ynoise = 0.1;
135
136 generateSineData(xval, yval, xnoise, ynoise);
137
138
139 yval[numPoints/3] *= 100;
140 yval[2 * numPoints/3] *= -100;
141
142
143
144
145 double[] variances = new double[4];
146 for (int i = 0; i < 4; i++) {
147 LoessInterpolator li = new LoessInterpolator(0.3, i, 1e-12);
148
149 double[] res = li.smooth(xval, yval);
150
151 for (int j = 1; j < res.length; ++j) {
152 variances[i] += JdkMath.abs(res[j] - res[j-1]);
153 }
154 }
155
156 for(int i = 1; i < variances.length; ++i) {
157 Assert.assertTrue(variances[i] < variances[i-1]);
158 }
159 }
160
161 @Test(expected=DimensionMismatchException.class)
162 public void testUnequalSizeArguments() {
163 new LoessInterpolator().smooth(new double[] {1,2,3}, new double[] {1,2,3,4});
164 }
165
166 @Test(expected=NoDataException.class)
167 public void testEmptyData() {
168 new LoessInterpolator().smooth(new double[] {}, new double[] {});
169 }
170
171 @Test(expected=NonMonotonicSequenceException.class)
172 public void testNonStrictlyIncreasing1() {
173 new LoessInterpolator().smooth(new double[] {4,3,1,2}, new double[] {3,4,5,6});
174 }
175
176 @Test(expected=NonMonotonicSequenceException.class)
177 public void testNonStrictlyIncreasing2() {
178 new LoessInterpolator().smooth(new double[] {1,2,2,3}, new double[] {3,4,5,6});
179 }
180
181 @Test(expected=NotFiniteNumberException.class)
182 public void testNotAllFiniteReal1() {
183 new LoessInterpolator().smooth(new double[] {1,2,Double.NaN}, new double[] {3,4,5});
184 }
185
186 @Test(expected=NotFiniteNumberException.class)
187 public void testNotAllFiniteReal2() {
188 new LoessInterpolator().smooth(new double[] {1,2,Double.POSITIVE_INFINITY}, new double[] {3,4,5});
189 }
190
191 @Test(expected=NotFiniteNumberException.class)
192 public void testNotAllFiniteReal3() {
193 new LoessInterpolator().smooth(new double[] {1,2,Double.NEGATIVE_INFINITY}, new double[] {3,4,5});
194 }
195
196 @Test(expected=NotFiniteNumberException.class)
197 public void testNotAllFiniteReal4() {
198 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NaN});
199 }
200
201 @Test(expected=NotFiniteNumberException.class)
202 public void testNotAllFiniteReal5() {
203 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.POSITIVE_INFINITY});
204 }
205
206 @Test(expected=NotFiniteNumberException.class)
207 public void testNotAllFiniteReal6() {
208 new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NEGATIVE_INFINITY});
209 }
210
211 @Test(expected=NumberIsTooSmallException.class)
212 public void testInsufficientBandwidth() {
213 LoessInterpolator li = new LoessInterpolator(0.1, 3, 1e-12);
214 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});
215 }
216
217 @Test(expected=OutOfRangeException.class)
218 public void testCompletelyIncorrectBandwidth1() {
219 new LoessInterpolator(-0.2, 3, 1e-12);
220 }
221
222 @Test(expected=OutOfRangeException.class)
223 public void testCompletelyIncorrectBandwidth2() {
224 new LoessInterpolator(1.1, 3, 1e-12);
225 }
226
227 @Test
228 public void testMath296withoutWeights() {
229 double[] xval = {
230 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
231 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0};
232 double[] yval = {
233 0.47, 0.48, 0.55, 0.56, -0.08, -0.04, -0.07, -0.07,
234 -0.56, -0.46, -0.56, -0.52, -3.03, -3.08, -3.09,
235 -3.04, 3.54, 3.46, 3.36, 3.35};
236
237 double[] yref = {
238 0.461, 0.499, 0.541, 0.308, 0.175, -0.042, -0.072,
239 -0.196, -0.311, -0.446, -0.557, -1.497, -2.133,
240 -3.08, -3.09, -0.621, 0.982, 3.449, 3.389, 3.336
241 };
242 LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
243 double[] res = li.smooth(xval, yval);
244 Assert.assertEquals(xval.length, res.length);
245 for(int i = 0; i < res.length; ++i) {
246 Assert.assertEquals(yref[i], res[i], 0.02);
247 }
248 }
249
250
251 @Test
252 public void testFitWithUnevenXSpacing() {
253 final double[] xval = {
254 0.1, 0.12, 0.23, 0.4, 0.57,
255 0.7, 0.87, 1.3, 1.9, 2.2,
256 2.3, 2.65, 3.0, 3.1, 3.5,
257 4.6, 4.7, 5.8, 5.95, 6.1
258 };
259 final double[] yval = {
260 0.47, 0.48, 0.55, 0.56, -0.08,
261 -0.04, -0.07, -0.07, -0.56, -0.46,
262 -0.56, -0.52, -3.03, -3.08, -3.09,
263 -3.04, 3.54, 3.46, 3.36, 3.35
264 };
265
266
267
268 final double[] yref = {
269 0.556184894, 0.541907126, 0.455059334, 0.303681477, 0.142126445,
270 0.002615653, -0.031178445, -0.187124310, -0.405235207, -0.535023851,
271 -0.706801740, -1.466740294, -2.349248503, -2.596576469, -3.354222419,
272 0.086206868, 0.320251370, 3.064778450, 3.426179479, 3.783500164
273 };
274
275 final double delta = 1e-8;
276
277
278 final LoessInterpolator li = new LoessInterpolator(0.35, 0, 1e-12);
279 final double[] res = li.smooth(xval, yval);
280 Assert.assertEquals(xval.length, res.length);
281 for (int i = 0; i < res.length; ++i) {
282 Assert.assertEquals(yref[i], res[i], delta);
283 }
284 }
285
286
287 @Test
288 public void testFitWithVaryingWeightsAndUnevenXSpacing() {
289 final double[] xval = {
290 0.1, 0.12, 0.23, 0.4, 0.57,
291 0.7, 0.87, 1.3, 1.9, 2.2,
292 2.3, 2.65, 3.0, 3.1, 3.5,
293 4.6, 4.7, 5.8, 5.95, 6.1
294 };
295 final double[] yval = {
296 0.47, 0.48, 0.55, 0.56, -0.08,
297 -0.04, -0.07, -0.07, -0.56, -0.46,
298 -0.56, -0.52, -3.03, -3.08, -3.09,
299 -3.04, 3.54, 3.46, 3.36, 3.35};
300 final double[] weights = {
301 1, 1, 1, 1, 1,
302 1, 1, 1, 1, 1,
303 1, 0.8, 0.5, 0.5, 0.8,
304 0.8, 0.5, 0.5, 0.9, 1
305 };
306
307
308
309 final double[] yref = {
310 0.556184894, 0.541907126, 0.455059334, 0.303681477, 0.142126445,
311 0.002615653, -0.031178445, -0.187124310, -0.406403569, -0.531957113,
312 -0.669978426, -1.411039850, -2.225022609, -2.463874517, -3.298767758,
313 -0.506116921, -0.231242991, 2.873755984, 3.295897106, 3.715495321};
314
315 final double delta = 1e-8;
316
317
318 final LoessInterpolator li = new LoessInterpolator(0.35, 0, 1e-12);
319 final double[] res = li.smooth(xval, yval, weights);
320 Assert.assertEquals(xval.length, res.length);
321 for (int i = 0; i < res.length; ++i) {
322 Assert.assertEquals(yref[i], res[i], delta);
323 }
324 }
325
326 private void generateSineData(double[] xval, double[] yval, double xnoise, double ynoise) {
327 double dx = 2 * JdkMath.PI / xval.length;
328 double x = 0;
329 for(int i = 0; i < xval.length; ++i) {
330 xval[i] = x;
331 yval[i] = JdkMath.sin(x) + (2 * JdkMath.random() - 1) * ynoise;
332 x += dx * (1 + (2 * JdkMath.random() - 1) * xnoise);
333 }
334 }
335 }