1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math.analysis.interpolation;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22
23 import org.apache.commons.math.MathException;
24 import org.junit.Test;
25
26
27
28
29 public class LoessInterpolatorTest {
30
31 @Test
32 public void testOnOnePoint() throws MathException {
33 double[] xval = {0.5};
34 double[] yval = {0.7};
35 double[] res = new LoessInterpolator().smooth(xval, yval);
36 assertEquals(1, res.length);
37 assertEquals(0.7, res[0], 0.0);
38 }
39
40 @Test
41 public void testOnTwoPoints() throws MathException {
42 double[] xval = {0.5, 0.6};
43 double[] yval = {0.7, 0.8};
44 double[] res = new LoessInterpolator().smooth(xval, yval);
45 assertEquals(2, res.length);
46 assertEquals(0.7, res[0], 0.0);
47 assertEquals(0.8, res[1], 0.0);
48 }
49
50 @Test
51 public void testOnStraightLine() throws MathException {
52 double[] xval = {1,2,3,4,5};
53 double[] yval = {2,4,6,8,10};
54 LoessInterpolator li = new LoessInterpolator(0.6, 2);
55 double[] res = li.smooth(xval, yval);
56 assertEquals(5, res.length);
57 for(int i = 0; i < 5; ++i) {
58 assertEquals(yval[i], res[i], 1e-8);
59 }
60 }
61
62 @Test
63 public void testOnDistortedSine() throws MathException {
64 int numPoints = 100;
65 double[] xval = new double[numPoints];
66 double[] yval = new double[numPoints];
67 double xnoise = 0.1;
68 double ynoise = 0.2;
69
70 generateSineData(xval, yval, xnoise, ynoise);
71
72 LoessInterpolator li = new LoessInterpolator(0.3, 4);
73
74 double[] res = li.smooth(xval, yval);
75
76
77
78
79 double noisyResidualSum = 0;
80 double fitResidualSum = 0;
81
82 for(int i = 0; i < numPoints; ++i) {
83 double expected = Math.sin(xval[i]);
84 double noisy = yval[i];
85 double fit = res[i];
86
87 noisyResidualSum += Math.pow(noisy - expected, 2);
88 fitResidualSum += Math.pow(fit - expected, 2);
89 }
90
91 assertTrue(fitResidualSum < noisyResidualSum);
92 }
93
94 @Test
95 public void testIncreasingBandwidthIncreasesSmoothness() throws MathException {
96 int numPoints = 100;
97 double[] xval = new double[numPoints];
98 double[] yval = new double[numPoints];
99 double xnoise = 0.1;
100 double ynoise = 0.1;
101
102 generateSineData(xval, yval, xnoise, ynoise);
103
104
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
136 yval[numPoints/3] *= 100;
137 yval[2 * numPoints/3] *= -100;
138
139
140
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
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
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
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
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
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
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
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
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
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
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
242 }
243 }
244
245 @Test
246 public void testCompletelyIncorrectBandwidth() {
247 try {
248 new LoessInterpolator(-0.2, 3);
249 fail();
250 } catch(MathException e) {
251
252 }
253 try {
254 new LoessInterpolator(1.1, 3);
255 fail();
256 } catch(MathException e) {
257
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 }