123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876 |
- """
- The arraypad module contains a group of functions to pad values onto the edges
- of an n-dimensional array.
- """
- import numpy as np
- from numpy.core.overrides import array_function_dispatch
- from numpy.lib.index_tricks import ndindex
- __all__ = ['pad']
- ###############################################################################
- # Private utility functions.
- def _round_if_needed(arr, dtype):
- """
- Rounds arr inplace if destination dtype is integer.
- Parameters
- ----------
- arr : ndarray
- Input array.
- dtype : dtype
- The dtype of the destination array.
- """
- if np.issubdtype(dtype, np.integer):
- arr.round(out=arr)
- def _slice_at_axis(sl, axis):
- """
- Construct tuple of slices to slice an array in the given dimension.
- Parameters
- ----------
- sl : slice
- The slice for the given dimension.
- axis : int
- The axis to which `sl` is applied. All other dimensions are left
- "unsliced".
- Returns
- -------
- sl : tuple of slices
- A tuple with slices matching `shape` in length.
- Examples
- --------
- >>> _slice_at_axis(slice(None, 3, -1), 1)
- (slice(None, None, None), slice(None, 3, -1), (...,))
- """
- return (slice(None),) * axis + (sl,) + (...,)
- def _view_roi(array, original_area_slice, axis):
- """
- Get a view of the current region of interest during iterative padding.
- When padding multiple dimensions iteratively corner values are
- unnecessarily overwritten multiple times. This function reduces the
- working area for the first dimensions so that corners are excluded.
- Parameters
- ----------
- array : ndarray
- The array with the region of interest.
- original_area_slice : tuple of slices
- Denotes the area with original values of the unpadded array.
- axis : int
- The currently padded dimension assuming that `axis` is padded before
- `axis` + 1.
- Returns
- -------
- roi : ndarray
- The region of interest of the original `array`.
- """
- axis += 1
- sl = (slice(None),) * axis + original_area_slice[axis:]
- return array[sl]
- def _pad_simple(array, pad_width, fill_value=None):
- """
- Pad array on all sides with either a single value or undefined values.
- Parameters
- ----------
- array : ndarray
- Array to grow.
- pad_width : sequence of tuple[int, int]
- Pad width on both sides for each dimension in `arr`.
- fill_value : scalar, optional
- If provided the padded area is filled with this value, otherwise
- the pad area left undefined.
- Returns
- -------
- padded : ndarray
- The padded array with the same dtype as`array`. Its order will default
- to C-style if `array` is not F-contiguous.
- original_area_slice : tuple
- A tuple of slices pointing to the area of the original array.
- """
- # Allocate grown array
- new_shape = tuple(
- left + size + right
- for size, (left, right) in zip(array.shape, pad_width)
- )
- order = 'F' if array.flags.fnc else 'C' # Fortran and not also C-order
- padded = np.empty(new_shape, dtype=array.dtype, order=order)
- if fill_value is not None:
- padded.fill(fill_value)
- # Copy old array into correct space
- original_area_slice = tuple(
- slice(left, left + size)
- for size, (left, right) in zip(array.shape, pad_width)
- )
- padded[original_area_slice] = array
- return padded, original_area_slice
- def _set_pad_area(padded, axis, width_pair, value_pair):
- """
- Set empty-padded area in given dimension.
- Parameters
- ----------
- padded : ndarray
- Array with the pad area which is modified inplace.
- axis : int
- Dimension with the pad area to set.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- value_pair : tuple of scalars or ndarrays
- Values inserted into the pad area on each side. It must match or be
- broadcastable to the shape of `arr`.
- """
- left_slice = _slice_at_axis(slice(None, width_pair[0]), axis)
- padded[left_slice] = value_pair[0]
- right_slice = _slice_at_axis(
- slice(padded.shape[axis] - width_pair[1], None), axis)
- padded[right_slice] = value_pair[1]
- def _get_edges(padded, axis, width_pair):
- """
- Retrieve edge values from empty-padded array in given dimension.
- Parameters
- ----------
- padded : ndarray
- Empty-padded array.
- axis : int
- Dimension in which the edges are considered.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- Returns
- -------
- left_edge, right_edge : ndarray
- Edge values of the valid area in `padded` in the given dimension. Its
- shape will always match `padded` except for the dimension given by
- `axis` which will have a length of 1.
- """
- left_index = width_pair[0]
- left_slice = _slice_at_axis(slice(left_index, left_index + 1), axis)
- left_edge = padded[left_slice]
- right_index = padded.shape[axis] - width_pair[1]
- right_slice = _slice_at_axis(slice(right_index - 1, right_index), axis)
- right_edge = padded[right_slice]
- return left_edge, right_edge
- def _get_linear_ramps(padded, axis, width_pair, end_value_pair):
- """
- Construct linear ramps for empty-padded array in given dimension.
- Parameters
- ----------
- padded : ndarray
- Empty-padded array.
- axis : int
- Dimension in which the ramps are constructed.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- end_value_pair : (scalar, scalar)
- End values for the linear ramps which form the edge of the fully padded
- array. These values are included in the linear ramps.
- Returns
- -------
- left_ramp, right_ramp : ndarray
- Linear ramps to set on both sides of `padded`.
- """
- edge_pair = _get_edges(padded, axis, width_pair)
- left_ramp, right_ramp = (
- np.linspace(
- start=end_value,
- stop=edge.squeeze(axis), # Dimension is replaced by linspace
- num=width,
- endpoint=False,
- dtype=padded.dtype,
- axis=axis
- )
- for end_value, edge, width in zip(
- end_value_pair, edge_pair, width_pair
- )
- )
-
- # Reverse linear space in appropriate dimension
- right_ramp = right_ramp[_slice_at_axis(slice(None, None, -1), axis)]
- return left_ramp, right_ramp
- def _get_stats(padded, axis, width_pair, length_pair, stat_func):
- """
- Calculate statistic for the empty-padded array in given dimension.
- Parameters
- ----------
- padded : ndarray
- Empty-padded array.
- axis : int
- Dimension in which the statistic is calculated.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- length_pair : 2-element sequence of None or int
- Gives the number of values in valid area from each side that is
- taken into account when calculating the statistic. If None the entire
- valid area in `padded` is considered.
- stat_func : function
- Function to compute statistic. The expected signature is
- ``stat_func(x: ndarray, axis: int, keepdims: bool) -> ndarray``.
- Returns
- -------
- left_stat, right_stat : ndarray
- Calculated statistic for both sides of `padded`.
- """
- # Calculate indices of the edges of the area with original values
- left_index = width_pair[0]
- right_index = padded.shape[axis] - width_pair[1]
- # as well as its length
- max_length = right_index - left_index
- # Limit stat_lengths to max_length
- left_length, right_length = length_pair
- if left_length is None or max_length < left_length:
- left_length = max_length
- if right_length is None or max_length < right_length:
- right_length = max_length
- if (left_length == 0 or right_length == 0) \
- and stat_func in {np.amax, np.amin}:
- # amax and amin can't operate on an empty array,
- # raise a more descriptive warning here instead of the default one
- raise ValueError("stat_length of 0 yields no value for padding")
- # Calculate statistic for the left side
- left_slice = _slice_at_axis(
- slice(left_index, left_index + left_length), axis)
- left_chunk = padded[left_slice]
- left_stat = stat_func(left_chunk, axis=axis, keepdims=True)
- _round_if_needed(left_stat, padded.dtype)
- if left_length == right_length == max_length:
- # return early as right_stat must be identical to left_stat
- return left_stat, left_stat
- # Calculate statistic for the right side
- right_slice = _slice_at_axis(
- slice(right_index - right_length, right_index), axis)
- right_chunk = padded[right_slice]
- right_stat = stat_func(right_chunk, axis=axis, keepdims=True)
- _round_if_needed(right_stat, padded.dtype)
- return left_stat, right_stat
- def _set_reflect_both(padded, axis, width_pair, method, include_edge=False):
- """
- Pad `axis` of `arr` with reflection.
- Parameters
- ----------
- padded : ndarray
- Input array of arbitrary shape.
- axis : int
- Axis along which to pad `arr`.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- method : str
- Controls method of reflection; options are 'even' or 'odd'.
- include_edge : bool
- If true, edge value is included in reflection, otherwise the edge
- value forms the symmetric axis to the reflection.
- Returns
- -------
- pad_amt : tuple of ints, length 2
- New index positions of padding to do along the `axis`. If these are
- both 0, padding is done in this dimension.
- """
- left_pad, right_pad = width_pair
- old_length = padded.shape[axis] - right_pad - left_pad
- if include_edge:
- # Edge is included, we need to offset the pad amount by 1
- edge_offset = 1
- else:
- edge_offset = 0 # Edge is not included, no need to offset pad amount
- old_length -= 1 # but must be omitted from the chunk
- if left_pad > 0:
- # Pad with reflected values on left side:
- # First limit chunk size which can't be larger than pad area
- chunk_length = min(old_length, left_pad)
- # Slice right to left, stop on or next to edge, start relative to stop
- stop = left_pad - edge_offset
- start = stop + chunk_length
- left_slice = _slice_at_axis(slice(start, stop, -1), axis)
- left_chunk = padded[left_slice]
- if method == "odd":
- # Negate chunk and align with edge
- edge_slice = _slice_at_axis(slice(left_pad, left_pad + 1), axis)
- left_chunk = 2 * padded[edge_slice] - left_chunk
- # Insert chunk into padded area
- start = left_pad - chunk_length
- stop = left_pad
- pad_area = _slice_at_axis(slice(start, stop), axis)
- padded[pad_area] = left_chunk
- # Adjust pointer to left edge for next iteration
- left_pad -= chunk_length
- if right_pad > 0:
- # Pad with reflected values on right side:
- # First limit chunk size which can't be larger than pad area
- chunk_length = min(old_length, right_pad)
- # Slice right to left, start on or next to edge, stop relative to start
- start = -right_pad + edge_offset - 2
- stop = start - chunk_length
- right_slice = _slice_at_axis(slice(start, stop, -1), axis)
- right_chunk = padded[right_slice]
- if method == "odd":
- # Negate chunk and align with edge
- edge_slice = _slice_at_axis(
- slice(-right_pad - 1, -right_pad), axis)
- right_chunk = 2 * padded[edge_slice] - right_chunk
- # Insert chunk into padded area
- start = padded.shape[axis] - right_pad
- stop = start + chunk_length
- pad_area = _slice_at_axis(slice(start, stop), axis)
- padded[pad_area] = right_chunk
- # Adjust pointer to right edge for next iteration
- right_pad -= chunk_length
- return left_pad, right_pad
- def _set_wrap_both(padded, axis, width_pair):
- """
- Pad `axis` of `arr` with wrapped values.
- Parameters
- ----------
- padded : ndarray
- Input array of arbitrary shape.
- axis : int
- Axis along which to pad `arr`.
- width_pair : (int, int)
- Pair of widths that mark the pad area on both sides in the given
- dimension.
- Returns
- -------
- pad_amt : tuple of ints, length 2
- New index positions of padding to do along the `axis`. If these are
- both 0, padding is done in this dimension.
- """
- left_pad, right_pad = width_pair
- period = padded.shape[axis] - right_pad - left_pad
- # If the current dimension of `arr` doesn't contain enough valid values
- # (not part of the undefined pad area) we need to pad multiple times.
- # Each time the pad area shrinks on both sides which is communicated with
- # these variables.
- new_left_pad = 0
- new_right_pad = 0
- if left_pad > 0:
- # Pad with wrapped values on left side
- # First slice chunk from right side of the non-pad area.
- # Use min(period, left_pad) to ensure that chunk is not larger than
- # pad area
- right_slice = _slice_at_axis(
- slice(-right_pad - min(period, left_pad),
- -right_pad if right_pad != 0 else None),
- axis
- )
- right_chunk = padded[right_slice]
- if left_pad > period:
- # Chunk is smaller than pad area
- pad_area = _slice_at_axis(slice(left_pad - period, left_pad), axis)
- new_left_pad = left_pad - period
- else:
- # Chunk matches pad area
- pad_area = _slice_at_axis(slice(None, left_pad), axis)
- padded[pad_area] = right_chunk
- if right_pad > 0:
- # Pad with wrapped values on right side
- # First slice chunk from left side of the non-pad area.
- # Use min(period, right_pad) to ensure that chunk is not larger than
- # pad area
- left_slice = _slice_at_axis(
- slice(left_pad, left_pad + min(period, right_pad),), axis)
- left_chunk = padded[left_slice]
- if right_pad > period:
- # Chunk is smaller than pad area
- pad_area = _slice_at_axis(
- slice(-right_pad, -right_pad + period), axis)
- new_right_pad = right_pad - period
- else:
- # Chunk matches pad area
- pad_area = _slice_at_axis(slice(-right_pad, None), axis)
- padded[pad_area] = left_chunk
- return new_left_pad, new_right_pad
- def _as_pairs(x, ndim, as_index=False):
- """
- Broadcast `x` to an array with the shape (`ndim`, 2).
- A helper function for `pad` that prepares and validates arguments like
- `pad_width` for iteration in pairs.
- Parameters
- ----------
- x : {None, scalar, array-like}
- The object to broadcast to the shape (`ndim`, 2).
- ndim : int
- Number of pairs the broadcasted `x` will have.
- as_index : bool, optional
- If `x` is not None, try to round each element of `x` to an integer
- (dtype `np.intp`) and ensure every element is positive.
- Returns
- -------
- pairs : nested iterables, shape (`ndim`, 2)
- The broadcasted version of `x`.
- Raises
- ------
- ValueError
- If `as_index` is True and `x` contains negative elements.
- Or if `x` is not broadcastable to the shape (`ndim`, 2).
- """
- if x is None:
- # Pass through None as a special case, otherwise np.round(x) fails
- # with an AttributeError
- return ((None, None),) * ndim
- x = np.array(x)
- if as_index:
- x = np.round(x).astype(np.intp, copy=False)
- if x.ndim < 3:
- # Optimization: Possibly use faster paths for cases where `x` has
- # only 1 or 2 elements. `np.broadcast_to` could handle these as well
- # but is currently slower
- if x.size == 1:
- # x was supplied as a single value
- x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2
- if as_index and x < 0:
- raise ValueError("index can't contain negative values")
- return ((x[0], x[0]),) * ndim
- if x.size == 2 and x.shape != (2, 1):
- # x was supplied with a single value for each side
- # but except case when each dimension has a single value
- # which should be broadcasted to a pair,
- # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]]
- x = x.ravel() # Ensure x[0], x[1] works
- if as_index and (x[0] < 0 or x[1] < 0):
- raise ValueError("index can't contain negative values")
- return ((x[0], x[1]),) * ndim
- if as_index and x.min() < 0:
- raise ValueError("index can't contain negative values")
- # Converting the array with `tolist` seems to improve performance
- # when iterating and indexing the result (see usage in `pad`)
- return np.broadcast_to(x, (ndim, 2)).tolist()
- def _pad_dispatcher(array, pad_width, mode=None, **kwargs):
- return (array,)
- ###############################################################################
- # Public functions
- @array_function_dispatch(_pad_dispatcher, module='numpy')
- def pad(array, pad_width, mode='constant', **kwargs):
- """
- Pad an array.
- Parameters
- ----------
- array : array_like of rank N
- The array to pad.
- pad_width : {sequence, array_like, int}
- Number of values padded to the edges of each axis.
- ((before_1, after_1), ... (before_N, after_N)) unique pad widths
- for each axis.
- ((before, after),) yields same before and after pad for each axis.
- (pad,) or int is a shortcut for before = after = pad width for all
- axes.
- mode : str or function, optional
- One of the following string values or a user supplied function.
- 'constant' (default)
- Pads with a constant value.
- 'edge'
- Pads with the edge values of array.
- 'linear_ramp'
- Pads with the linear ramp between end_value and the
- array edge value.
- 'maximum'
- Pads with the maximum value of all or part of the
- vector along each axis.
- 'mean'
- Pads with the mean value of all or part of the
- vector along each axis.
- 'median'
- Pads with the median value of all or part of the
- vector along each axis.
- 'minimum'
- Pads with the minimum value of all or part of the
- vector along each axis.
- 'reflect'
- Pads with the reflection of the vector mirrored on
- the first and last values of the vector along each
- axis.
- 'symmetric'
- Pads with the reflection of the vector mirrored
- along the edge of the array.
- 'wrap'
- Pads with the wrap of the vector along the axis.
- The first values are used to pad the end and the
- end values are used to pad the beginning.
- 'empty'
- Pads with undefined values.
- .. versionadded:: 1.17
- <function>
- Padding function, see Notes.
- stat_length : sequence or int, optional
- Used in 'maximum', 'mean', 'median', and 'minimum'. Number of
- values at edge of each axis used to calculate the statistic value.
- ((before_1, after_1), ... (before_N, after_N)) unique statistic
- lengths for each axis.
- ((before, after),) yields same before and after statistic lengths
- for each axis.
- (stat_length,) or int is a shortcut for before = after = statistic
- length for all axes.
- Default is ``None``, to use the entire axis.
- constant_values : sequence or scalar, optional
- Used in 'constant'. The values to set the padded values for each
- axis.
- ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants
- for each axis.
- ``((before, after),)`` yields same before and after constants for each
- axis.
- ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
- all axes.
- Default is 0.
- end_values : sequence or scalar, optional
- Used in 'linear_ramp'. The values used for the ending value of the
- linear_ramp and that will form the edge of the padded array.
- ``((before_1, after_1), ... (before_N, after_N))`` unique end values
- for each axis.
- ``((before, after),)`` yields same before and after end values for each
- axis.
- ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
- all axes.
- Default is 0.
- reflect_type : {'even', 'odd'}, optional
- Used in 'reflect', and 'symmetric'. The 'even' style is the
- default with an unaltered reflection around the edge value. For
- the 'odd' style, the extended part of the array is created by
- subtracting the reflected values from two times the edge value.
- Returns
- -------
- pad : ndarray
- Padded array of rank equal to `array` with shape increased
- according to `pad_width`.
- Notes
- -----
- .. versionadded:: 1.7.0
- For an array with rank greater than 1, some of the padding of later
- axes is calculated from padding of previous axes. This is easiest to
- think about with a rank 2 array where the corners of the padded array
- are calculated by using padded values from the first axis.
- The padding function, if used, should modify a rank 1 array in-place. It
- has the following signature::
- padding_func(vector, iaxis_pad_width, iaxis, kwargs)
- where
- vector : ndarray
- A rank 1 array already padded with zeros. Padded values are
- vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:].
- iaxis_pad_width : tuple
- A 2-tuple of ints, iaxis_pad_width[0] represents the number of
- values padded at the beginning of vector where
- iaxis_pad_width[1] represents the number of values padded at
- the end of vector.
- iaxis : int
- The axis currently being calculated.
- kwargs : dict
- Any keyword arguments the function requires.
- Examples
- --------
- >>> a = [1, 2, 3, 4, 5]
- >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6))
- array([4, 4, 1, ..., 6, 6, 6])
- >>> np.pad(a, (2, 3), 'edge')
- array([1, 1, 1, ..., 5, 5, 5])
- >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
- array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4])
- >>> np.pad(a, (2,), 'maximum')
- array([5, 5, 1, 2, 3, 4, 5, 5, 5])
- >>> np.pad(a, (2,), 'mean')
- array([3, 3, 1, 2, 3, 4, 5, 3, 3])
- >>> np.pad(a, (2,), 'median')
- array([3, 3, 1, 2, 3, 4, 5, 3, 3])
- >>> a = [[1, 2], [3, 4]]
- >>> np.pad(a, ((3, 2), (2, 3)), 'minimum')
- array([[1, 1, 1, 2, 1, 1, 1],
- [1, 1, 1, 2, 1, 1, 1],
- [1, 1, 1, 2, 1, 1, 1],
- [1, 1, 1, 2, 1, 1, 1],
- [3, 3, 3, 4, 3, 3, 3],
- [1, 1, 1, 2, 1, 1, 1],
- [1, 1, 1, 2, 1, 1, 1]])
- >>> a = [1, 2, 3, 4, 5]
- >>> np.pad(a, (2, 3), 'reflect')
- array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])
- >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd')
- array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
- >>> np.pad(a, (2, 3), 'symmetric')
- array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])
- >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd')
- array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])
- >>> np.pad(a, (2, 3), 'wrap')
- array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])
- >>> def pad_with(vector, pad_width, iaxis, kwargs):
- ... pad_value = kwargs.get('padder', 10)
- ... vector[:pad_width[0]] = pad_value
- ... vector[-pad_width[1]:] = pad_value
- >>> a = np.arange(6)
- >>> a = a.reshape((2, 3))
- >>> np.pad(a, 2, pad_with)
- array([[10, 10, 10, 10, 10, 10, 10],
- [10, 10, 10, 10, 10, 10, 10],
- [10, 10, 0, 1, 2, 10, 10],
- [10, 10, 3, 4, 5, 10, 10],
- [10, 10, 10, 10, 10, 10, 10],
- [10, 10, 10, 10, 10, 10, 10]])
- >>> np.pad(a, 2, pad_with, padder=100)
- array([[100, 100, 100, 100, 100, 100, 100],
- [100, 100, 100, 100, 100, 100, 100],
- [100, 100, 0, 1, 2, 100, 100],
- [100, 100, 3, 4, 5, 100, 100],
- [100, 100, 100, 100, 100, 100, 100],
- [100, 100, 100, 100, 100, 100, 100]])
- """
- array = np.asarray(array)
- pad_width = np.asarray(pad_width)
- if not pad_width.dtype.kind == 'i':
- raise TypeError('`pad_width` must be of integral type.')
- # Broadcast to shape (array.ndim, 2)
- pad_width = _as_pairs(pad_width, array.ndim, as_index=True)
- if callable(mode):
- # Old behavior: Use user-supplied function with np.apply_along_axis
- function = mode
- # Create a new zero padded array
- padded, _ = _pad_simple(array, pad_width, fill_value=0)
- # And apply along each axis
- for axis in range(padded.ndim):
- # Iterate using ndindex as in apply_along_axis, but assuming that
- # function operates inplace on the padded array.
- # view with the iteration axis at the end
- view = np.moveaxis(padded, axis, -1)
- # compute indices for the iteration axes, and append a trailing
- # ellipsis to prevent 0d arrays decaying to scalars (gh-8642)
- inds = ndindex(view.shape[:-1])
- inds = (ind + (Ellipsis,) for ind in inds)
- for ind in inds:
- function(view[ind], pad_width[axis], axis, kwargs)
- return padded
- # Make sure that no unsupported keywords were passed for the current mode
- allowed_kwargs = {
- 'empty': [], 'edge': [], 'wrap': [],
- 'constant': ['constant_values'],
- 'linear_ramp': ['end_values'],
- 'maximum': ['stat_length'],
- 'mean': ['stat_length'],
- 'median': ['stat_length'],
- 'minimum': ['stat_length'],
- 'reflect': ['reflect_type'],
- 'symmetric': ['reflect_type'],
- }
- try:
- unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode])
- except KeyError:
- raise ValueError("mode '{}' is not supported".format(mode)) from None
- if unsupported_kwargs:
- raise ValueError("unsupported keyword arguments for mode '{}': {}"
- .format(mode, unsupported_kwargs))
- stat_functions = {"maximum": np.amax, "minimum": np.amin,
- "mean": np.mean, "median": np.median}
- # Create array with final shape and original values
- # (padded area is undefined)
- padded, original_area_slice = _pad_simple(array, pad_width)
- # And prepare iteration over all dimensions
- # (zipping may be more readable than using enumerate)
- axes = range(padded.ndim)
- if mode == "constant":
- values = kwargs.get("constant_values", 0)
- values = _as_pairs(values, padded.ndim)
- for axis, width_pair, value_pair in zip(axes, pad_width, values):
- roi = _view_roi(padded, original_area_slice, axis)
- _set_pad_area(roi, axis, width_pair, value_pair)
- elif mode == "empty":
- pass # Do nothing as _pad_simple already returned the correct result
- elif array.size == 0:
- # Only modes "constant" and "empty" can extend empty axes, all other
- # modes depend on `array` not being empty
- # -> ensure every empty axis is only "padded with 0"
- for axis, width_pair in zip(axes, pad_width):
- if array.shape[axis] == 0 and any(width_pair):
- raise ValueError(
- "can't extend empty axis {} using modes other than "
- "'constant' or 'empty'".format(axis)
- )
- # passed, don't need to do anything more as _pad_simple already
- # returned the correct result
- elif mode == "edge":
- for axis, width_pair in zip(axes, pad_width):
- roi = _view_roi(padded, original_area_slice, axis)
- edge_pair = _get_edges(roi, axis, width_pair)
- _set_pad_area(roi, axis, width_pair, edge_pair)
- elif mode == "linear_ramp":
- end_values = kwargs.get("end_values", 0)
- end_values = _as_pairs(end_values, padded.ndim)
- for axis, width_pair, value_pair in zip(axes, pad_width, end_values):
- roi = _view_roi(padded, original_area_slice, axis)
- ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair)
- _set_pad_area(roi, axis, width_pair, ramp_pair)
- elif mode in stat_functions:
- func = stat_functions[mode]
- length = kwargs.get("stat_length", None)
- length = _as_pairs(length, padded.ndim, as_index=True)
- for axis, width_pair, length_pair in zip(axes, pad_width, length):
- roi = _view_roi(padded, original_area_slice, axis)
- stat_pair = _get_stats(roi, axis, width_pair, length_pair, func)
- _set_pad_area(roi, axis, width_pair, stat_pair)
- elif mode in {"reflect", "symmetric"}:
- method = kwargs.get("reflect_type", "even")
- include_edge = True if mode == "symmetric" else False
- for axis, (left_index, right_index) in zip(axes, pad_width):
- if array.shape[axis] == 1 and (left_index > 0 or right_index > 0):
- # Extending singleton dimension for 'reflect' is legacy
- # behavior; it really should raise an error.
- edge_pair = _get_edges(padded, axis, (left_index, right_index))
- _set_pad_area(
- padded, axis, (left_index, right_index), edge_pair)
- continue
- roi = _view_roi(padded, original_area_slice, axis)
- while left_index > 0 or right_index > 0:
- # Iteratively pad until dimension is filled with reflected
- # values. This is necessary if the pad area is larger than
- # the length of the original values in the current dimension.
- left_index, right_index = _set_reflect_both(
- roi, axis, (left_index, right_index),
- method, include_edge
- )
- elif mode == "wrap":
- for axis, (left_index, right_index) in zip(axes, pad_width):
- roi = _view_roi(padded, original_area_slice, axis)
- while left_index > 0 or right_index > 0:
- # Iteratively pad until dimension is filled with wrapped
- # values. This is necessary if the pad area is larger than
- # the length of the original values in the current dimension.
- left_index, right_index = _set_wrap_both(
- roi, axis, (left_index, right_index))
- return padded
|