Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/numpy/lib/arraypad.py : 8%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2The arraypad module contains a group of functions to pad values onto the edges
3of an n-dimensional array.
5"""
6import numpy as np
7from numpy.core.overrides import array_function_dispatch
8from numpy.lib.index_tricks import ndindex
11__all__ = ['pad']
14###############################################################################
15# Private utility functions.
18def _round_if_needed(arr, dtype):
19 """
20 Rounds arr inplace if destination dtype is integer.
22 Parameters
23 ----------
24 arr : ndarray
25 Input array.
26 dtype : dtype
27 The dtype of the destination array.
28 """
29 if np.issubdtype(dtype, np.integer):
30 arr.round(out=arr)
33def _slice_at_axis(sl, axis):
34 """
35 Construct tuple of slices to slice an array in the given dimension.
37 Parameters
38 ----------
39 sl : slice
40 The slice for the given dimension.
41 axis : int
42 The axis to which `sl` is applied. All other dimensions are left
43 "unsliced".
45 Returns
46 -------
47 sl : tuple of slices
48 A tuple with slices matching `shape` in length.
50 Examples
51 --------
52 >>> _slice_at_axis(slice(None, 3, -1), 1)
53 (slice(None, None, None), slice(None, 3, -1), (...,))
54 """
55 return (slice(None),) * axis + (sl,) + (...,)
58def _view_roi(array, original_area_slice, axis):
59 """
60 Get a view of the current region of interest during iterative padding.
62 When padding multiple dimensions iteratively corner values are
63 unnecessarily overwritten multiple times. This function reduces the
64 working area for the first dimensions so that corners are excluded.
66 Parameters
67 ----------
68 array : ndarray
69 The array with the region of interest.
70 original_area_slice : tuple of slices
71 Denotes the area with original values of the unpadded array.
72 axis : int
73 The currently padded dimension assuming that `axis` is padded before
74 `axis` + 1.
76 Returns
77 -------
78 roi : ndarray
79 The region of interest of the original `array`.
80 """
81 axis += 1
82 sl = (slice(None),) * axis + original_area_slice[axis:]
83 return array[sl]
86def _pad_simple(array, pad_width, fill_value=None):
87 """
88 Pad array on all sides with either a single value or undefined values.
90 Parameters
91 ----------
92 array : ndarray
93 Array to grow.
94 pad_width : sequence of tuple[int, int]
95 Pad width on both sides for each dimension in `arr`.
96 fill_value : scalar, optional
97 If provided the padded area is filled with this value, otherwise
98 the pad area left undefined.
100 Returns
101 -------
102 padded : ndarray
103 The padded array with the same dtype as`array`. Its order will default
104 to C-style if `array` is not F-contiguous.
105 original_area_slice : tuple
106 A tuple of slices pointing to the area of the original array.
107 """
108 # Allocate grown array
109 new_shape = tuple(
110 left + size + right
111 for size, (left, right) in zip(array.shape, pad_width)
112 )
113 order = 'F' if array.flags.fnc else 'C' # Fortran and not also C-order
114 padded = np.empty(new_shape, dtype=array.dtype, order=order)
116 if fill_value is not None:
117 padded.fill(fill_value)
119 # Copy old array into correct space
120 original_area_slice = tuple(
121 slice(left, left + size)
122 for size, (left, right) in zip(array.shape, pad_width)
123 )
124 padded[original_area_slice] = array
126 return padded, original_area_slice
129def _set_pad_area(padded, axis, width_pair, value_pair):
130 """
131 Set empty-padded area in given dimension.
133 Parameters
134 ----------
135 padded : ndarray
136 Array with the pad area which is modified inplace.
137 axis : int
138 Dimension with the pad area to set.
139 width_pair : (int, int)
140 Pair of widths that mark the pad area on both sides in the given
141 dimension.
142 value_pair : tuple of scalars or ndarrays
143 Values inserted into the pad area on each side. It must match or be
144 broadcastable to the shape of `arr`.
145 """
146 left_slice = _slice_at_axis(slice(None, width_pair[0]), axis)
147 padded[left_slice] = value_pair[0]
149 right_slice = _slice_at_axis(
150 slice(padded.shape[axis] - width_pair[1], None), axis)
151 padded[right_slice] = value_pair[1]
154def _get_edges(padded, axis, width_pair):
155 """
156 Retrieve edge values from empty-padded array in given dimension.
158 Parameters
159 ----------
160 padded : ndarray
161 Empty-padded array.
162 axis : int
163 Dimension in which the edges are considered.
164 width_pair : (int, int)
165 Pair of widths that mark the pad area on both sides in the given
166 dimension.
168 Returns
169 -------
170 left_edge, right_edge : ndarray
171 Edge values of the valid area in `padded` in the given dimension. Its
172 shape will always match `padded` except for the dimension given by
173 `axis` which will have a length of 1.
174 """
175 left_index = width_pair[0]
176 left_slice = _slice_at_axis(slice(left_index, left_index + 1), axis)
177 left_edge = padded[left_slice]
179 right_index = padded.shape[axis] - width_pair[1]
180 right_slice = _slice_at_axis(slice(right_index - 1, right_index), axis)
181 right_edge = padded[right_slice]
183 return left_edge, right_edge
186def _get_linear_ramps(padded, axis, width_pair, end_value_pair):
187 """
188 Construct linear ramps for empty-padded array in given dimension.
190 Parameters
191 ----------
192 padded : ndarray
193 Empty-padded array.
194 axis : int
195 Dimension in which the ramps are constructed.
196 width_pair : (int, int)
197 Pair of widths that mark the pad area on both sides in the given
198 dimension.
199 end_value_pair : (scalar, scalar)
200 End values for the linear ramps which form the edge of the fully padded
201 array. These values are included in the linear ramps.
203 Returns
204 -------
205 left_ramp, right_ramp : ndarray
206 Linear ramps to set on both sides of `padded`.
207 """
208 edge_pair = _get_edges(padded, axis, width_pair)
210 left_ramp = np.linspace(
211 start=end_value_pair[0],
212 stop=edge_pair[0].squeeze(axis), # Dimensions is replaced by linspace
213 num=width_pair[0],
214 endpoint=False,
215 dtype=padded.dtype,
216 axis=axis,
217 )
219 right_ramp = np.linspace(
220 start=end_value_pair[1],
221 stop=edge_pair[1].squeeze(axis), # Dimension is replaced by linspace
222 num=width_pair[1],
223 endpoint=False,
224 dtype=padded.dtype,
225 axis=axis,
226 )
227 # Reverse linear space in appropriate dimension
228 right_ramp = right_ramp[_slice_at_axis(slice(None, None, -1), axis)]
230 return left_ramp, right_ramp
233def _get_stats(padded, axis, width_pair, length_pair, stat_func):
234 """
235 Calculate statistic for the empty-padded array in given dimension.
237 Parameters
238 ----------
239 padded : ndarray
240 Empty-padded array.
241 axis : int
242 Dimension in which the statistic is calculated.
243 width_pair : (int, int)
244 Pair of widths that mark the pad area on both sides in the given
245 dimension.
246 length_pair : 2-element sequence of None or int
247 Gives the number of values in valid area from each side that is
248 taken into account when calculating the statistic. If None the entire
249 valid area in `padded` is considered.
250 stat_func : function
251 Function to compute statistic. The expected signature is
252 ``stat_func(x: ndarray, axis: int, keepdims: bool) -> ndarray``.
254 Returns
255 -------
256 left_stat, right_stat : ndarray
257 Calculated statistic for both sides of `padded`.
258 """
259 # Calculate indices of the edges of the area with original values
260 left_index = width_pair[0]
261 right_index = padded.shape[axis] - width_pair[1]
262 # as well as its length
263 max_length = right_index - left_index
265 # Limit stat_lengths to max_length
266 left_length, right_length = length_pair
267 if left_length is None or max_length < left_length:
268 left_length = max_length
269 if right_length is None or max_length < right_length:
270 right_length = max_length
272 if (left_length == 0 or right_length == 0) \
273 and stat_func in {np.amax, np.amin}:
274 # amax and amin can't operate on an empty array,
275 # raise a more descriptive warning here instead of the default one
276 raise ValueError("stat_length of 0 yields no value for padding")
278 # Calculate statistic for the left side
279 left_slice = _slice_at_axis(
280 slice(left_index, left_index + left_length), axis)
281 left_chunk = padded[left_slice]
282 left_stat = stat_func(left_chunk, axis=axis, keepdims=True)
283 _round_if_needed(left_stat, padded.dtype)
285 if left_length == right_length == max_length:
286 # return early as right_stat must be identical to left_stat
287 return left_stat, left_stat
289 # Calculate statistic for the right side
290 right_slice = _slice_at_axis(
291 slice(right_index - right_length, right_index), axis)
292 right_chunk = padded[right_slice]
293 right_stat = stat_func(right_chunk, axis=axis, keepdims=True)
294 _round_if_needed(right_stat, padded.dtype)
296 return left_stat, right_stat
299def _set_reflect_both(padded, axis, width_pair, method, include_edge=False):
300 """
301 Pad `axis` of `arr` with reflection.
303 Parameters
304 ----------
305 padded : ndarray
306 Input array of arbitrary shape.
307 axis : int
308 Axis along which to pad `arr`.
309 width_pair : (int, int)
310 Pair of widths that mark the pad area on both sides in the given
311 dimension.
312 method : str
313 Controls method of reflection; options are 'even' or 'odd'.
314 include_edge : bool
315 If true, edge value is included in reflection, otherwise the edge
316 value forms the symmetric axis to the reflection.
318 Returns
319 -------
320 pad_amt : tuple of ints, length 2
321 New index positions of padding to do along the `axis`. If these are
322 both 0, padding is done in this dimension.
323 """
324 left_pad, right_pad = width_pair
325 old_length = padded.shape[axis] - right_pad - left_pad
327 if include_edge:
328 # Edge is included, we need to offset the pad amount by 1
329 edge_offset = 1
330 else:
331 edge_offset = 0 # Edge is not included, no need to offset pad amount
332 old_length -= 1 # but must be omitted from the chunk
334 if left_pad > 0:
335 # Pad with reflected values on left side:
336 # First limit chunk size which can't be larger than pad area
337 chunk_length = min(old_length, left_pad)
338 # Slice right to left, stop on or next to edge, start relative to stop
339 stop = left_pad - edge_offset
340 start = stop + chunk_length
341 left_slice = _slice_at_axis(slice(start, stop, -1), axis)
342 left_chunk = padded[left_slice]
344 if method == "odd":
345 # Negate chunk and align with edge
346 edge_slice = _slice_at_axis(slice(left_pad, left_pad + 1), axis)
347 left_chunk = 2 * padded[edge_slice] - left_chunk
349 # Insert chunk into padded area
350 start = left_pad - chunk_length
351 stop = left_pad
352 pad_area = _slice_at_axis(slice(start, stop), axis)
353 padded[pad_area] = left_chunk
354 # Adjust pointer to left edge for next iteration
355 left_pad -= chunk_length
357 if right_pad > 0:
358 # Pad with reflected values on right side:
359 # First limit chunk size which can't be larger than pad area
360 chunk_length = min(old_length, right_pad)
361 # Slice right to left, start on or next to edge, stop relative to start
362 start = -right_pad + edge_offset - 2
363 stop = start - chunk_length
364 right_slice = _slice_at_axis(slice(start, stop, -1), axis)
365 right_chunk = padded[right_slice]
367 if method == "odd":
368 # Negate chunk and align with edge
369 edge_slice = _slice_at_axis(
370 slice(-right_pad - 1, -right_pad), axis)
371 right_chunk = 2 * padded[edge_slice] - right_chunk
373 # Insert chunk into padded area
374 start = padded.shape[axis] - right_pad
375 stop = start + chunk_length
376 pad_area = _slice_at_axis(slice(start, stop), axis)
377 padded[pad_area] = right_chunk
378 # Adjust pointer to right edge for next iteration
379 right_pad -= chunk_length
381 return left_pad, right_pad
384def _set_wrap_both(padded, axis, width_pair):
385 """
386 Pad `axis` of `arr` with wrapped values.
388 Parameters
389 ----------
390 padded : ndarray
391 Input array of arbitrary shape.
392 axis : int
393 Axis along which to pad `arr`.
394 width_pair : (int, int)
395 Pair of widths that mark the pad area on both sides in the given
396 dimension.
398 Returns
399 -------
400 pad_amt : tuple of ints, length 2
401 New index positions of padding to do along the `axis`. If these are
402 both 0, padding is done in this dimension.
403 """
404 left_pad, right_pad = width_pair
405 period = padded.shape[axis] - right_pad - left_pad
407 # If the current dimension of `arr` doesn't contain enough valid values
408 # (not part of the undefined pad area) we need to pad multiple times.
409 # Each time the pad area shrinks on both sides which is communicated with
410 # these variables.
411 new_left_pad = 0
412 new_right_pad = 0
414 if left_pad > 0:
415 # Pad with wrapped values on left side
416 # First slice chunk from right side of the non-pad area.
417 # Use min(period, left_pad) to ensure that chunk is not larger than
418 # pad area
419 right_slice = _slice_at_axis(
420 slice(-right_pad - min(period, left_pad),
421 -right_pad if right_pad != 0 else None),
422 axis
423 )
424 right_chunk = padded[right_slice]
426 if left_pad > period:
427 # Chunk is smaller than pad area
428 pad_area = _slice_at_axis(slice(left_pad - period, left_pad), axis)
429 new_left_pad = left_pad - period
430 else:
431 # Chunk matches pad area
432 pad_area = _slice_at_axis(slice(None, left_pad), axis)
433 padded[pad_area] = right_chunk
435 if right_pad > 0:
436 # Pad with wrapped values on right side
437 # First slice chunk from left side of the non-pad area.
438 # Use min(period, right_pad) to ensure that chunk is not larger than
439 # pad area
440 left_slice = _slice_at_axis(
441 slice(left_pad, left_pad + min(period, right_pad),), axis)
442 left_chunk = padded[left_slice]
444 if right_pad > period:
445 # Chunk is smaller than pad area
446 pad_area = _slice_at_axis(
447 slice(-right_pad, -right_pad + period), axis)
448 new_right_pad = right_pad - period
449 else:
450 # Chunk matches pad area
451 pad_area = _slice_at_axis(slice(-right_pad, None), axis)
452 padded[pad_area] = left_chunk
454 return new_left_pad, new_right_pad
457def _as_pairs(x, ndim, as_index=False):
458 """
459 Broadcast `x` to an array with the shape (`ndim`, 2).
461 A helper function for `pad` that prepares and validates arguments like
462 `pad_width` for iteration in pairs.
464 Parameters
465 ----------
466 x : {None, scalar, array-like}
467 The object to broadcast to the shape (`ndim`, 2).
468 ndim : int
469 Number of pairs the broadcasted `x` will have.
470 as_index : bool, optional
471 If `x` is not None, try to round each element of `x` to an integer
472 (dtype `np.intp`) and ensure every element is positive.
474 Returns
475 -------
476 pairs : nested iterables, shape (`ndim`, 2)
477 The broadcasted version of `x`.
479 Raises
480 ------
481 ValueError
482 If `as_index` is True and `x` contains negative elements.
483 Or if `x` is not broadcastable to the shape (`ndim`, 2).
484 """
485 if x is None:
486 # Pass through None as a special case, otherwise np.round(x) fails
487 # with an AttributeError
488 return ((None, None),) * ndim
490 x = np.array(x)
491 if as_index:
492 x = np.round(x).astype(np.intp, copy=False)
494 if x.ndim < 3:
495 # Optimization: Possibly use faster paths for cases where `x` has
496 # only 1 or 2 elements. `np.broadcast_to` could handle these as well
497 # but is currently slower
499 if x.size == 1:
500 # x was supplied as a single value
501 x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2
502 if as_index and x < 0:
503 raise ValueError("index can't contain negative values")
504 return ((x[0], x[0]),) * ndim
506 if x.size == 2 and x.shape != (2, 1):
507 # x was supplied with a single value for each side
508 # but except case when each dimension has a single value
509 # which should be broadcasted to a pair,
510 # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]]
511 x = x.ravel() # Ensure x[0], x[1] works
512 if as_index and (x[0] < 0 or x[1] < 0):
513 raise ValueError("index can't contain negative values")
514 return ((x[0], x[1]),) * ndim
516 if as_index and x.min() < 0:
517 raise ValueError("index can't contain negative values")
519 # Converting the array with `tolist` seems to improve performance
520 # when iterating and indexing the result (see usage in `pad`)
521 return np.broadcast_to(x, (ndim, 2)).tolist()
524def _pad_dispatcher(array, pad_width, mode=None, **kwargs):
525 return (array,)
528###############################################################################
529# Public functions
532@array_function_dispatch(_pad_dispatcher, module='numpy')
533def pad(array, pad_width, mode='constant', **kwargs):
534 """
535 Pad an array.
537 Parameters
538 ----------
539 array : array_like of rank N
540 The array to pad.
541 pad_width : {sequence, array_like, int}
542 Number of values padded to the edges of each axis.
543 ((before_1, after_1), ... (before_N, after_N)) unique pad widths
544 for each axis.
545 ((before, after),) yields same before and after pad for each axis.
546 (pad,) or int is a shortcut for before = after = pad width for all
547 axes.
548 mode : str or function, optional
549 One of the following string values or a user supplied function.
551 'constant' (default)
552 Pads with a constant value.
553 'edge'
554 Pads with the edge values of array.
555 'linear_ramp'
556 Pads with the linear ramp between end_value and the
557 array edge value.
558 'maximum'
559 Pads with the maximum value of all or part of the
560 vector along each axis.
561 'mean'
562 Pads with the mean value of all or part of the
563 vector along each axis.
564 'median'
565 Pads with the median value of all or part of the
566 vector along each axis.
567 'minimum'
568 Pads with the minimum value of all or part of the
569 vector along each axis.
570 'reflect'
571 Pads with the reflection of the vector mirrored on
572 the first and last values of the vector along each
573 axis.
574 'symmetric'
575 Pads with the reflection of the vector mirrored
576 along the edge of the array.
577 'wrap'
578 Pads with the wrap of the vector along the axis.
579 The first values are used to pad the end and the
580 end values are used to pad the beginning.
581 'empty'
582 Pads with undefined values.
584 .. versionadded:: 1.17
586 <function>
587 Padding function, see Notes.
588 stat_length : sequence or int, optional
589 Used in 'maximum', 'mean', 'median', and 'minimum'. Number of
590 values at edge of each axis used to calculate the statistic value.
592 ((before_1, after_1), ... (before_N, after_N)) unique statistic
593 lengths for each axis.
595 ((before, after),) yields same before and after statistic lengths
596 for each axis.
598 (stat_length,) or int is a shortcut for before = after = statistic
599 length for all axes.
601 Default is ``None``, to use the entire axis.
602 constant_values : sequence or scalar, optional
603 Used in 'constant'. The values to set the padded values for each
604 axis.
606 ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants
607 for each axis.
609 ``((before, after),)`` yields same before and after constants for each
610 axis.
612 ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
613 all axes.
615 Default is 0.
616 end_values : sequence or scalar, optional
617 Used in 'linear_ramp'. The values used for the ending value of the
618 linear_ramp and that will form the edge of the padded array.
620 ``((before_1, after_1), ... (before_N, after_N))`` unique end values
621 for each axis.
623 ``((before, after),)`` yields same before and after end values for each
624 axis.
626 ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
627 all axes.
629 Default is 0.
630 reflect_type : {'even', 'odd'}, optional
631 Used in 'reflect', and 'symmetric'. The 'even' style is the
632 default with an unaltered reflection around the edge value. For
633 the 'odd' style, the extended part of the array is created by
634 subtracting the reflected values from two times the edge value.
636 Returns
637 -------
638 pad : ndarray
639 Padded array of rank equal to `array` with shape increased
640 according to `pad_width`.
642 Notes
643 -----
644 .. versionadded:: 1.7.0
646 For an array with rank greater than 1, some of the padding of later
647 axes is calculated from padding of previous axes. This is easiest to
648 think about with a rank 2 array where the corners of the padded array
649 are calculated by using padded values from the first axis.
651 The padding function, if used, should modify a rank 1 array in-place. It
652 has the following signature::
654 padding_func(vector, iaxis_pad_width, iaxis, kwargs)
656 where
658 vector : ndarray
659 A rank 1 array already padded with zeros. Padded values are
660 vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:].
661 iaxis_pad_width : tuple
662 A 2-tuple of ints, iaxis_pad_width[0] represents the number of
663 values padded at the beginning of vector where
664 iaxis_pad_width[1] represents the number of values padded at
665 the end of vector.
666 iaxis : int
667 The axis currently being calculated.
668 kwargs : dict
669 Any keyword arguments the function requires.
671 Examples
672 --------
673 >>> a = [1, 2, 3, 4, 5]
674 >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6))
675 array([4, 4, 1, ..., 6, 6, 6])
677 >>> np.pad(a, (2, 3), 'edge')
678 array([1, 1, 1, ..., 5, 5, 5])
680 >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
681 array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4])
683 >>> np.pad(a, (2,), 'maximum')
684 array([5, 5, 1, 2, 3, 4, 5, 5, 5])
686 >>> np.pad(a, (2,), 'mean')
687 array([3, 3, 1, 2, 3, 4, 5, 3, 3])
689 >>> np.pad(a, (2,), 'median')
690 array([3, 3, 1, 2, 3, 4, 5, 3, 3])
692 >>> a = [[1, 2], [3, 4]]
693 >>> np.pad(a, ((3, 2), (2, 3)), 'minimum')
694 array([[1, 1, 1, 2, 1, 1, 1],
695 [1, 1, 1, 2, 1, 1, 1],
696 [1, 1, 1, 2, 1, 1, 1],
697 [1, 1, 1, 2, 1, 1, 1],
698 [3, 3, 3, 4, 3, 3, 3],
699 [1, 1, 1, 2, 1, 1, 1],
700 [1, 1, 1, 2, 1, 1, 1]])
702 >>> a = [1, 2, 3, 4, 5]
703 >>> np.pad(a, (2, 3), 'reflect')
704 array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])
706 >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd')
707 array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
709 >>> np.pad(a, (2, 3), 'symmetric')
710 array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])
712 >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd')
713 array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])
715 >>> np.pad(a, (2, 3), 'wrap')
716 array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])
718 >>> def pad_with(vector, pad_width, iaxis, kwargs):
719 ... pad_value = kwargs.get('padder', 10)
720 ... vector[:pad_width[0]] = pad_value
721 ... vector[-pad_width[1]:] = pad_value
722 >>> a = np.arange(6)
723 >>> a = a.reshape((2, 3))
724 >>> np.pad(a, 2, pad_with)
725 array([[10, 10, 10, 10, 10, 10, 10],
726 [10, 10, 10, 10, 10, 10, 10],
727 [10, 10, 0, 1, 2, 10, 10],
728 [10, 10, 3, 4, 5, 10, 10],
729 [10, 10, 10, 10, 10, 10, 10],
730 [10, 10, 10, 10, 10, 10, 10]])
731 >>> np.pad(a, 2, pad_with, padder=100)
732 array([[100, 100, 100, 100, 100, 100, 100],
733 [100, 100, 100, 100, 100, 100, 100],
734 [100, 100, 0, 1, 2, 100, 100],
735 [100, 100, 3, 4, 5, 100, 100],
736 [100, 100, 100, 100, 100, 100, 100],
737 [100, 100, 100, 100, 100, 100, 100]])
738 """
739 array = np.asarray(array)
740 pad_width = np.asarray(pad_width)
742 if not pad_width.dtype.kind == 'i':
743 raise TypeError('`pad_width` must be of integral type.')
745 # Broadcast to shape (array.ndim, 2)
746 pad_width = _as_pairs(pad_width, array.ndim, as_index=True)
748 if callable(mode):
749 # Old behavior: Use user-supplied function with np.apply_along_axis
750 function = mode
751 # Create a new zero padded array
752 padded, _ = _pad_simple(array, pad_width, fill_value=0)
753 # And apply along each axis
755 for axis in range(padded.ndim):
756 # Iterate using ndindex as in apply_along_axis, but assuming that
757 # function operates inplace on the padded array.
759 # view with the iteration axis at the end
760 view = np.moveaxis(padded, axis, -1)
762 # compute indices for the iteration axes, and append a trailing
763 # ellipsis to prevent 0d arrays decaying to scalars (gh-8642)
764 inds = ndindex(view.shape[:-1])
765 inds = (ind + (Ellipsis,) for ind in inds)
766 for ind in inds:
767 function(view[ind], pad_width[axis], axis, kwargs)
769 return padded
771 # Make sure that no unsupported keywords were passed for the current mode
772 allowed_kwargs = {
773 'empty': [], 'edge': [], 'wrap': [],
774 'constant': ['constant_values'],
775 'linear_ramp': ['end_values'],
776 'maximum': ['stat_length'],
777 'mean': ['stat_length'],
778 'median': ['stat_length'],
779 'minimum': ['stat_length'],
780 'reflect': ['reflect_type'],
781 'symmetric': ['reflect_type'],
782 }
783 try:
784 unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode])
785 except KeyError:
786 raise ValueError("mode '{}' is not supported".format(mode))
787 if unsupported_kwargs:
788 raise ValueError("unsupported keyword arguments for mode '{}': {}"
789 .format(mode, unsupported_kwargs))
791 stat_functions = {"maximum": np.amax, "minimum": np.amin,
792 "mean": np.mean, "median": np.median}
794 # Create array with final shape and original values
795 # (padded area is undefined)
796 padded, original_area_slice = _pad_simple(array, pad_width)
797 # And prepare iteration over all dimensions
798 # (zipping may be more readable than using enumerate)
799 axes = range(padded.ndim)
801 if mode == "constant":
802 values = kwargs.get("constant_values", 0)
803 values = _as_pairs(values, padded.ndim)
804 for axis, width_pair, value_pair in zip(axes, pad_width, values):
805 roi = _view_roi(padded, original_area_slice, axis)
806 _set_pad_area(roi, axis, width_pair, value_pair)
808 elif mode == "empty":
809 pass # Do nothing as _pad_simple already returned the correct result
811 elif array.size == 0:
812 # Only modes "constant" and "empty" can extend empty axes, all other
813 # modes depend on `array` not being empty
814 # -> ensure every empty axis is only "padded with 0"
815 for axis, width_pair in zip(axes, pad_width):
816 if array.shape[axis] == 0 and any(width_pair):
817 raise ValueError(
818 "can't extend empty axis {} using modes other than "
819 "'constant' or 'empty'".format(axis)
820 )
821 # passed, don't need to do anything more as _pad_simple already
822 # returned the correct result
824 elif mode == "edge":
825 for axis, width_pair in zip(axes, pad_width):
826 roi = _view_roi(padded, original_area_slice, axis)
827 edge_pair = _get_edges(roi, axis, width_pair)
828 _set_pad_area(roi, axis, width_pair, edge_pair)
830 elif mode == "linear_ramp":
831 end_values = kwargs.get("end_values", 0)
832 end_values = _as_pairs(end_values, padded.ndim)
833 for axis, width_pair, value_pair in zip(axes, pad_width, end_values):
834 roi = _view_roi(padded, original_area_slice, axis)
835 ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair)
836 _set_pad_area(roi, axis, width_pair, ramp_pair)
838 elif mode in stat_functions:
839 func = stat_functions[mode]
840 length = kwargs.get("stat_length", None)
841 length = _as_pairs(length, padded.ndim, as_index=True)
842 for axis, width_pair, length_pair in zip(axes, pad_width, length):
843 roi = _view_roi(padded, original_area_slice, axis)
844 stat_pair = _get_stats(roi, axis, width_pair, length_pair, func)
845 _set_pad_area(roi, axis, width_pair, stat_pair)
847 elif mode in {"reflect", "symmetric"}:
848 method = kwargs.get("reflect_type", "even")
849 include_edge = True if mode == "symmetric" else False
850 for axis, (left_index, right_index) in zip(axes, pad_width):
851 if array.shape[axis] == 1 and (left_index > 0 or right_index > 0):
852 # Extending singleton dimension for 'reflect' is legacy
853 # behavior; it really should raise an error.
854 edge_pair = _get_edges(padded, axis, (left_index, right_index))
855 _set_pad_area(
856 padded, axis, (left_index, right_index), edge_pair)
857 continue
859 roi = _view_roi(padded, original_area_slice, axis)
860 while left_index > 0 or right_index > 0:
861 # Iteratively pad until dimension is filled with reflected
862 # values. This is necessary if the pad area is larger than
863 # the length of the original values in the current dimension.
864 left_index, right_index = _set_reflect_both(
865 roi, axis, (left_index, right_index),
866 method, include_edge
867 )
869 elif mode == "wrap":
870 for axis, (left_index, right_index) in zip(axes, pad_width):
871 roi = _view_roi(padded, original_area_slice, axis)
872 while left_index > 0 or right_index > 0:
873 # Iteratively pad until dimension is filled with wrapped
874 # values. This is necessary if the pad area is larger than
875 # the length of the original values in the current dimension.
876 left_index, right_index = _set_wrap_both(
877 roi, axis, (left_index, right_index))
879 return padded