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 }