comparison scripts/general/interp1.m @ 9754:4219e5cf773d

improve interp1 and pchip
author Jaroslav Hajek <highegg@gmail.com>
date Thu, 22 Oct 2009 10:12:54 +0200
parents e9dc2ed2ec0f
children 9a1c4fe44af8
comparison
equal deleted inserted replaced
9753:892e2aa7bc75 9754:4219e5cf773d
124 endfor 124 endfor
125 endif 125 endif
126 126
127 ## reshape matrices for convenience 127 ## reshape matrices for convenience
128 x = x(:); 128 x = x(:);
129 nx = size (x, 1); 129 nx = rows (x);
130 if (isvector(y) && size (y, 1) == 1) 130 if (isvector (y))
131 y = y(:); 131 y = y(:);
132 endif 132 endif
133 ndy = ndims (y);
134 szy = size (y); 133 szy = size (y);
135 ny = szy(1); 134 y = y(:,:);
136 nc = prod (szy(2:end)); 135 [ny, nc] = size (y);
137 y = reshape (y, ny, nc);
138 szx = size (xi); 136 szx = size (xi);
139 xi = xi(:); 137 xi = xi(:);
140 138
141 ## determine sizes 139 ## determine sizes
142 if (nx < 2 || ny < 2) 140 if (nx < 2 || ny < 2)
143 error ("interp1: table too short"); 141 error ("interp1: table too short");
144 endif 142 endif
145 143
146 ## determine which values are out of range and set them to extrap, 144 ## check whether x is sorted; sort if not.
147 ## unless extrap == "extrap" in which case, extrapolate them like we 145 if (! issorted (x))
148 ## should be doing in the first place. 146 [x, p] = sort (x);
149 minx = x(1); 147 y = y(p,:);
150 maxx = x(nx); 148 endif
151 if (minx > maxx) 149
152 tmp = minx; 150 starmethod = method(1) == "*";
153 minx = maxx; 151
154 maxx = tmp; 152 if (starmethod)
155 endif
156 if (method(1) == "*")
157 dx = x(2) - x(1); 153 dx = x(2) - x(1);
158 endif 154 else
159 155 if (any (x(1:nx-1) == x(2:nx)))
160 if (! pp) 156 error ("interp1: table must be strictly monotonic");
161 if (ischar (extrap) && strcmp (extrap, "extrap")) 157 endif
162 range = 1:size (xi, 1); 158 endif
163 yi = zeros (size (xi, 1), size (y, 2)); 159
164 else 160 ## Proceed with interpolating by all methods.
165 range = find (xi >= minx & xi <= maxx); 161
166 yi = extrap * ones (size (xi, 1), size (y, 2)); 162 switch (method)
167 if (isempty (range)) 163 case "nearest"
168 if (! isvector (y) && length (szx) == 2
169 && (szx(1) == 1 || szx(2) == 1))
170 if (szx(1) == 1)
171 yi = reshape (yi, [szx(2), szy(2:end)]);
172 else
173 yi = reshape (yi, [szx(1), szy(2:end)]);
174 endif
175 else
176 yi = reshape (yi, [szx, szy(2:end)]);
177 endif
178 return;
179 endif
180 xi = xi(range);
181 endif
182 endif
183
184 if (strcmp (method, "nearest"))
185 if (pp) 164 if (pp)
186 yi = mkpp ([x(1); (x(1:end-1)+x(2:end))/2; x(end)], y, szy(2:end)); 165 yi = mkpp ([x(1); (x(1:end-1)+x(2:end))/2; x(end)], y, szy(2:end));
187 else 166 else
188 idx = lookup (0.5*(x(1:nx-1)+x(2:nx)), xi) + 1; 167 idx = lookup (0.5*(x(1:nx-1)+x(2:nx)), xi) + 1;
189 yi(range,:) = y(idx,:); 168 yi = y(idx,:);
190 endif 169 endif
191 elseif (strcmp (method, "*nearest")) 170 case "*nearest"
192 if (pp) 171 if (pp)
193 yi = mkpp ([x(1); x(1)+[0.5:(ny-1)]'*dx; x(nx)], y, szy(2:end)); 172 yi = mkpp ([x(1); x(1)+[0.5:(ny-1)]'*dx; x(nx)], y, szy(2:end));
194 else 173 else
195 idx = max (1, min (ny, floor((xi-x(1))/dx+1.5))); 174 idx = max (1, min (ny, floor((xi-x(1))/dx+1.5)));
196 yi(range,:) = y(idx,:); 175 yi = y(idx,:);
197 endif 176 endif
198 elseif (strcmp (method, "linear")) 177 case "linear"
199 dy = y(2:ny,:) - y(1:ny-1,:); 178 dy = diff (y);
200 dx = x(2:nx) - x(1:nx-1); 179 dx = diff (x);
201 if (pp) 180 if (pp)
202 yi = mkpp (x, [dy./dx, y(1:end-1)], szy(2:end)); 181 yi = mkpp (x, [dy./dx, y(1:end-1)], szy(2:end));
203 else 182 else
204 ## find the interval containing the test point 183 ## find the interval containing the test point
205 idx = lookup (x, xi, "lr"); 184 idx = lookup (x, xi, "lr");
206 ## use the endpoints of the interval to define a line 185 ## use the endpoints of the interval to define a line
207 s = (xi - x(idx))./dx(idx); 186 s = (xi - x(idx))./dx(idx);
208 yi(range,:) = s(:,ones(1,nc)).*dy(idx,:) + y(idx,:); 187 yi = bsxfun (@times, s, dy(idx,:)) + y(idx,:);
209 endif 188 endif
210 elseif (strcmp (method, "*linear")) 189 case "*linear"
190 dy = diff (y);
211 if (pp) 191 if (pp)
212 dy = [y(2:ny,:) - y(1:ny-1,:)];
213 yi = mkpp (x(1) + [0:ny-1]*dx, [dy./dx, y(1:end-1)], szy(2:end)); 192 yi = mkpp (x(1) + [0:ny-1]*dx, [dy./dx, y(1:end-1)], szy(2:end));
214 else 193 else
215 ## find the interval containing the test point 194 ## find the interval containing the test point
216 t = (xi - x(1))/dx + 1; 195 t = (xi - x(1))/dx + 1;
217 idx = max(1,min(ny,floor(t))); 196 idx = max (1, min (ny - 1, floor (t)));
218 197
219 ## use the endpoints of the interval to define a line 198 ## use the endpoints of the interval to define a line
220 dy = [y(2:ny,:) - y(1:ny-1,:); y(ny,:) - y(ny-1,:)];
221 s = t - idx; 199 s = t - idx;
222 yi(range,:) = s(:,ones(1,nc)).*dy(idx,:) + y(idx,:); 200 yi = bsxfun (@times, s, dy(idx,:)) + y(idx,:);
223 endif 201 endif
224 elseif (strcmp (method, "pchip") || strcmp (method, "*pchip")) 202 case {"pchip", "*pchip"}
225 if (nx == 2 || method(1) == "*") 203 if (nx == 2 || starmethod)
226 x = linspace (x(1), x(nx), ny); 204 x = linspace (x(1), x(nx), ny);
227 endif 205 endif
228 ## Note that pchip's arguments are transposed relative to interp1 206 ## Note that pchip's arguments are transposed relative to interp1
229 if (pp) 207 if (pp)
230 yi = pchip (x.', y.'); 208 yi = pchip (x.', y.');
231 yi.d = szy(2:end); 209 yi.d = szy(2:end);
232 else 210 else
233 yi(range,:) = pchip (x.', y.', xi.').'; 211 yi = pchip (x.', y.', xi.').';
234 endif 212 endif
235 213
236 elseif (strcmp (method, "cubic") || (strcmp (method, "*cubic") && pp)) 214 case {"cubic", "*cubic"}
237 ## FIXME Is there a better way to treat pp return return and *cubic
238 if (method(1) == "*")
239 x = linspace (x(1), x(nx), ny).';
240 nx = ny;
241 endif
242
243 if (nx < 4 || ny < 4) 215 if (nx < 4 || ny < 4)
244 error ("interp1: table too short"); 216 error ("interp1: table too short");
245 endif 217 endif
246 idx = lookup (x(2:nx-1), xi, "lr"); 218
247 219 ## FIXME Is there a better way to treat pp return and *cubic
248 ## Construct cubic equations for each interval using divided 220 if (starmethod && ! pp)
249 ## differences (computation of c and d don't use divided differences 221 ## From: Miloje Makivic
250 ## but instead solve 2 equations for 2 unknowns). Perhaps 222 ## http://www.npac.syr.edu/projects/nasa/MILOJE/final/node36.html
251 ## reformulating this as a lagrange polynomial would be more efficient. 223 t = (xi - x(1))/dx + 1;
252 i = 1:nx-3; 224 idx = max (min (floor (t), ny-2), 2);
253 J = ones (1, nc); 225 t = t - idx;
254 dx = diff (x); 226 t2 = t.*t;
255 dx2 = x(i+1).^2 - x(i).^2; 227 tp = 1 - 0.5*t;
256 dx3 = x(i+1).^3 - x(i).^3; 228 a = (1 - t2).*tp;
257 a = diff (y, 3)./dx(i,J).^3/6; 229 b = (t2 + t).*tp;
258 b = (diff (y(1:nx-1,:), 2)./dx(i,J).^2 - 6*a.*x(i+1,J))/2; 230 c = (t2 - t).*tp/3;
259 c = (diff (y(1:nx-2,:), 1) - a.*dx3(:,J) - b.*dx2(:,J))./dx(i,J); 231 d = (t2 - 1).*t/6;
260 d = y(i,:) - ((a.*x(i,J) + b).*x(i,J) + c).*x(i,J); 232 J = ones (1, nc);
261 233
262 if (pp) 234 yi = a(:,J) .* y(idx,:) + b(:,J) .* y(idx+1,:) ...
263 xs = [x(1);x(3:nx-2)]; 235 + c(:,J) .* y(idx-1,:) + d(:,J) .* y(idx+2,:);
264 yi = mkpp ([x(1);x(3:nx-2);x(nx)], 236 else
265 [a(:), (b(:) + 3.*xs(:,J).*a(:)), ... 237 if (starmethod)
266 (c(:) + 2.*xs(:,J).*b(:) + 3.*xs(:,J)(:).^2.*a(:)), ... 238 x = linspace (x(1), x(nx), ny).';
267 (d(:) + xs(:,J).*c(:) + xs(:,J).^2.*b(:) + ... 239 nx = ny;
268 xs(:,J).^3.*a(:))], szy(2:end)); 240 endif
269 else 241
270 yi(range,:) = ((a(idx,:).*xi(:,J) + b(idx,:)).*xi(:,J) ... 242 idx = lookup (x(2:nx-1), xi, "lr");
271 + c(idx,:)).*xi(:,J) + d(idx,:); 243
272 endif 244 ## Construct cubic equations for each interval using divided
273 elseif (strcmp (method, "*cubic")) 245 ## differences (computation of c and d don't use divided differences
274 if (nx < 4 || ny < 4) 246 ## but instead solve 2 equations for 2 unknowns). Perhaps
275 error ("interp1: table too short"); 247 ## reformulating this as a lagrange polynomial would be more efficient.
276 endif 248 i = 1:nx-3;
277 249 J = ones (1, nc);
278 ## From: Miloje Makivic 250 dx = diff (x);
279 ## http://www.npac.syr.edu/projects/nasa/MILOJE/final/node36.html 251 dx2 = x(i+1).^2 - x(i).^2;
280 t = (xi - x(1))/dx + 1; 252 dx3 = x(i+1).^3 - x(i).^3;
281 idx = max (min (floor (t), ny-2), 2); 253 a = diff (y, 3)./dx(i,J).^3/6;
282 t = t - idx; 254 b = (diff (y(1:nx-1,:), 2)./dx(i,J).^2 - 6*a.*x(i+1,J))/2;
283 t2 = t.*t; 255 c = (diff (y(1:nx-2,:), 1) - a.*dx3(:,J) - b.*dx2(:,J))./dx(i,J);
284 tp = 1 - 0.5*t; 256 d = y(i,:) - ((a.*x(i,J) + b).*x(i,J) + c).*x(i,J);
285 a = (1 - t2).*tp; 257
286 b = (t2 + t).*tp; 258 if (pp)
287 c = (t2 - t).*tp/3; 259 xs = [x(1);x(3:nx-2)];
288 d = (t2 - 1).*t/6; 260 yi = mkpp ([x(1);x(3:nx-2);x(nx)],
289 J = ones (1, nc); 261 [a(:), (b(:) + 3.*xs(:,J).*a(:)), ...
290 262 (c(:) + 2.*xs(:,J).*b(:) + 3.*xs(:,J)(:).^2.*a(:)), ...
291 yi(range,:) = a(:,J) .* y(idx,:) + b(:,J) .* y(idx+1,:) ... 263 (d(:) + xs(:,J).*c(:) + xs(:,J).^2.*b(:) + ...
292 + c(:,J) .* y(idx-1,:) + d(:,J) .* y(idx+2,:); 264 xs(:,J).^3.*a(:))], szy(2:end));
293 265 else
294 elseif (strcmp (method, "spline") || strcmp (method, "*spline")) 266 yi = ((a(idx,:).*xi(:,J) + b(idx,:)).*xi(:,J) ...
295 if (nx == 2 || method(1) == "*") 267 + c(idx,:)).*xi(:,J) + d(idx,:);
268 endif
269 endif
270 case {"spline", "*spline"}
271 if (nx == 2 || starmethod)
296 x = linspace(x(1), x(nx), ny); 272 x = linspace(x(1), x(nx), ny);
297 endif 273 endif
298 ## Note that spline's arguments are transposed relative to interp1 274 ## Note that spline's arguments are transposed relative to interp1
299 if (pp) 275 if (pp)
300 yi = spline (x.', y.'); 276 yi = spline (x.', y.');
301 yi.d = szy(2:end); 277 yi.d = szy(2:end);
302 else 278 else
303 yi(range,:) = spline (x.', y.', xi.').'; 279 yi = spline (x.', y.', xi.').';
304 endif 280 endif
305 else 281 otherwise
306 error ("interp1: invalid method '%s'", method); 282 error ("interp1: invalid method '%s'", method);
307 endif 283 endswitch
308 284
309 if (! pp) 285 if (! pp)
286 if (! ischar (extrap))
287 ## determine which values are out of range and set them to extrap,
288 ## unless extrap == "extrap".
289 minx = min (x(1), x(nx));
290 maxx = max (x(1), x(nx));
291
292 outliers = xi < minx | ! (xi <= maxx); # this catches even NaNs
293 yi(outliers, :) = extrap;
294 endif
295
310 if (! isvector (y) && length (szx) == 2 && (szx(1) == 1 || szx(2) == 1)) 296 if (! isvector (y) && length (szx) == 2 && (szx(1) == 1 || szx(2) == 1))
311 if (szx(1) == 1) 297 if (szx(1) == 1)
312 yi = reshape (yi, [szx(2), szy(2:end)]); 298 yi = reshape (yi, [szx(2), szy(2:end)]);
313 else 299 else
314 yi = reshape (yi, [szx(1), szy(2:end)]); 300 yi = reshape (yi, [szx(1), szy(2:end)]);