Chris@87
|
1 """
|
Chris@87
|
2 The arraypad module contains a group of functions to pad values onto the edges
|
Chris@87
|
3 of an n-dimensional array.
|
Chris@87
|
4
|
Chris@87
|
5 """
|
Chris@87
|
6 from __future__ import division, absolute_import, print_function
|
Chris@87
|
7
|
Chris@87
|
8 import numpy as np
|
Chris@87
|
9 from numpy.compat import long
|
Chris@87
|
10
|
Chris@87
|
11
|
Chris@87
|
12 __all__ = ['pad']
|
Chris@87
|
13
|
Chris@87
|
14
|
Chris@87
|
15 ###############################################################################
|
Chris@87
|
16 # Private utility functions.
|
Chris@87
|
17
|
Chris@87
|
18
|
Chris@87
|
19 def _arange_ndarray(arr, shape, axis, reverse=False):
|
Chris@87
|
20 """
|
Chris@87
|
21 Create an ndarray of `shape` with increments along specified `axis`
|
Chris@87
|
22
|
Chris@87
|
23 Parameters
|
Chris@87
|
24 ----------
|
Chris@87
|
25 arr : ndarray
|
Chris@87
|
26 Input array of arbitrary shape.
|
Chris@87
|
27 shape : tuple of ints
|
Chris@87
|
28 Shape of desired array. Should be equivalent to `arr.shape` except
|
Chris@87
|
29 `shape[axis]` which may have any positive value.
|
Chris@87
|
30 axis : int
|
Chris@87
|
31 Axis to increment along.
|
Chris@87
|
32 reverse : bool
|
Chris@87
|
33 If False, increment in a positive fashion from 1 to `shape[axis]`,
|
Chris@87
|
34 inclusive. If True, the bounds are the same but the order reversed.
|
Chris@87
|
35
|
Chris@87
|
36 Returns
|
Chris@87
|
37 -------
|
Chris@87
|
38 padarr : ndarray
|
Chris@87
|
39 Output array sized to pad `arr` along `axis`, with linear range from
|
Chris@87
|
40 1 to `shape[axis]` along specified `axis`.
|
Chris@87
|
41
|
Chris@87
|
42 Notes
|
Chris@87
|
43 -----
|
Chris@87
|
44 The range is deliberately 1-indexed for this specific use case. Think of
|
Chris@87
|
45 this algorithm as broadcasting `np.arange` to a single `axis` of an
|
Chris@87
|
46 arbitrarily shaped ndarray.
|
Chris@87
|
47
|
Chris@87
|
48 """
|
Chris@87
|
49 initshape = tuple(1 if i != axis else shape[axis]
|
Chris@87
|
50 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
51 if not reverse:
|
Chris@87
|
52 padarr = np.arange(1, shape[axis] + 1)
|
Chris@87
|
53 else:
|
Chris@87
|
54 padarr = np.arange(shape[axis], 0, -1)
|
Chris@87
|
55 padarr = padarr.reshape(initshape)
|
Chris@87
|
56 for i, dim in enumerate(shape):
|
Chris@87
|
57 if padarr.shape[i] != dim:
|
Chris@87
|
58 padarr = padarr.repeat(dim, axis=i)
|
Chris@87
|
59 return padarr
|
Chris@87
|
60
|
Chris@87
|
61
|
Chris@87
|
62 def _round_ifneeded(arr, dtype):
|
Chris@87
|
63 """
|
Chris@87
|
64 Rounds arr inplace if destination dtype is integer.
|
Chris@87
|
65
|
Chris@87
|
66 Parameters
|
Chris@87
|
67 ----------
|
Chris@87
|
68 arr : ndarray
|
Chris@87
|
69 Input array.
|
Chris@87
|
70 dtype : dtype
|
Chris@87
|
71 The dtype of the destination array.
|
Chris@87
|
72
|
Chris@87
|
73 """
|
Chris@87
|
74 if np.issubdtype(dtype, np.integer):
|
Chris@87
|
75 arr.round(out=arr)
|
Chris@87
|
76
|
Chris@87
|
77
|
Chris@87
|
78 def _prepend_const(arr, pad_amt, val, axis=-1):
|
Chris@87
|
79 """
|
Chris@87
|
80 Prepend constant `val` along `axis` of `arr`.
|
Chris@87
|
81
|
Chris@87
|
82 Parameters
|
Chris@87
|
83 ----------
|
Chris@87
|
84 arr : ndarray
|
Chris@87
|
85 Input array of arbitrary shape.
|
Chris@87
|
86 pad_amt : int
|
Chris@87
|
87 Amount of padding to prepend.
|
Chris@87
|
88 val : scalar
|
Chris@87
|
89 Constant value to use. For best results should be of type `arr.dtype`;
|
Chris@87
|
90 if not `arr.dtype` will be cast to `arr.dtype`.
|
Chris@87
|
91 axis : int
|
Chris@87
|
92 Axis along which to pad `arr`.
|
Chris@87
|
93
|
Chris@87
|
94 Returns
|
Chris@87
|
95 -------
|
Chris@87
|
96 padarr : ndarray
|
Chris@87
|
97 Output array, with `pad_amt` constant `val` prepended along `axis`.
|
Chris@87
|
98
|
Chris@87
|
99 """
|
Chris@87
|
100 if pad_amt == 0:
|
Chris@87
|
101 return arr
|
Chris@87
|
102 padshape = tuple(x if i != axis else pad_amt
|
Chris@87
|
103 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
104 if val == 0:
|
Chris@87
|
105 return np.concatenate((np.zeros(padshape, dtype=arr.dtype), arr),
|
Chris@87
|
106 axis=axis)
|
Chris@87
|
107 else:
|
Chris@87
|
108 return np.concatenate(((np.zeros(padshape) + val).astype(arr.dtype),
|
Chris@87
|
109 arr), axis=axis)
|
Chris@87
|
110
|
Chris@87
|
111
|
Chris@87
|
112 def _append_const(arr, pad_amt, val, axis=-1):
|
Chris@87
|
113 """
|
Chris@87
|
114 Append constant `val` along `axis` of `arr`.
|
Chris@87
|
115
|
Chris@87
|
116 Parameters
|
Chris@87
|
117 ----------
|
Chris@87
|
118 arr : ndarray
|
Chris@87
|
119 Input array of arbitrary shape.
|
Chris@87
|
120 pad_amt : int
|
Chris@87
|
121 Amount of padding to append.
|
Chris@87
|
122 val : scalar
|
Chris@87
|
123 Constant value to use. For best results should be of type `arr.dtype`;
|
Chris@87
|
124 if not `arr.dtype` will be cast to `arr.dtype`.
|
Chris@87
|
125 axis : int
|
Chris@87
|
126 Axis along which to pad `arr`.
|
Chris@87
|
127
|
Chris@87
|
128 Returns
|
Chris@87
|
129 -------
|
Chris@87
|
130 padarr : ndarray
|
Chris@87
|
131 Output array, with `pad_amt` constant `val` appended along `axis`.
|
Chris@87
|
132
|
Chris@87
|
133 """
|
Chris@87
|
134 if pad_amt == 0:
|
Chris@87
|
135 return arr
|
Chris@87
|
136 padshape = tuple(x if i != axis else pad_amt
|
Chris@87
|
137 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
138 if val == 0:
|
Chris@87
|
139 return np.concatenate((arr, np.zeros(padshape, dtype=arr.dtype)),
|
Chris@87
|
140 axis=axis)
|
Chris@87
|
141 else:
|
Chris@87
|
142 return np.concatenate(
|
Chris@87
|
143 (arr, (np.zeros(padshape) + val).astype(arr.dtype)), axis=axis)
|
Chris@87
|
144
|
Chris@87
|
145
|
Chris@87
|
146 def _prepend_edge(arr, pad_amt, axis=-1):
|
Chris@87
|
147 """
|
Chris@87
|
148 Prepend `pad_amt` to `arr` along `axis` by extending edge values.
|
Chris@87
|
149
|
Chris@87
|
150 Parameters
|
Chris@87
|
151 ----------
|
Chris@87
|
152 arr : ndarray
|
Chris@87
|
153 Input array of arbitrary shape.
|
Chris@87
|
154 pad_amt : int
|
Chris@87
|
155 Amount of padding to prepend.
|
Chris@87
|
156 axis : int
|
Chris@87
|
157 Axis along which to pad `arr`.
|
Chris@87
|
158
|
Chris@87
|
159 Returns
|
Chris@87
|
160 -------
|
Chris@87
|
161 padarr : ndarray
|
Chris@87
|
162 Output array, extended by `pad_amt` edge values appended along `axis`.
|
Chris@87
|
163
|
Chris@87
|
164 """
|
Chris@87
|
165 if pad_amt == 0:
|
Chris@87
|
166 return arr
|
Chris@87
|
167
|
Chris@87
|
168 edge_slice = tuple(slice(None) if i != axis else 0
|
Chris@87
|
169 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
170
|
Chris@87
|
171 # Shape to restore singleton dimension after slicing
|
Chris@87
|
172 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
173 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
174 edge_arr = arr[edge_slice].reshape(pad_singleton)
|
Chris@87
|
175 return np.concatenate((edge_arr.repeat(pad_amt, axis=axis), arr),
|
Chris@87
|
176 axis=axis)
|
Chris@87
|
177
|
Chris@87
|
178
|
Chris@87
|
179 def _append_edge(arr, pad_amt, axis=-1):
|
Chris@87
|
180 """
|
Chris@87
|
181 Append `pad_amt` to `arr` along `axis` by extending edge values.
|
Chris@87
|
182
|
Chris@87
|
183 Parameters
|
Chris@87
|
184 ----------
|
Chris@87
|
185 arr : ndarray
|
Chris@87
|
186 Input array of arbitrary shape.
|
Chris@87
|
187 pad_amt : int
|
Chris@87
|
188 Amount of padding to append.
|
Chris@87
|
189 axis : int
|
Chris@87
|
190 Axis along which to pad `arr`.
|
Chris@87
|
191
|
Chris@87
|
192 Returns
|
Chris@87
|
193 -------
|
Chris@87
|
194 padarr : ndarray
|
Chris@87
|
195 Output array, extended by `pad_amt` edge values prepended along
|
Chris@87
|
196 `axis`.
|
Chris@87
|
197
|
Chris@87
|
198 """
|
Chris@87
|
199 if pad_amt == 0:
|
Chris@87
|
200 return arr
|
Chris@87
|
201
|
Chris@87
|
202 edge_slice = tuple(slice(None) if i != axis else arr.shape[axis] - 1
|
Chris@87
|
203 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
204
|
Chris@87
|
205 # Shape to restore singleton dimension after slicing
|
Chris@87
|
206 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
207 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
208 edge_arr = arr[edge_slice].reshape(pad_singleton)
|
Chris@87
|
209 return np.concatenate((arr, edge_arr.repeat(pad_amt, axis=axis)),
|
Chris@87
|
210 axis=axis)
|
Chris@87
|
211
|
Chris@87
|
212
|
Chris@87
|
213 def _prepend_ramp(arr, pad_amt, end, axis=-1):
|
Chris@87
|
214 """
|
Chris@87
|
215 Prepend linear ramp along `axis`.
|
Chris@87
|
216
|
Chris@87
|
217 Parameters
|
Chris@87
|
218 ----------
|
Chris@87
|
219 arr : ndarray
|
Chris@87
|
220 Input array of arbitrary shape.
|
Chris@87
|
221 pad_amt : int
|
Chris@87
|
222 Amount of padding to prepend.
|
Chris@87
|
223 end : scalar
|
Chris@87
|
224 Constal value to use. For best results should be of type `arr.dtype`;
|
Chris@87
|
225 if not `arr.dtype` will be cast to `arr.dtype`.
|
Chris@87
|
226 axis : int
|
Chris@87
|
227 Axis along which to pad `arr`.
|
Chris@87
|
228
|
Chris@87
|
229 Returns
|
Chris@87
|
230 -------
|
Chris@87
|
231 padarr : ndarray
|
Chris@87
|
232 Output array, with `pad_amt` values prepended along `axis`. The
|
Chris@87
|
233 prepended region ramps linearly from the edge value to `end`.
|
Chris@87
|
234
|
Chris@87
|
235 """
|
Chris@87
|
236 if pad_amt == 0:
|
Chris@87
|
237 return arr
|
Chris@87
|
238
|
Chris@87
|
239 # Generate shape for final concatenated array
|
Chris@87
|
240 padshape = tuple(x if i != axis else pad_amt
|
Chris@87
|
241 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
242
|
Chris@87
|
243 # Generate an n-dimensional array incrementing along `axis`
|
Chris@87
|
244 ramp_arr = _arange_ndarray(arr, padshape, axis,
|
Chris@87
|
245 reverse=True).astype(np.float64)
|
Chris@87
|
246
|
Chris@87
|
247 # Appropriate slicing to extract n-dimensional edge along `axis`
|
Chris@87
|
248 edge_slice = tuple(slice(None) if i != axis else 0
|
Chris@87
|
249 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
250
|
Chris@87
|
251 # Shape to restore singleton dimension after slicing
|
Chris@87
|
252 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
253 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
254
|
Chris@87
|
255 # Extract edge, reshape to original rank, and extend along `axis`
|
Chris@87
|
256 edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis)
|
Chris@87
|
257
|
Chris@87
|
258 # Linear ramp
|
Chris@87
|
259 slope = (end - edge_pad) / float(pad_amt)
|
Chris@87
|
260 ramp_arr = ramp_arr * slope
|
Chris@87
|
261 ramp_arr += edge_pad
|
Chris@87
|
262 _round_ifneeded(ramp_arr, arr.dtype)
|
Chris@87
|
263
|
Chris@87
|
264 # Ramp values will most likely be float, cast them to the same type as arr
|
Chris@87
|
265 return np.concatenate((ramp_arr.astype(arr.dtype), arr), axis=axis)
|
Chris@87
|
266
|
Chris@87
|
267
|
Chris@87
|
268 def _append_ramp(arr, pad_amt, end, axis=-1):
|
Chris@87
|
269 """
|
Chris@87
|
270 Append linear ramp along `axis`.
|
Chris@87
|
271
|
Chris@87
|
272 Parameters
|
Chris@87
|
273 ----------
|
Chris@87
|
274 arr : ndarray
|
Chris@87
|
275 Input array of arbitrary shape.
|
Chris@87
|
276 pad_amt : int
|
Chris@87
|
277 Amount of padding to append.
|
Chris@87
|
278 end : scalar
|
Chris@87
|
279 Constal value to use. For best results should be of type `arr.dtype`;
|
Chris@87
|
280 if not `arr.dtype` will be cast to `arr.dtype`.
|
Chris@87
|
281 axis : int
|
Chris@87
|
282 Axis along which to pad `arr`.
|
Chris@87
|
283
|
Chris@87
|
284 Returns
|
Chris@87
|
285 -------
|
Chris@87
|
286 padarr : ndarray
|
Chris@87
|
287 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
288 appended region ramps linearly from the edge value to `end`.
|
Chris@87
|
289
|
Chris@87
|
290 """
|
Chris@87
|
291 if pad_amt == 0:
|
Chris@87
|
292 return arr
|
Chris@87
|
293
|
Chris@87
|
294 # Generate shape for final concatenated array
|
Chris@87
|
295 padshape = tuple(x if i != axis else pad_amt
|
Chris@87
|
296 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
297
|
Chris@87
|
298 # Generate an n-dimensional array incrementing along `axis`
|
Chris@87
|
299 ramp_arr = _arange_ndarray(arr, padshape, axis,
|
Chris@87
|
300 reverse=False).astype(np.float64)
|
Chris@87
|
301
|
Chris@87
|
302 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
303 edge_slice = tuple(slice(None) if i != axis else -1
|
Chris@87
|
304 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
305
|
Chris@87
|
306 # Shape to restore singleton dimension after slicing
|
Chris@87
|
307 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
308 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
309
|
Chris@87
|
310 # Extract edge, reshape to original rank, and extend along `axis`
|
Chris@87
|
311 edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis)
|
Chris@87
|
312
|
Chris@87
|
313 # Linear ramp
|
Chris@87
|
314 slope = (end - edge_pad) / float(pad_amt)
|
Chris@87
|
315 ramp_arr = ramp_arr * slope
|
Chris@87
|
316 ramp_arr += edge_pad
|
Chris@87
|
317 _round_ifneeded(ramp_arr, arr.dtype)
|
Chris@87
|
318
|
Chris@87
|
319 # Ramp values will most likely be float, cast them to the same type as arr
|
Chris@87
|
320 return np.concatenate((arr, ramp_arr.astype(arr.dtype)), axis=axis)
|
Chris@87
|
321
|
Chris@87
|
322
|
Chris@87
|
323 def _prepend_max(arr, pad_amt, num, axis=-1):
|
Chris@87
|
324 """
|
Chris@87
|
325 Prepend `pad_amt` maximum values along `axis`.
|
Chris@87
|
326
|
Chris@87
|
327 Parameters
|
Chris@87
|
328 ----------
|
Chris@87
|
329 arr : ndarray
|
Chris@87
|
330 Input array of arbitrary shape.
|
Chris@87
|
331 pad_amt : int
|
Chris@87
|
332 Amount of padding to prepend.
|
Chris@87
|
333 num : int
|
Chris@87
|
334 Depth into `arr` along `axis` to calculate maximum.
|
Chris@87
|
335 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
336 axis : int
|
Chris@87
|
337 Axis along which to pad `arr`.
|
Chris@87
|
338
|
Chris@87
|
339 Returns
|
Chris@87
|
340 -------
|
Chris@87
|
341 padarr : ndarray
|
Chris@87
|
342 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
343 prepended region is the maximum of the first `num` values along
|
Chris@87
|
344 `axis`.
|
Chris@87
|
345
|
Chris@87
|
346 """
|
Chris@87
|
347 if pad_amt == 0:
|
Chris@87
|
348 return arr
|
Chris@87
|
349
|
Chris@87
|
350 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
351 if num == 1:
|
Chris@87
|
352 return _prepend_edge(arr, pad_amt, axis)
|
Chris@87
|
353
|
Chris@87
|
354 # Use entire array if `num` is too large
|
Chris@87
|
355 if num is not None:
|
Chris@87
|
356 if num >= arr.shape[axis]:
|
Chris@87
|
357 num = None
|
Chris@87
|
358
|
Chris@87
|
359 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
360 max_slice = tuple(slice(None) if i != axis else slice(num)
|
Chris@87
|
361 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
362
|
Chris@87
|
363 # Shape to restore singleton dimension after slicing
|
Chris@87
|
364 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
365 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
366
|
Chris@87
|
367 # Extract slice, calculate max, reshape to add singleton dimension back
|
Chris@87
|
368 max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton)
|
Chris@87
|
369
|
Chris@87
|
370 # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
371 return np.concatenate((max_chunk.repeat(pad_amt, axis=axis), arr),
|
Chris@87
|
372 axis=axis)
|
Chris@87
|
373
|
Chris@87
|
374
|
Chris@87
|
375 def _append_max(arr, pad_amt, num, axis=-1):
|
Chris@87
|
376 """
|
Chris@87
|
377 Pad one `axis` of `arr` with the maximum of the last `num` elements.
|
Chris@87
|
378
|
Chris@87
|
379 Parameters
|
Chris@87
|
380 ----------
|
Chris@87
|
381 arr : ndarray
|
Chris@87
|
382 Input array of arbitrary shape.
|
Chris@87
|
383 pad_amt : int
|
Chris@87
|
384 Amount of padding to append.
|
Chris@87
|
385 num : int
|
Chris@87
|
386 Depth into `arr` along `axis` to calculate maximum.
|
Chris@87
|
387 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
388 axis : int
|
Chris@87
|
389 Axis along which to pad `arr`.
|
Chris@87
|
390
|
Chris@87
|
391 Returns
|
Chris@87
|
392 -------
|
Chris@87
|
393 padarr : ndarray
|
Chris@87
|
394 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
395 appended region is the maximum of the final `num` values along `axis`.
|
Chris@87
|
396
|
Chris@87
|
397 """
|
Chris@87
|
398 if pad_amt == 0:
|
Chris@87
|
399 return arr
|
Chris@87
|
400
|
Chris@87
|
401 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
402 if num == 1:
|
Chris@87
|
403 return _append_edge(arr, pad_amt, axis)
|
Chris@87
|
404
|
Chris@87
|
405 # Use entire array if `num` is too large
|
Chris@87
|
406 if num is not None:
|
Chris@87
|
407 if num >= arr.shape[axis]:
|
Chris@87
|
408 num = None
|
Chris@87
|
409
|
Chris@87
|
410 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
411 end = arr.shape[axis] - 1
|
Chris@87
|
412 if num is not None:
|
Chris@87
|
413 max_slice = tuple(
|
Chris@87
|
414 slice(None) if i != axis else slice(end, end - num, -1)
|
Chris@87
|
415 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
416 else:
|
Chris@87
|
417 max_slice = tuple(slice(None) for x in arr.shape)
|
Chris@87
|
418
|
Chris@87
|
419 # Shape to restore singleton dimension after slicing
|
Chris@87
|
420 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
421 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
422
|
Chris@87
|
423 # Extract slice, calculate max, reshape to add singleton dimension back
|
Chris@87
|
424 max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton)
|
Chris@87
|
425
|
Chris@87
|
426 # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
427 return np.concatenate((arr, max_chunk.repeat(pad_amt, axis=axis)),
|
Chris@87
|
428 axis=axis)
|
Chris@87
|
429
|
Chris@87
|
430
|
Chris@87
|
431 def _prepend_mean(arr, pad_amt, num, axis=-1):
|
Chris@87
|
432 """
|
Chris@87
|
433 Prepend `pad_amt` mean values along `axis`.
|
Chris@87
|
434
|
Chris@87
|
435 Parameters
|
Chris@87
|
436 ----------
|
Chris@87
|
437 arr : ndarray
|
Chris@87
|
438 Input array of arbitrary shape.
|
Chris@87
|
439 pad_amt : int
|
Chris@87
|
440 Amount of padding to prepend.
|
Chris@87
|
441 num : int
|
Chris@87
|
442 Depth into `arr` along `axis` to calculate mean.
|
Chris@87
|
443 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
444 axis : int
|
Chris@87
|
445 Axis along which to pad `arr`.
|
Chris@87
|
446
|
Chris@87
|
447 Returns
|
Chris@87
|
448 -------
|
Chris@87
|
449 padarr : ndarray
|
Chris@87
|
450 Output array, with `pad_amt` values prepended along `axis`. The
|
Chris@87
|
451 prepended region is the mean of the first `num` values along `axis`.
|
Chris@87
|
452
|
Chris@87
|
453 """
|
Chris@87
|
454 if pad_amt == 0:
|
Chris@87
|
455 return arr
|
Chris@87
|
456
|
Chris@87
|
457 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
458 if num == 1:
|
Chris@87
|
459 return _prepend_edge(arr, pad_amt, axis)
|
Chris@87
|
460
|
Chris@87
|
461 # Use entire array if `num` is too large
|
Chris@87
|
462 if num is not None:
|
Chris@87
|
463 if num >= arr.shape[axis]:
|
Chris@87
|
464 num = None
|
Chris@87
|
465
|
Chris@87
|
466 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
467 mean_slice = tuple(slice(None) if i != axis else slice(num)
|
Chris@87
|
468 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
469
|
Chris@87
|
470 # Shape to restore singleton dimension after slicing
|
Chris@87
|
471 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
472 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
473
|
Chris@87
|
474 # Extract slice, calculate mean, reshape to add singleton dimension back
|
Chris@87
|
475 mean_chunk = arr[mean_slice].mean(axis).reshape(pad_singleton)
|
Chris@87
|
476 _round_ifneeded(mean_chunk, arr.dtype)
|
Chris@87
|
477
|
Chris@87
|
478 # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
479 return np.concatenate((mean_chunk.repeat(pad_amt, axis).astype(arr.dtype),
|
Chris@87
|
480 arr), axis=axis)
|
Chris@87
|
481
|
Chris@87
|
482
|
Chris@87
|
483 def _append_mean(arr, pad_amt, num, axis=-1):
|
Chris@87
|
484 """
|
Chris@87
|
485 Append `pad_amt` mean values along `axis`.
|
Chris@87
|
486
|
Chris@87
|
487 Parameters
|
Chris@87
|
488 ----------
|
Chris@87
|
489 arr : ndarray
|
Chris@87
|
490 Input array of arbitrary shape.
|
Chris@87
|
491 pad_amt : int
|
Chris@87
|
492 Amount of padding to append.
|
Chris@87
|
493 num : int
|
Chris@87
|
494 Depth into `arr` along `axis` to calculate mean.
|
Chris@87
|
495 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
496 axis : int
|
Chris@87
|
497 Axis along which to pad `arr`.
|
Chris@87
|
498
|
Chris@87
|
499 Returns
|
Chris@87
|
500 -------
|
Chris@87
|
501 padarr : ndarray
|
Chris@87
|
502 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
503 appended region is the maximum of the final `num` values along `axis`.
|
Chris@87
|
504
|
Chris@87
|
505 """
|
Chris@87
|
506 if pad_amt == 0:
|
Chris@87
|
507 return arr
|
Chris@87
|
508
|
Chris@87
|
509 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
510 if num == 1:
|
Chris@87
|
511 return _append_edge(arr, pad_amt, axis)
|
Chris@87
|
512
|
Chris@87
|
513 # Use entire array if `num` is too large
|
Chris@87
|
514 if num is not None:
|
Chris@87
|
515 if num >= arr.shape[axis]:
|
Chris@87
|
516 num = None
|
Chris@87
|
517
|
Chris@87
|
518 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
519 end = arr.shape[axis] - 1
|
Chris@87
|
520 if num is not None:
|
Chris@87
|
521 mean_slice = tuple(
|
Chris@87
|
522 slice(None) if i != axis else slice(end, end - num, -1)
|
Chris@87
|
523 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
524 else:
|
Chris@87
|
525 mean_slice = tuple(slice(None) for x in arr.shape)
|
Chris@87
|
526
|
Chris@87
|
527 # Shape to restore singleton dimension after slicing
|
Chris@87
|
528 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
529 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
530
|
Chris@87
|
531 # Extract slice, calculate mean, reshape to add singleton dimension back
|
Chris@87
|
532 mean_chunk = arr[mean_slice].mean(axis=axis).reshape(pad_singleton)
|
Chris@87
|
533 _round_ifneeded(mean_chunk, arr.dtype)
|
Chris@87
|
534
|
Chris@87
|
535 # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
536 return np.concatenate(
|
Chris@87
|
537 (arr, mean_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis)
|
Chris@87
|
538
|
Chris@87
|
539
|
Chris@87
|
540 def _prepend_med(arr, pad_amt, num, axis=-1):
|
Chris@87
|
541 """
|
Chris@87
|
542 Prepend `pad_amt` median values along `axis`.
|
Chris@87
|
543
|
Chris@87
|
544 Parameters
|
Chris@87
|
545 ----------
|
Chris@87
|
546 arr : ndarray
|
Chris@87
|
547 Input array of arbitrary shape.
|
Chris@87
|
548 pad_amt : int
|
Chris@87
|
549 Amount of padding to prepend.
|
Chris@87
|
550 num : int
|
Chris@87
|
551 Depth into `arr` along `axis` to calculate median.
|
Chris@87
|
552 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
553 axis : int
|
Chris@87
|
554 Axis along which to pad `arr`.
|
Chris@87
|
555
|
Chris@87
|
556 Returns
|
Chris@87
|
557 -------
|
Chris@87
|
558 padarr : ndarray
|
Chris@87
|
559 Output array, with `pad_amt` values prepended along `axis`. The
|
Chris@87
|
560 prepended region is the median of the first `num` values along `axis`.
|
Chris@87
|
561
|
Chris@87
|
562 """
|
Chris@87
|
563 if pad_amt == 0:
|
Chris@87
|
564 return arr
|
Chris@87
|
565
|
Chris@87
|
566 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
567 if num == 1:
|
Chris@87
|
568 return _prepend_edge(arr, pad_amt, axis)
|
Chris@87
|
569
|
Chris@87
|
570 # Use entire array if `num` is too large
|
Chris@87
|
571 if num is not None:
|
Chris@87
|
572 if num >= arr.shape[axis]:
|
Chris@87
|
573 num = None
|
Chris@87
|
574
|
Chris@87
|
575 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
576 med_slice = tuple(slice(None) if i != axis else slice(num)
|
Chris@87
|
577 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
578
|
Chris@87
|
579 # Shape to restore singleton dimension after slicing
|
Chris@87
|
580 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
581 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
582
|
Chris@87
|
583 # Extract slice, calculate median, reshape to add singleton dimension back
|
Chris@87
|
584 med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton)
|
Chris@87
|
585 _round_ifneeded(med_chunk, arr.dtype)
|
Chris@87
|
586
|
Chris@87
|
587 # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
588 return np.concatenate(
|
Chris@87
|
589 (med_chunk.repeat(pad_amt, axis).astype(arr.dtype), arr), axis=axis)
|
Chris@87
|
590
|
Chris@87
|
591
|
Chris@87
|
592 def _append_med(arr, pad_amt, num, axis=-1):
|
Chris@87
|
593 """
|
Chris@87
|
594 Append `pad_amt` median values along `axis`.
|
Chris@87
|
595
|
Chris@87
|
596 Parameters
|
Chris@87
|
597 ----------
|
Chris@87
|
598 arr : ndarray
|
Chris@87
|
599 Input array of arbitrary shape.
|
Chris@87
|
600 pad_amt : int
|
Chris@87
|
601 Amount of padding to append.
|
Chris@87
|
602 num : int
|
Chris@87
|
603 Depth into `arr` along `axis` to calculate median.
|
Chris@87
|
604 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
605 axis : int
|
Chris@87
|
606 Axis along which to pad `arr`.
|
Chris@87
|
607
|
Chris@87
|
608 Returns
|
Chris@87
|
609 -------
|
Chris@87
|
610 padarr : ndarray
|
Chris@87
|
611 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
612 appended region is the median of the final `num` values along `axis`.
|
Chris@87
|
613
|
Chris@87
|
614 """
|
Chris@87
|
615 if pad_amt == 0:
|
Chris@87
|
616 return arr
|
Chris@87
|
617
|
Chris@87
|
618 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
619 if num == 1:
|
Chris@87
|
620 return _append_edge(arr, pad_amt, axis)
|
Chris@87
|
621
|
Chris@87
|
622 # Use entire array if `num` is too large
|
Chris@87
|
623 if num is not None:
|
Chris@87
|
624 if num >= arr.shape[axis]:
|
Chris@87
|
625 num = None
|
Chris@87
|
626
|
Chris@87
|
627 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
628 end = arr.shape[axis] - 1
|
Chris@87
|
629 if num is not None:
|
Chris@87
|
630 med_slice = tuple(
|
Chris@87
|
631 slice(None) if i != axis else slice(end, end - num, -1)
|
Chris@87
|
632 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
633 else:
|
Chris@87
|
634 med_slice = tuple(slice(None) for x in arr.shape)
|
Chris@87
|
635
|
Chris@87
|
636 # Shape to restore singleton dimension after slicing
|
Chris@87
|
637 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
638 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
639
|
Chris@87
|
640 # Extract slice, calculate median, reshape to add singleton dimension back
|
Chris@87
|
641 med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton)
|
Chris@87
|
642 _round_ifneeded(med_chunk, arr.dtype)
|
Chris@87
|
643
|
Chris@87
|
644 # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
645 return np.concatenate(
|
Chris@87
|
646 (arr, med_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis)
|
Chris@87
|
647
|
Chris@87
|
648
|
Chris@87
|
649 def _prepend_min(arr, pad_amt, num, axis=-1):
|
Chris@87
|
650 """
|
Chris@87
|
651 Prepend `pad_amt` minimum values along `axis`.
|
Chris@87
|
652
|
Chris@87
|
653 Parameters
|
Chris@87
|
654 ----------
|
Chris@87
|
655 arr : ndarray
|
Chris@87
|
656 Input array of arbitrary shape.
|
Chris@87
|
657 pad_amt : int
|
Chris@87
|
658 Amount of padding to prepend.
|
Chris@87
|
659 num : int
|
Chris@87
|
660 Depth into `arr` along `axis` to calculate minimum.
|
Chris@87
|
661 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
662 axis : int
|
Chris@87
|
663 Axis along which to pad `arr`.
|
Chris@87
|
664
|
Chris@87
|
665 Returns
|
Chris@87
|
666 -------
|
Chris@87
|
667 padarr : ndarray
|
Chris@87
|
668 Output array, with `pad_amt` values prepended along `axis`. The
|
Chris@87
|
669 prepended region is the minimum of the first `num` values along
|
Chris@87
|
670 `axis`.
|
Chris@87
|
671
|
Chris@87
|
672 """
|
Chris@87
|
673 if pad_amt == 0:
|
Chris@87
|
674 return arr
|
Chris@87
|
675
|
Chris@87
|
676 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
677 if num == 1:
|
Chris@87
|
678 return _prepend_edge(arr, pad_amt, axis)
|
Chris@87
|
679
|
Chris@87
|
680 # Use entire array if `num` is too large
|
Chris@87
|
681 if num is not None:
|
Chris@87
|
682 if num >= arr.shape[axis]:
|
Chris@87
|
683 num = None
|
Chris@87
|
684
|
Chris@87
|
685 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
686 min_slice = tuple(slice(None) if i != axis else slice(num)
|
Chris@87
|
687 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
688
|
Chris@87
|
689 # Shape to restore singleton dimension after slicing
|
Chris@87
|
690 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
691 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
692
|
Chris@87
|
693 # Extract slice, calculate min, reshape to add singleton dimension back
|
Chris@87
|
694 min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton)
|
Chris@87
|
695
|
Chris@87
|
696 # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
697 return np.concatenate((min_chunk.repeat(pad_amt, axis=axis), arr),
|
Chris@87
|
698 axis=axis)
|
Chris@87
|
699
|
Chris@87
|
700
|
Chris@87
|
701 def _append_min(arr, pad_amt, num, axis=-1):
|
Chris@87
|
702 """
|
Chris@87
|
703 Append `pad_amt` median values along `axis`.
|
Chris@87
|
704
|
Chris@87
|
705 Parameters
|
Chris@87
|
706 ----------
|
Chris@87
|
707 arr : ndarray
|
Chris@87
|
708 Input array of arbitrary shape.
|
Chris@87
|
709 pad_amt : int
|
Chris@87
|
710 Amount of padding to append.
|
Chris@87
|
711 num : int
|
Chris@87
|
712 Depth into `arr` along `axis` to calculate minimum.
|
Chris@87
|
713 Range: [1, `arr.shape[axis]`] or None (entire axis)
|
Chris@87
|
714 axis : int
|
Chris@87
|
715 Axis along which to pad `arr`.
|
Chris@87
|
716
|
Chris@87
|
717 Returns
|
Chris@87
|
718 -------
|
Chris@87
|
719 padarr : ndarray
|
Chris@87
|
720 Output array, with `pad_amt` values appended along `axis`. The
|
Chris@87
|
721 appended region is the minimum of the final `num` values along `axis`.
|
Chris@87
|
722
|
Chris@87
|
723 """
|
Chris@87
|
724 if pad_amt == 0:
|
Chris@87
|
725 return arr
|
Chris@87
|
726
|
Chris@87
|
727 # Equivalent to edge padding for single value, so do that instead
|
Chris@87
|
728 if num == 1:
|
Chris@87
|
729 return _append_edge(arr, pad_amt, axis)
|
Chris@87
|
730
|
Chris@87
|
731 # Use entire array if `num` is too large
|
Chris@87
|
732 if num is not None:
|
Chris@87
|
733 if num >= arr.shape[axis]:
|
Chris@87
|
734 num = None
|
Chris@87
|
735
|
Chris@87
|
736 # Slice a chunk from the edge to calculate stats on
|
Chris@87
|
737 end = arr.shape[axis] - 1
|
Chris@87
|
738 if num is not None:
|
Chris@87
|
739 min_slice = tuple(
|
Chris@87
|
740 slice(None) if i != axis else slice(end, end - num, -1)
|
Chris@87
|
741 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
742 else:
|
Chris@87
|
743 min_slice = tuple(slice(None) for x in arr.shape)
|
Chris@87
|
744
|
Chris@87
|
745 # Shape to restore singleton dimension after slicing
|
Chris@87
|
746 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
747 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
748
|
Chris@87
|
749 # Extract slice, calculate min, reshape to add singleton dimension back
|
Chris@87
|
750 min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton)
|
Chris@87
|
751
|
Chris@87
|
752 # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt`
|
Chris@87
|
753 return np.concatenate((arr, min_chunk.repeat(pad_amt, axis=axis)),
|
Chris@87
|
754 axis=axis)
|
Chris@87
|
755
|
Chris@87
|
756
|
Chris@87
|
757 def _pad_ref(arr, pad_amt, method, axis=-1):
|
Chris@87
|
758 """
|
Chris@87
|
759 Pad `axis` of `arr` by reflection.
|
Chris@87
|
760
|
Chris@87
|
761 Parameters
|
Chris@87
|
762 ----------
|
Chris@87
|
763 arr : ndarray
|
Chris@87
|
764 Input array of arbitrary shape.
|
Chris@87
|
765 pad_amt : tuple of ints, length 2
|
Chris@87
|
766 Padding to (prepend, append) along `axis`.
|
Chris@87
|
767 method : str
|
Chris@87
|
768 Controls method of reflection; options are 'even' or 'odd'.
|
Chris@87
|
769 axis : int
|
Chris@87
|
770 Axis along which to pad `arr`.
|
Chris@87
|
771
|
Chris@87
|
772 Returns
|
Chris@87
|
773 -------
|
Chris@87
|
774 padarr : ndarray
|
Chris@87
|
775 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
|
Chris@87
|
776 values appended along `axis`. Both regions are padded with reflected
|
Chris@87
|
777 values from the original array.
|
Chris@87
|
778
|
Chris@87
|
779 Notes
|
Chris@87
|
780 -----
|
Chris@87
|
781 This algorithm does not pad with repetition, i.e. the edges are not
|
Chris@87
|
782 repeated in the reflection. For that behavior, use `method='symmetric'`.
|
Chris@87
|
783
|
Chris@87
|
784 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
|
Chris@87
|
785 single function, lest the indexing tricks in non-integer multiples of the
|
Chris@87
|
786 original shape would violate repetition in the final iteration.
|
Chris@87
|
787
|
Chris@87
|
788 """
|
Chris@87
|
789 # Implicit booleanness to test for zero (or None) in any scalar type
|
Chris@87
|
790 if pad_amt[0] == 0 and pad_amt[1] == 0:
|
Chris@87
|
791 return arr
|
Chris@87
|
792
|
Chris@87
|
793 ##########################################################################
|
Chris@87
|
794 # Prepended region
|
Chris@87
|
795
|
Chris@87
|
796 # Slice off a reverse indexed chunk from near edge to pad `arr` before
|
Chris@87
|
797 ref_slice = tuple(slice(None) if i != axis else slice(pad_amt[0], 0, -1)
|
Chris@87
|
798 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
799
|
Chris@87
|
800 ref_chunk1 = arr[ref_slice]
|
Chris@87
|
801
|
Chris@87
|
802 # Shape to restore singleton dimension after slicing
|
Chris@87
|
803 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
804 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
805 if pad_amt[0] == 1:
|
Chris@87
|
806 ref_chunk1 = ref_chunk1.reshape(pad_singleton)
|
Chris@87
|
807
|
Chris@87
|
808 # Memory/computationally more expensive, only do this if `method='odd'`
|
Chris@87
|
809 if 'odd' in method and pad_amt[0] > 0:
|
Chris@87
|
810 edge_slice1 = tuple(slice(None) if i != axis else 0
|
Chris@87
|
811 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
812 edge_chunk = arr[edge_slice1].reshape(pad_singleton)
|
Chris@87
|
813 ref_chunk1 = 2 * edge_chunk - ref_chunk1
|
Chris@87
|
814 del edge_chunk
|
Chris@87
|
815
|
Chris@87
|
816 ##########################################################################
|
Chris@87
|
817 # Appended region
|
Chris@87
|
818
|
Chris@87
|
819 # Slice off a reverse indexed chunk from far edge to pad `arr` after
|
Chris@87
|
820 start = arr.shape[axis] - pad_amt[1] - 1
|
Chris@87
|
821 end = arr.shape[axis] - 1
|
Chris@87
|
822 ref_slice = tuple(slice(None) if i != axis else slice(start, end)
|
Chris@87
|
823 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
824 rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1)
|
Chris@87
|
825 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
826 ref_chunk2 = arr[ref_slice][rev_idx]
|
Chris@87
|
827
|
Chris@87
|
828 if pad_amt[1] == 1:
|
Chris@87
|
829 ref_chunk2 = ref_chunk2.reshape(pad_singleton)
|
Chris@87
|
830
|
Chris@87
|
831 if 'odd' in method:
|
Chris@87
|
832 edge_slice2 = tuple(slice(None) if i != axis else -1
|
Chris@87
|
833 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
834 edge_chunk = arr[edge_slice2].reshape(pad_singleton)
|
Chris@87
|
835 ref_chunk2 = 2 * edge_chunk - ref_chunk2
|
Chris@87
|
836 del edge_chunk
|
Chris@87
|
837
|
Chris@87
|
838 # Concatenate `arr` with both chunks, extending along `axis`
|
Chris@87
|
839 return np.concatenate((ref_chunk1, arr, ref_chunk2), axis=axis)
|
Chris@87
|
840
|
Chris@87
|
841
|
Chris@87
|
842 def _pad_sym(arr, pad_amt, method, axis=-1):
|
Chris@87
|
843 """
|
Chris@87
|
844 Pad `axis` of `arr` by symmetry.
|
Chris@87
|
845
|
Chris@87
|
846 Parameters
|
Chris@87
|
847 ----------
|
Chris@87
|
848 arr : ndarray
|
Chris@87
|
849 Input array of arbitrary shape.
|
Chris@87
|
850 pad_amt : tuple of ints, length 2
|
Chris@87
|
851 Padding to (prepend, append) along `axis`.
|
Chris@87
|
852 method : str
|
Chris@87
|
853 Controls method of symmetry; options are 'even' or 'odd'.
|
Chris@87
|
854 axis : int
|
Chris@87
|
855 Axis along which to pad `arr`.
|
Chris@87
|
856
|
Chris@87
|
857 Returns
|
Chris@87
|
858 -------
|
Chris@87
|
859 padarr : ndarray
|
Chris@87
|
860 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
|
Chris@87
|
861 values appended along `axis`. Both regions are padded with symmetric
|
Chris@87
|
862 values from the original array.
|
Chris@87
|
863
|
Chris@87
|
864 Notes
|
Chris@87
|
865 -----
|
Chris@87
|
866 This algorithm DOES pad with repetition, i.e. the edges are repeated.
|
Chris@87
|
867 For a method that does not repeat edges, use `method='reflect'`.
|
Chris@87
|
868
|
Chris@87
|
869 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
|
Chris@87
|
870 single function, lest the indexing tricks in non-integer multiples of the
|
Chris@87
|
871 original shape would violate repetition in the final iteration.
|
Chris@87
|
872
|
Chris@87
|
873 """
|
Chris@87
|
874 # Implicit booleanness to test for zero (or None) in any scalar type
|
Chris@87
|
875 if pad_amt[0] == 0 and pad_amt[1] == 0:
|
Chris@87
|
876 return arr
|
Chris@87
|
877
|
Chris@87
|
878 ##########################################################################
|
Chris@87
|
879 # Prepended region
|
Chris@87
|
880
|
Chris@87
|
881 # Slice off a reverse indexed chunk from near edge to pad `arr` before
|
Chris@87
|
882 sym_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[0])
|
Chris@87
|
883 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
884 rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1)
|
Chris@87
|
885 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
886 sym_chunk1 = arr[sym_slice][rev_idx]
|
Chris@87
|
887
|
Chris@87
|
888 # Shape to restore singleton dimension after slicing
|
Chris@87
|
889 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
890 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
891 if pad_amt[0] == 1:
|
Chris@87
|
892 sym_chunk1 = sym_chunk1.reshape(pad_singleton)
|
Chris@87
|
893
|
Chris@87
|
894 # Memory/computationally more expensive, only do this if `method='odd'`
|
Chris@87
|
895 if 'odd' in method and pad_amt[0] > 0:
|
Chris@87
|
896 edge_slice1 = tuple(slice(None) if i != axis else 0
|
Chris@87
|
897 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
898 edge_chunk = arr[edge_slice1].reshape(pad_singleton)
|
Chris@87
|
899 sym_chunk1 = 2 * edge_chunk - sym_chunk1
|
Chris@87
|
900 del edge_chunk
|
Chris@87
|
901
|
Chris@87
|
902 ##########################################################################
|
Chris@87
|
903 # Appended region
|
Chris@87
|
904
|
Chris@87
|
905 # Slice off a reverse indexed chunk from far edge to pad `arr` after
|
Chris@87
|
906 start = arr.shape[axis] - pad_amt[1]
|
Chris@87
|
907 end = arr.shape[axis]
|
Chris@87
|
908 sym_slice = tuple(slice(None) if i != axis else slice(start, end)
|
Chris@87
|
909 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
910 sym_chunk2 = arr[sym_slice][rev_idx]
|
Chris@87
|
911
|
Chris@87
|
912 if pad_amt[1] == 1:
|
Chris@87
|
913 sym_chunk2 = sym_chunk2.reshape(pad_singleton)
|
Chris@87
|
914
|
Chris@87
|
915 if 'odd' in method:
|
Chris@87
|
916 edge_slice2 = tuple(slice(None) if i != axis else -1
|
Chris@87
|
917 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
918 edge_chunk = arr[edge_slice2].reshape(pad_singleton)
|
Chris@87
|
919 sym_chunk2 = 2 * edge_chunk - sym_chunk2
|
Chris@87
|
920 del edge_chunk
|
Chris@87
|
921
|
Chris@87
|
922 # Concatenate `arr` with both chunks, extending along `axis`
|
Chris@87
|
923 return np.concatenate((sym_chunk1, arr, sym_chunk2), axis=axis)
|
Chris@87
|
924
|
Chris@87
|
925
|
Chris@87
|
926 def _pad_wrap(arr, pad_amt, axis=-1):
|
Chris@87
|
927 """
|
Chris@87
|
928 Pad `axis` of `arr` via wrapping.
|
Chris@87
|
929
|
Chris@87
|
930 Parameters
|
Chris@87
|
931 ----------
|
Chris@87
|
932 arr : ndarray
|
Chris@87
|
933 Input array of arbitrary shape.
|
Chris@87
|
934 pad_amt : tuple of ints, length 2
|
Chris@87
|
935 Padding to (prepend, append) along `axis`.
|
Chris@87
|
936 axis : int
|
Chris@87
|
937 Axis along which to pad `arr`.
|
Chris@87
|
938
|
Chris@87
|
939 Returns
|
Chris@87
|
940 -------
|
Chris@87
|
941 padarr : ndarray
|
Chris@87
|
942 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
|
Chris@87
|
943 values appended along `axis`. Both regions are padded wrapped values
|
Chris@87
|
944 from the opposite end of `axis`.
|
Chris@87
|
945
|
Chris@87
|
946 Notes
|
Chris@87
|
947 -----
|
Chris@87
|
948 This method of padding is also known as 'tile' or 'tiling'.
|
Chris@87
|
949
|
Chris@87
|
950 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
|
Chris@87
|
951 single function, lest the indexing tricks in non-integer multiples of the
|
Chris@87
|
952 original shape would violate repetition in the final iteration.
|
Chris@87
|
953
|
Chris@87
|
954 """
|
Chris@87
|
955 # Implicit booleanness to test for zero (or None) in any scalar type
|
Chris@87
|
956 if pad_amt[0] == 0 and pad_amt[1] == 0:
|
Chris@87
|
957 return arr
|
Chris@87
|
958
|
Chris@87
|
959 ##########################################################################
|
Chris@87
|
960 # Prepended region
|
Chris@87
|
961
|
Chris@87
|
962 # Slice off a reverse indexed chunk from near edge to pad `arr` before
|
Chris@87
|
963 start = arr.shape[axis] - pad_amt[0]
|
Chris@87
|
964 end = arr.shape[axis]
|
Chris@87
|
965 wrap_slice = tuple(slice(None) if i != axis else slice(start, end)
|
Chris@87
|
966 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
967 wrap_chunk1 = arr[wrap_slice]
|
Chris@87
|
968
|
Chris@87
|
969 # Shape to restore singleton dimension after slicing
|
Chris@87
|
970 pad_singleton = tuple(x if i != axis else 1
|
Chris@87
|
971 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
972 if pad_amt[0] == 1:
|
Chris@87
|
973 wrap_chunk1 = wrap_chunk1.reshape(pad_singleton)
|
Chris@87
|
974
|
Chris@87
|
975 ##########################################################################
|
Chris@87
|
976 # Appended region
|
Chris@87
|
977
|
Chris@87
|
978 # Slice off a reverse indexed chunk from far edge to pad `arr` after
|
Chris@87
|
979 wrap_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[1])
|
Chris@87
|
980 for (i, x) in enumerate(arr.shape))
|
Chris@87
|
981 wrap_chunk2 = arr[wrap_slice]
|
Chris@87
|
982
|
Chris@87
|
983 if pad_amt[1] == 1:
|
Chris@87
|
984 wrap_chunk2 = wrap_chunk2.reshape(pad_singleton)
|
Chris@87
|
985
|
Chris@87
|
986 # Concatenate `arr` with both chunks, extending along `axis`
|
Chris@87
|
987 return np.concatenate((wrap_chunk1, arr, wrap_chunk2), axis=axis)
|
Chris@87
|
988
|
Chris@87
|
989
|
Chris@87
|
990 def _normalize_shape(narray, shape):
|
Chris@87
|
991 """
|
Chris@87
|
992 Private function which does some checks and normalizes the possibly
|
Chris@87
|
993 much simpler representations of 'pad_width', 'stat_length',
|
Chris@87
|
994 'constant_values', 'end_values'.
|
Chris@87
|
995
|
Chris@87
|
996 Parameters
|
Chris@87
|
997 ----------
|
Chris@87
|
998 narray : ndarray
|
Chris@87
|
999 Input ndarray
|
Chris@87
|
1000 shape : {sequence, int}, optional
|
Chris@87
|
1001 The width of padding (pad_width) or the number of elements on the
|
Chris@87
|
1002 edge of the narray used for statistics (stat_length).
|
Chris@87
|
1003 ((before_1, after_1), ... (before_N, after_N)) unique number of
|
Chris@87
|
1004 elements for each axis where `N` is rank of `narray`.
|
Chris@87
|
1005 ((before, after),) yields same before and after constants for each
|
Chris@87
|
1006 axis.
|
Chris@87
|
1007 (constant,) or int is a shortcut for before = after = constant for
|
Chris@87
|
1008 all axes.
|
Chris@87
|
1009
|
Chris@87
|
1010 Returns
|
Chris@87
|
1011 -------
|
Chris@87
|
1012 _normalize_shape : tuple of tuples
|
Chris@87
|
1013 int => ((int, int), (int, int), ...)
|
Chris@87
|
1014 [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...)
|
Chris@87
|
1015 ((int1, int2), (int3, int4), ...) => no change
|
Chris@87
|
1016 [[int1, int2], ] => ((int1, int2), (int1, int2), ...)
|
Chris@87
|
1017 ((int1, int2), ) => ((int1, int2), (int1, int2), ...)
|
Chris@87
|
1018 [[int , ], ] => ((int, int), (int, int), ...)
|
Chris@87
|
1019 ((int , ), ) => ((int, int), (int, int), ...)
|
Chris@87
|
1020
|
Chris@87
|
1021 """
|
Chris@87
|
1022 normshp = None
|
Chris@87
|
1023 shapelen = len(np.shape(narray))
|
Chris@87
|
1024 if (isinstance(shape, int)) or shape is None:
|
Chris@87
|
1025 normshp = ((shape, shape), ) * shapelen
|
Chris@87
|
1026 elif (isinstance(shape, (tuple, list))
|
Chris@87
|
1027 and isinstance(shape[0], (tuple, list))
|
Chris@87
|
1028 and len(shape) == shapelen):
|
Chris@87
|
1029 normshp = shape
|
Chris@87
|
1030 for i in normshp:
|
Chris@87
|
1031 if len(i) != 2:
|
Chris@87
|
1032 fmt = "Unable to create correctly shaped tuple from %s"
|
Chris@87
|
1033 raise ValueError(fmt % (normshp,))
|
Chris@87
|
1034 elif (isinstance(shape, (tuple, list))
|
Chris@87
|
1035 and isinstance(shape[0], (int, float, long))
|
Chris@87
|
1036 and len(shape) == 1):
|
Chris@87
|
1037 normshp = ((shape[0], shape[0]), ) * shapelen
|
Chris@87
|
1038 elif (isinstance(shape, (tuple, list))
|
Chris@87
|
1039 and isinstance(shape[0], (int, float, long))
|
Chris@87
|
1040 and len(shape) == 2):
|
Chris@87
|
1041 normshp = (shape, ) * shapelen
|
Chris@87
|
1042 if normshp is None:
|
Chris@87
|
1043 fmt = "Unable to create correctly shaped tuple from %s"
|
Chris@87
|
1044 raise ValueError(fmt % (shape,))
|
Chris@87
|
1045 return normshp
|
Chris@87
|
1046
|
Chris@87
|
1047
|
Chris@87
|
1048 def _validate_lengths(narray, number_elements):
|
Chris@87
|
1049 """
|
Chris@87
|
1050 Private function which does some checks and reformats pad_width and
|
Chris@87
|
1051 stat_length using _normalize_shape.
|
Chris@87
|
1052
|
Chris@87
|
1053 Parameters
|
Chris@87
|
1054 ----------
|
Chris@87
|
1055 narray : ndarray
|
Chris@87
|
1056 Input ndarray
|
Chris@87
|
1057 number_elements : {sequence, int}, optional
|
Chris@87
|
1058 The width of padding (pad_width) or the number of elements on the edge
|
Chris@87
|
1059 of the narray used for statistics (stat_length).
|
Chris@87
|
1060 ((before_1, after_1), ... (before_N, after_N)) unique number of
|
Chris@87
|
1061 elements for each axis.
|
Chris@87
|
1062 ((before, after),) yields same before and after constants for each
|
Chris@87
|
1063 axis.
|
Chris@87
|
1064 (constant,) or int is a shortcut for before = after = constant for all
|
Chris@87
|
1065 axes.
|
Chris@87
|
1066
|
Chris@87
|
1067 Returns
|
Chris@87
|
1068 -------
|
Chris@87
|
1069 _validate_lengths : tuple of tuples
|
Chris@87
|
1070 int => ((int, int), (int, int), ...)
|
Chris@87
|
1071 [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...)
|
Chris@87
|
1072 ((int1, int2), (int3, int4), ...) => no change
|
Chris@87
|
1073 [[int1, int2], ] => ((int1, int2), (int1, int2), ...)
|
Chris@87
|
1074 ((int1, int2), ) => ((int1, int2), (int1, int2), ...)
|
Chris@87
|
1075 [[int , ], ] => ((int, int), (int, int), ...)
|
Chris@87
|
1076 ((int , ), ) => ((int, int), (int, int), ...)
|
Chris@87
|
1077
|
Chris@87
|
1078 """
|
Chris@87
|
1079 normshp = _normalize_shape(narray, number_elements)
|
Chris@87
|
1080 for i in normshp:
|
Chris@87
|
1081 chk = [1 if x is None else x for x in i]
|
Chris@87
|
1082 chk = [1 if x >= 0 else -1 for x in chk]
|
Chris@87
|
1083 if (chk[0] < 0) or (chk[1] < 0):
|
Chris@87
|
1084 fmt = "%s cannot contain negative values."
|
Chris@87
|
1085 raise ValueError(fmt % (number_elements,))
|
Chris@87
|
1086 return normshp
|
Chris@87
|
1087
|
Chris@87
|
1088
|
Chris@87
|
1089 ###############################################################################
|
Chris@87
|
1090 # Public functions
|
Chris@87
|
1091
|
Chris@87
|
1092
|
Chris@87
|
1093 def pad(array, pad_width, mode=None, **kwargs):
|
Chris@87
|
1094 """
|
Chris@87
|
1095 Pads an array.
|
Chris@87
|
1096
|
Chris@87
|
1097 Parameters
|
Chris@87
|
1098 ----------
|
Chris@87
|
1099 array : array_like of rank N
|
Chris@87
|
1100 Input array
|
Chris@87
|
1101 pad_width : {sequence, int}
|
Chris@87
|
1102 Number of values padded to the edges of each axis.
|
Chris@87
|
1103 ((before_1, after_1), ... (before_N, after_N)) unique pad widths
|
Chris@87
|
1104 for each axis.
|
Chris@87
|
1105 ((before, after),) yields same before and after pad for each axis.
|
Chris@87
|
1106 (pad,) or int is a shortcut for before = after = pad width for all
|
Chris@87
|
1107 axes.
|
Chris@87
|
1108 mode : {str, function}
|
Chris@87
|
1109 One of the following string values or a user supplied function.
|
Chris@87
|
1110
|
Chris@87
|
1111 'constant'
|
Chris@87
|
1112 Pads with a constant value.
|
Chris@87
|
1113 'edge'
|
Chris@87
|
1114 Pads with the edge values of array.
|
Chris@87
|
1115 'linear_ramp'
|
Chris@87
|
1116 Pads with the linear ramp between end_value and the
|
Chris@87
|
1117 array edge value.
|
Chris@87
|
1118 'maximum'
|
Chris@87
|
1119 Pads with the maximum value of all or part of the
|
Chris@87
|
1120 vector along each axis.
|
Chris@87
|
1121 'mean'
|
Chris@87
|
1122 Pads with the mean value of all or part of the
|
Chris@87
|
1123 vector along each axis.
|
Chris@87
|
1124 'median'
|
Chris@87
|
1125 Pads with the median value of all or part of the
|
Chris@87
|
1126 vector along each axis.
|
Chris@87
|
1127 'minimum'
|
Chris@87
|
1128 Pads with the minimum value of all or part of the
|
Chris@87
|
1129 vector along each axis.
|
Chris@87
|
1130 'reflect'
|
Chris@87
|
1131 Pads with the reflection of the vector mirrored on
|
Chris@87
|
1132 the first and last values of the vector along each
|
Chris@87
|
1133 axis.
|
Chris@87
|
1134 'symmetric'
|
Chris@87
|
1135 Pads with the reflection of the vector mirrored
|
Chris@87
|
1136 along the edge of the array.
|
Chris@87
|
1137 'wrap'
|
Chris@87
|
1138 Pads with the wrap of the vector along the axis.
|
Chris@87
|
1139 The first values are used to pad the end and the
|
Chris@87
|
1140 end values are used to pad the beginning.
|
Chris@87
|
1141 <function>
|
Chris@87
|
1142 Padding function, see Notes.
|
Chris@87
|
1143 stat_length : {sequence, int}, optional
|
Chris@87
|
1144 Used in 'maximum', 'mean', 'median', and 'minimum'. Number of
|
Chris@87
|
1145 values at edge of each axis used to calculate the statistic value.
|
Chris@87
|
1146
|
Chris@87
|
1147 ((before_1, after_1), ... (before_N, after_N)) unique statistic
|
Chris@87
|
1148 lengths for each axis.
|
Chris@87
|
1149
|
Chris@87
|
1150 ((before, after),) yields same before and after statistic lengths
|
Chris@87
|
1151 for each axis.
|
Chris@87
|
1152
|
Chris@87
|
1153 (stat_length,) or int is a shortcut for before = after = statistic
|
Chris@87
|
1154 length for all axes.
|
Chris@87
|
1155
|
Chris@87
|
1156 Default is ``None``, to use the entire axis.
|
Chris@87
|
1157 constant_values : {sequence, int}, optional
|
Chris@87
|
1158 Used in 'constant'. The values to set the padded values for each
|
Chris@87
|
1159 axis.
|
Chris@87
|
1160
|
Chris@87
|
1161 ((before_1, after_1), ... (before_N, after_N)) unique pad constants
|
Chris@87
|
1162 for each axis.
|
Chris@87
|
1163
|
Chris@87
|
1164 ((before, after),) yields same before and after constants for each
|
Chris@87
|
1165 axis.
|
Chris@87
|
1166
|
Chris@87
|
1167 (constant,) or int is a shortcut for before = after = constant for
|
Chris@87
|
1168 all axes.
|
Chris@87
|
1169
|
Chris@87
|
1170 Default is 0.
|
Chris@87
|
1171 end_values : {sequence, int}, optional
|
Chris@87
|
1172 Used in 'linear_ramp'. The values used for the ending value of the
|
Chris@87
|
1173 linear_ramp and that will form the edge of the padded array.
|
Chris@87
|
1174
|
Chris@87
|
1175 ((before_1, after_1), ... (before_N, after_N)) unique end values
|
Chris@87
|
1176 for each axis.
|
Chris@87
|
1177
|
Chris@87
|
1178 ((before, after),) yields same before and after end values for each
|
Chris@87
|
1179 axis.
|
Chris@87
|
1180
|
Chris@87
|
1181 (constant,) or int is a shortcut for before = after = end value for
|
Chris@87
|
1182 all axes.
|
Chris@87
|
1183
|
Chris@87
|
1184 Default is 0.
|
Chris@87
|
1185 reflect_type : str {'even', 'odd'}, optional
|
Chris@87
|
1186 Used in 'reflect', and 'symmetric'. The 'even' style is the
|
Chris@87
|
1187 default with an unaltered reflection around the edge value. For
|
Chris@87
|
1188 the 'odd' style, the extented part of the array is created by
|
Chris@87
|
1189 subtracting the reflected values from two times the edge value.
|
Chris@87
|
1190
|
Chris@87
|
1191 Returns
|
Chris@87
|
1192 -------
|
Chris@87
|
1193 pad : ndarray
|
Chris@87
|
1194 Padded array of rank equal to `array` with shape increased
|
Chris@87
|
1195 according to `pad_width`.
|
Chris@87
|
1196
|
Chris@87
|
1197 Notes
|
Chris@87
|
1198 -----
|
Chris@87
|
1199 .. versionadded:: 1.7.0
|
Chris@87
|
1200
|
Chris@87
|
1201 For an array with rank greater than 1, some of the padding of later
|
Chris@87
|
1202 axes is calculated from padding of previous axes. This is easiest to
|
Chris@87
|
1203 think about with a rank 2 array where the corners of the padded array
|
Chris@87
|
1204 are calculated by using padded values from the first axis.
|
Chris@87
|
1205
|
Chris@87
|
1206 The padding function, if used, should return a rank 1 array equal in
|
Chris@87
|
1207 length to the vector argument with padded values replaced. It has the
|
Chris@87
|
1208 following signature::
|
Chris@87
|
1209
|
Chris@87
|
1210 padding_func(vector, iaxis_pad_width, iaxis, **kwargs)
|
Chris@87
|
1211
|
Chris@87
|
1212 where
|
Chris@87
|
1213
|
Chris@87
|
1214 vector : ndarray
|
Chris@87
|
1215 A rank 1 array already padded with zeros. Padded values are
|
Chris@87
|
1216 vector[:pad_tuple[0]] and vector[-pad_tuple[1]:].
|
Chris@87
|
1217 iaxis_pad_width : tuple
|
Chris@87
|
1218 A 2-tuple of ints, iaxis_pad_width[0] represents the number of
|
Chris@87
|
1219 values padded at the beginning of vector where
|
Chris@87
|
1220 iaxis_pad_width[1] represents the number of values padded at
|
Chris@87
|
1221 the end of vector.
|
Chris@87
|
1222 iaxis : int
|
Chris@87
|
1223 The axis currently being calculated.
|
Chris@87
|
1224 kwargs : misc
|
Chris@87
|
1225 Any keyword arguments the function requires.
|
Chris@87
|
1226
|
Chris@87
|
1227 Examples
|
Chris@87
|
1228 --------
|
Chris@87
|
1229 >>> a = [1, 2, 3, 4, 5]
|
Chris@87
|
1230 >>> np.lib.pad(a, (2,3), 'constant', constant_values=(4,6))
|
Chris@87
|
1231 array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6])
|
Chris@87
|
1232
|
Chris@87
|
1233 >>> np.lib.pad(a, (2,3), 'edge')
|
Chris@87
|
1234 array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5])
|
Chris@87
|
1235
|
Chris@87
|
1236 >>> np.lib.pad(a, (2,3), 'linear_ramp', end_values=(5,-4))
|
Chris@87
|
1237 array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4])
|
Chris@87
|
1238
|
Chris@87
|
1239 >>> np.lib.pad(a, (2,), 'maximum')
|
Chris@87
|
1240 array([5, 5, 1, 2, 3, 4, 5, 5, 5])
|
Chris@87
|
1241
|
Chris@87
|
1242 >>> np.lib.pad(a, (2,), 'mean')
|
Chris@87
|
1243 array([3, 3, 1, 2, 3, 4, 5, 3, 3])
|
Chris@87
|
1244
|
Chris@87
|
1245 >>> np.lib.pad(a, (2,), 'median')
|
Chris@87
|
1246 array([3, 3, 1, 2, 3, 4, 5, 3, 3])
|
Chris@87
|
1247
|
Chris@87
|
1248 >>> a = [[1,2], [3,4]]
|
Chris@87
|
1249 >>> np.lib.pad(a, ((3, 2), (2, 3)), 'minimum')
|
Chris@87
|
1250 array([[1, 1, 1, 2, 1, 1, 1],
|
Chris@87
|
1251 [1, 1, 1, 2, 1, 1, 1],
|
Chris@87
|
1252 [1, 1, 1, 2, 1, 1, 1],
|
Chris@87
|
1253 [1, 1, 1, 2, 1, 1, 1],
|
Chris@87
|
1254 [3, 3, 3, 4, 3, 3, 3],
|
Chris@87
|
1255 [1, 1, 1, 2, 1, 1, 1],
|
Chris@87
|
1256 [1, 1, 1, 2, 1, 1, 1]])
|
Chris@87
|
1257
|
Chris@87
|
1258 >>> a = [1, 2, 3, 4, 5]
|
Chris@87
|
1259 >>> np.lib.pad(a, (2,3), 'reflect')
|
Chris@87
|
1260 array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])
|
Chris@87
|
1261
|
Chris@87
|
1262 >>> np.lib.pad(a, (2,3), 'reflect', reflect_type='odd')
|
Chris@87
|
1263 array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
|
Chris@87
|
1264
|
Chris@87
|
1265 >>> np.lib.pad(a, (2,3), 'symmetric')
|
Chris@87
|
1266 array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])
|
Chris@87
|
1267
|
Chris@87
|
1268 >>> np.lib.pad(a, (2,3), 'symmetric', reflect_type='odd')
|
Chris@87
|
1269 array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])
|
Chris@87
|
1270
|
Chris@87
|
1271 >>> np.lib.pad(a, (2,3), 'wrap')
|
Chris@87
|
1272 array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])
|
Chris@87
|
1273
|
Chris@87
|
1274 >>> def padwithtens(vector, pad_width, iaxis, kwargs):
|
Chris@87
|
1275 ... vector[:pad_width[0]] = 10
|
Chris@87
|
1276 ... vector[-pad_width[1]:] = 10
|
Chris@87
|
1277 ... return vector
|
Chris@87
|
1278
|
Chris@87
|
1279 >>> a = np.arange(6)
|
Chris@87
|
1280 >>> a = a.reshape((2,3))
|
Chris@87
|
1281
|
Chris@87
|
1282 >>> np.lib.pad(a, 2, padwithtens)
|
Chris@87
|
1283 array([[10, 10, 10, 10, 10, 10, 10],
|
Chris@87
|
1284 [10, 10, 10, 10, 10, 10, 10],
|
Chris@87
|
1285 [10, 10, 0, 1, 2, 10, 10],
|
Chris@87
|
1286 [10, 10, 3, 4, 5, 10, 10],
|
Chris@87
|
1287 [10, 10, 10, 10, 10, 10, 10],
|
Chris@87
|
1288 [10, 10, 10, 10, 10, 10, 10]])
|
Chris@87
|
1289 """
|
Chris@87
|
1290
|
Chris@87
|
1291 narray = np.array(array)
|
Chris@87
|
1292 pad_width = _validate_lengths(narray, pad_width)
|
Chris@87
|
1293
|
Chris@87
|
1294 allowedkwargs = {
|
Chris@87
|
1295 'constant': ['constant_values'],
|
Chris@87
|
1296 'edge': [],
|
Chris@87
|
1297 'linear_ramp': ['end_values'],
|
Chris@87
|
1298 'maximum': ['stat_length'],
|
Chris@87
|
1299 'mean': ['stat_length'],
|
Chris@87
|
1300 'median': ['stat_length'],
|
Chris@87
|
1301 'minimum': ['stat_length'],
|
Chris@87
|
1302 'reflect': ['reflect_type'],
|
Chris@87
|
1303 'symmetric': ['reflect_type'],
|
Chris@87
|
1304 'wrap': [],
|
Chris@87
|
1305 }
|
Chris@87
|
1306
|
Chris@87
|
1307 kwdefaults = {
|
Chris@87
|
1308 'stat_length': None,
|
Chris@87
|
1309 'constant_values': 0,
|
Chris@87
|
1310 'end_values': 0,
|
Chris@87
|
1311 'reflect_type': 'even',
|
Chris@87
|
1312 }
|
Chris@87
|
1313
|
Chris@87
|
1314 if isinstance(mode, str):
|
Chris@87
|
1315 # Make sure have allowed kwargs appropriate for mode
|
Chris@87
|
1316 for key in kwargs:
|
Chris@87
|
1317 if key not in allowedkwargs[mode]:
|
Chris@87
|
1318 raise ValueError('%s keyword not in allowed keywords %s' %
|
Chris@87
|
1319 (key, allowedkwargs[mode]))
|
Chris@87
|
1320
|
Chris@87
|
1321 # Set kwarg defaults
|
Chris@87
|
1322 for kw in allowedkwargs[mode]:
|
Chris@87
|
1323 kwargs.setdefault(kw, kwdefaults[kw])
|
Chris@87
|
1324
|
Chris@87
|
1325 # Need to only normalize particular keywords.
|
Chris@87
|
1326 for i in kwargs:
|
Chris@87
|
1327 if i == 'stat_length':
|
Chris@87
|
1328 kwargs[i] = _validate_lengths(narray, kwargs[i])
|
Chris@87
|
1329 if i in ['end_values', 'constant_values']:
|
Chris@87
|
1330 kwargs[i] = _normalize_shape(narray, kwargs[i])
|
Chris@87
|
1331 elif mode is None:
|
Chris@87
|
1332 raise ValueError('Keyword "mode" must be a function or one of %s.' %
|
Chris@87
|
1333 (list(allowedkwargs.keys()),))
|
Chris@87
|
1334 else:
|
Chris@87
|
1335 # Drop back to old, slower np.apply_along_axis mode for user-supplied
|
Chris@87
|
1336 # vector function
|
Chris@87
|
1337 function = mode
|
Chris@87
|
1338
|
Chris@87
|
1339 # Create a new padded array
|
Chris@87
|
1340 rank = list(range(len(narray.shape)))
|
Chris@87
|
1341 total_dim_increase = [np.sum(pad_width[i]) for i in rank]
|
Chris@87
|
1342 offset_slices = [slice(pad_width[i][0],
|
Chris@87
|
1343 pad_width[i][0] + narray.shape[i])
|
Chris@87
|
1344 for i in rank]
|
Chris@87
|
1345 new_shape = np.array(narray.shape) + total_dim_increase
|
Chris@87
|
1346 newmat = np.zeros(new_shape, narray.dtype)
|
Chris@87
|
1347
|
Chris@87
|
1348 # Insert the original array into the padded array
|
Chris@87
|
1349 newmat[offset_slices] = narray
|
Chris@87
|
1350
|
Chris@87
|
1351 # This is the core of pad ...
|
Chris@87
|
1352 for iaxis in rank:
|
Chris@87
|
1353 np.apply_along_axis(function,
|
Chris@87
|
1354 iaxis,
|
Chris@87
|
1355 newmat,
|
Chris@87
|
1356 pad_width[iaxis],
|
Chris@87
|
1357 iaxis,
|
Chris@87
|
1358 kwargs)
|
Chris@87
|
1359 return newmat
|
Chris@87
|
1360
|
Chris@87
|
1361 # If we get here, use new padding method
|
Chris@87
|
1362 newmat = narray.copy()
|
Chris@87
|
1363
|
Chris@87
|
1364 # API preserved, but completely new algorithm which pads by building the
|
Chris@87
|
1365 # entire block to pad before/after `arr` with in one step, for each axis.
|
Chris@87
|
1366 if mode == 'constant':
|
Chris@87
|
1367 for axis, ((pad_before, pad_after), (before_val, after_val)) \
|
Chris@87
|
1368 in enumerate(zip(pad_width, kwargs['constant_values'])):
|
Chris@87
|
1369 newmat = _prepend_const(newmat, pad_before, before_val, axis)
|
Chris@87
|
1370 newmat = _append_const(newmat, pad_after, after_val, axis)
|
Chris@87
|
1371
|
Chris@87
|
1372 elif mode == 'edge':
|
Chris@87
|
1373 for axis, (pad_before, pad_after) in enumerate(pad_width):
|
Chris@87
|
1374 newmat = _prepend_edge(newmat, pad_before, axis)
|
Chris@87
|
1375 newmat = _append_edge(newmat, pad_after, axis)
|
Chris@87
|
1376
|
Chris@87
|
1377 elif mode == 'linear_ramp':
|
Chris@87
|
1378 for axis, ((pad_before, pad_after), (before_val, after_val)) \
|
Chris@87
|
1379 in enumerate(zip(pad_width, kwargs['end_values'])):
|
Chris@87
|
1380 newmat = _prepend_ramp(newmat, pad_before, before_val, axis)
|
Chris@87
|
1381 newmat = _append_ramp(newmat, pad_after, after_val, axis)
|
Chris@87
|
1382
|
Chris@87
|
1383 elif mode == 'maximum':
|
Chris@87
|
1384 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
|
Chris@87
|
1385 in enumerate(zip(pad_width, kwargs['stat_length'])):
|
Chris@87
|
1386 newmat = _prepend_max(newmat, pad_before, chunk_before, axis)
|
Chris@87
|
1387 newmat = _append_max(newmat, pad_after, chunk_after, axis)
|
Chris@87
|
1388
|
Chris@87
|
1389 elif mode == 'mean':
|
Chris@87
|
1390 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
|
Chris@87
|
1391 in enumerate(zip(pad_width, kwargs['stat_length'])):
|
Chris@87
|
1392 newmat = _prepend_mean(newmat, pad_before, chunk_before, axis)
|
Chris@87
|
1393 newmat = _append_mean(newmat, pad_after, chunk_after, axis)
|
Chris@87
|
1394
|
Chris@87
|
1395 elif mode == 'median':
|
Chris@87
|
1396 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
|
Chris@87
|
1397 in enumerate(zip(pad_width, kwargs['stat_length'])):
|
Chris@87
|
1398 newmat = _prepend_med(newmat, pad_before, chunk_before, axis)
|
Chris@87
|
1399 newmat = _append_med(newmat, pad_after, chunk_after, axis)
|
Chris@87
|
1400
|
Chris@87
|
1401 elif mode == 'minimum':
|
Chris@87
|
1402 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
|
Chris@87
|
1403 in enumerate(zip(pad_width, kwargs['stat_length'])):
|
Chris@87
|
1404 newmat = _prepend_min(newmat, pad_before, chunk_before, axis)
|
Chris@87
|
1405 newmat = _append_min(newmat, pad_after, chunk_after, axis)
|
Chris@87
|
1406
|
Chris@87
|
1407 elif mode == 'reflect':
|
Chris@87
|
1408 for axis, (pad_before, pad_after) in enumerate(pad_width):
|
Chris@87
|
1409 # Recursive padding along any axis where `pad_amt` is too large
|
Chris@87
|
1410 # for indexing tricks. We can only safely pad the original axis
|
Chris@87
|
1411 # length, to keep the period of the reflections consistent.
|
Chris@87
|
1412 if ((pad_before > 0) or
|
Chris@87
|
1413 (pad_after > 0)) and newmat.shape[axis] == 1:
|
Chris@87
|
1414 # Extending singleton dimension for 'reflect' is legacy
|
Chris@87
|
1415 # behavior; it really should raise an error.
|
Chris@87
|
1416 newmat = _prepend_edge(newmat, pad_before, axis)
|
Chris@87
|
1417 newmat = _append_edge(newmat, pad_after, axis)
|
Chris@87
|
1418 continue
|
Chris@87
|
1419
|
Chris@87
|
1420 method = kwargs['reflect_type']
|
Chris@87
|
1421 safe_pad = newmat.shape[axis] - 1
|
Chris@87
|
1422 while ((pad_before > safe_pad) or (pad_after > safe_pad)):
|
Chris@87
|
1423 offset = 0
|
Chris@87
|
1424 pad_iter_b = min(safe_pad,
|
Chris@87
|
1425 safe_pad * (pad_before // safe_pad))
|
Chris@87
|
1426 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
|
Chris@87
|
1427 newmat = _pad_ref(newmat, (pad_iter_b,
|
Chris@87
|
1428 pad_iter_a), method, axis)
|
Chris@87
|
1429 pad_before -= pad_iter_b
|
Chris@87
|
1430 pad_after -= pad_iter_a
|
Chris@87
|
1431 if pad_iter_b > 0:
|
Chris@87
|
1432 offset += 1
|
Chris@87
|
1433 if pad_iter_a > 0:
|
Chris@87
|
1434 offset += 1
|
Chris@87
|
1435 safe_pad += pad_iter_b + pad_iter_a
|
Chris@87
|
1436 newmat = _pad_ref(newmat, (pad_before, pad_after), method, axis)
|
Chris@87
|
1437
|
Chris@87
|
1438 elif mode == 'symmetric':
|
Chris@87
|
1439 for axis, (pad_before, pad_after) in enumerate(pad_width):
|
Chris@87
|
1440 # Recursive padding along any axis where `pad_amt` is too large
|
Chris@87
|
1441 # for indexing tricks. We can only safely pad the original axis
|
Chris@87
|
1442 # length, to keep the period of the reflections consistent.
|
Chris@87
|
1443 method = kwargs['reflect_type']
|
Chris@87
|
1444 safe_pad = newmat.shape[axis]
|
Chris@87
|
1445 while ((pad_before > safe_pad) or
|
Chris@87
|
1446 (pad_after > safe_pad)):
|
Chris@87
|
1447 pad_iter_b = min(safe_pad,
|
Chris@87
|
1448 safe_pad * (pad_before // safe_pad))
|
Chris@87
|
1449 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
|
Chris@87
|
1450 newmat = _pad_sym(newmat, (pad_iter_b,
|
Chris@87
|
1451 pad_iter_a), method, axis)
|
Chris@87
|
1452 pad_before -= pad_iter_b
|
Chris@87
|
1453 pad_after -= pad_iter_a
|
Chris@87
|
1454 safe_pad += pad_iter_b + pad_iter_a
|
Chris@87
|
1455 newmat = _pad_sym(newmat, (pad_before, pad_after), method, axis)
|
Chris@87
|
1456
|
Chris@87
|
1457 elif mode == 'wrap':
|
Chris@87
|
1458 for axis, (pad_before, pad_after) in enumerate(pad_width):
|
Chris@87
|
1459 # Recursive padding along any axis where `pad_amt` is too large
|
Chris@87
|
1460 # for indexing tricks. We can only safely pad the original axis
|
Chris@87
|
1461 # length, to keep the period of the reflections consistent.
|
Chris@87
|
1462 safe_pad = newmat.shape[axis]
|
Chris@87
|
1463 while ((pad_before > safe_pad) or
|
Chris@87
|
1464 (pad_after > safe_pad)):
|
Chris@87
|
1465 pad_iter_b = min(safe_pad,
|
Chris@87
|
1466 safe_pad * (pad_before // safe_pad))
|
Chris@87
|
1467 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
|
Chris@87
|
1468 newmat = _pad_wrap(newmat, (pad_iter_b, pad_iter_a), axis)
|
Chris@87
|
1469
|
Chris@87
|
1470 pad_before -= pad_iter_b
|
Chris@87
|
1471 pad_after -= pad_iter_a
|
Chris@87
|
1472 safe_pad += pad_iter_b + pad_iter_a
|
Chris@87
|
1473 newmat = _pad_wrap(newmat, (pad_before, pad_after), axis)
|
Chris@87
|
1474
|
Chris@87
|
1475 return newmat
|