Chris@87: """ Chris@87: The arraypad module contains a group of functions to pad values onto the edges Chris@87: of an n-dimensional array. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import numpy as np Chris@87: from numpy.compat import long Chris@87: Chris@87: Chris@87: __all__ = ['pad'] Chris@87: Chris@87: Chris@87: ############################################################################### Chris@87: # Private utility functions. Chris@87: Chris@87: Chris@87: def _arange_ndarray(arr, shape, axis, reverse=False): Chris@87: """ Chris@87: Create an ndarray of `shape` with increments along specified `axis` Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: shape : tuple of ints Chris@87: Shape of desired array. Should be equivalent to `arr.shape` except Chris@87: `shape[axis]` which may have any positive value. Chris@87: axis : int Chris@87: Axis to increment along. Chris@87: reverse : bool Chris@87: If False, increment in a positive fashion from 1 to `shape[axis]`, Chris@87: inclusive. If True, the bounds are the same but the order reversed. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array sized to pad `arr` along `axis`, with linear range from Chris@87: 1 to `shape[axis]` along specified `axis`. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The range is deliberately 1-indexed for this specific use case. Think of Chris@87: this algorithm as broadcasting `np.arange` to a single `axis` of an Chris@87: arbitrarily shaped ndarray. Chris@87: Chris@87: """ Chris@87: initshape = tuple(1 if i != axis else shape[axis] Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if not reverse: Chris@87: padarr = np.arange(1, shape[axis] + 1) Chris@87: else: Chris@87: padarr = np.arange(shape[axis], 0, -1) Chris@87: padarr = padarr.reshape(initshape) Chris@87: for i, dim in enumerate(shape): Chris@87: if padarr.shape[i] != dim: Chris@87: padarr = padarr.repeat(dim, axis=i) Chris@87: return padarr Chris@87: Chris@87: Chris@87: def _round_ifneeded(arr, dtype): Chris@87: """ Chris@87: Rounds arr inplace if destination dtype is integer. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array. Chris@87: dtype : dtype Chris@87: The dtype of the destination array. Chris@87: Chris@87: """ Chris@87: if np.issubdtype(dtype, np.integer): Chris@87: arr.round(out=arr) Chris@87: Chris@87: Chris@87: def _prepend_const(arr, pad_amt, val, axis=-1): Chris@87: """ Chris@87: Prepend constant `val` along `axis` of `arr`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: val : scalar Chris@87: Constant value to use. For best results should be of type `arr.dtype`; Chris@87: if not `arr.dtype` will be cast to `arr.dtype`. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` constant `val` prepended along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: padshape = tuple(x if i != axis else pad_amt Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if val == 0: Chris@87: return np.concatenate((np.zeros(padshape, dtype=arr.dtype), arr), Chris@87: axis=axis) Chris@87: else: Chris@87: return np.concatenate(((np.zeros(padshape) + val).astype(arr.dtype), Chris@87: arr), axis=axis) Chris@87: Chris@87: Chris@87: def _append_const(arr, pad_amt, val, axis=-1): Chris@87: """ Chris@87: Append constant `val` along `axis` of `arr`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: val : scalar Chris@87: Constant value to use. For best results should be of type `arr.dtype`; Chris@87: if not `arr.dtype` will be cast to `arr.dtype`. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` constant `val` appended along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: padshape = tuple(x if i != axis else pad_amt Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if val == 0: Chris@87: return np.concatenate((arr, np.zeros(padshape, dtype=arr.dtype)), Chris@87: axis=axis) Chris@87: else: Chris@87: return np.concatenate( Chris@87: (arr, (np.zeros(padshape) + val).astype(arr.dtype)), axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_edge(arr, pad_amt, axis=-1): Chris@87: """ Chris@87: Prepend `pad_amt` to `arr` along `axis` by extending edge values. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, extended by `pad_amt` edge values appended along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: edge_slice = tuple(slice(None) if i != axis else 0 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_arr = arr[edge_slice].reshape(pad_singleton) Chris@87: return np.concatenate((edge_arr.repeat(pad_amt, axis=axis), arr), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _append_edge(arr, pad_amt, axis=-1): Chris@87: """ Chris@87: Append `pad_amt` to `arr` along `axis` by extending edge values. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, extended by `pad_amt` edge values prepended along Chris@87: `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: edge_slice = tuple(slice(None) if i != axis else arr.shape[axis] - 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_arr = arr[edge_slice].reshape(pad_singleton) Chris@87: return np.concatenate((arr, edge_arr.repeat(pad_amt, axis=axis)), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_ramp(arr, pad_amt, end, axis=-1): Chris@87: """ Chris@87: Prepend linear ramp along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: end : scalar Chris@87: Constal value to use. For best results should be of type `arr.dtype`; Chris@87: if not `arr.dtype` will be cast to `arr.dtype`. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values prepended along `axis`. The Chris@87: prepended region ramps linearly from the edge value to `end`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Generate shape for final concatenated array Chris@87: padshape = tuple(x if i != axis else pad_amt Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Generate an n-dimensional array incrementing along `axis` Chris@87: ramp_arr = _arange_ndarray(arr, padshape, axis, Chris@87: reverse=True).astype(np.float64) Chris@87: Chris@87: # Appropriate slicing to extract n-dimensional edge along `axis` Chris@87: edge_slice = tuple(slice(None) if i != axis else 0 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract edge, reshape to original rank, and extend along `axis` Chris@87: edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis) Chris@87: Chris@87: # Linear ramp Chris@87: slope = (end - edge_pad) / float(pad_amt) Chris@87: ramp_arr = ramp_arr * slope Chris@87: ramp_arr += edge_pad Chris@87: _round_ifneeded(ramp_arr, arr.dtype) Chris@87: Chris@87: # Ramp values will most likely be float, cast them to the same type as arr Chris@87: return np.concatenate((ramp_arr.astype(arr.dtype), arr), axis=axis) Chris@87: Chris@87: Chris@87: def _append_ramp(arr, pad_amt, end, axis=-1): Chris@87: """ Chris@87: Append linear ramp along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: end : scalar Chris@87: Constal value to use. For best results should be of type `arr.dtype`; Chris@87: if not `arr.dtype` will be cast to `arr.dtype`. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: appended region ramps linearly from the edge value to `end`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Generate shape for final concatenated array Chris@87: padshape = tuple(x if i != axis else pad_amt Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Generate an n-dimensional array incrementing along `axis` Chris@87: ramp_arr = _arange_ndarray(arr, padshape, axis, Chris@87: reverse=False).astype(np.float64) Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: edge_slice = tuple(slice(None) if i != axis else -1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract edge, reshape to original rank, and extend along `axis` Chris@87: edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis) Chris@87: Chris@87: # Linear ramp Chris@87: slope = (end - edge_pad) / float(pad_amt) Chris@87: ramp_arr = ramp_arr * slope Chris@87: ramp_arr += edge_pad Chris@87: _round_ifneeded(ramp_arr, arr.dtype) Chris@87: Chris@87: # Ramp values will most likely be float, cast them to the same type as arr Chris@87: return np.concatenate((arr, ramp_arr.astype(arr.dtype)), axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_max(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Prepend `pad_amt` maximum values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate maximum. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: prepended region is the maximum of the first `num` values along Chris@87: `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _prepend_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: max_slice = tuple(slice(None) if i != axis else slice(num) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate max, reshape to add singleton dimension back Chris@87: max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton) Chris@87: Chris@87: # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate((max_chunk.repeat(pad_amt, axis=axis), arr), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _append_max(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Pad one `axis` of `arr` with the maximum of the last `num` elements. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate maximum. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: appended region is the maximum of the final `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _append_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: end = arr.shape[axis] - 1 Chris@87: if num is not None: Chris@87: max_slice = tuple( Chris@87: slice(None) if i != axis else slice(end, end - num, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: else: Chris@87: max_slice = tuple(slice(None) for x in arr.shape) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate max, reshape to add singleton dimension back Chris@87: max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton) Chris@87: Chris@87: # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate((arr, max_chunk.repeat(pad_amt, axis=axis)), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_mean(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Prepend `pad_amt` mean values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate mean. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values prepended along `axis`. The Chris@87: prepended region is the mean of the first `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _prepend_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: mean_slice = tuple(slice(None) if i != axis else slice(num) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate mean, reshape to add singleton dimension back Chris@87: mean_chunk = arr[mean_slice].mean(axis).reshape(pad_singleton) Chris@87: _round_ifneeded(mean_chunk, arr.dtype) Chris@87: Chris@87: # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate((mean_chunk.repeat(pad_amt, axis).astype(arr.dtype), Chris@87: arr), axis=axis) Chris@87: Chris@87: Chris@87: def _append_mean(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Append `pad_amt` mean values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate mean. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: appended region is the maximum of the final `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _append_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: end = arr.shape[axis] - 1 Chris@87: if num is not None: Chris@87: mean_slice = tuple( Chris@87: slice(None) if i != axis else slice(end, end - num, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: else: Chris@87: mean_slice = tuple(slice(None) for x in arr.shape) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate mean, reshape to add singleton dimension back Chris@87: mean_chunk = arr[mean_slice].mean(axis=axis).reshape(pad_singleton) Chris@87: _round_ifneeded(mean_chunk, arr.dtype) Chris@87: Chris@87: # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate( Chris@87: (arr, mean_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_med(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Prepend `pad_amt` median values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate median. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values prepended along `axis`. The Chris@87: prepended region is the median of the first `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _prepend_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: med_slice = tuple(slice(None) if i != axis else slice(num) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate median, reshape to add singleton dimension back Chris@87: med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton) Chris@87: _round_ifneeded(med_chunk, arr.dtype) Chris@87: Chris@87: # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate( Chris@87: (med_chunk.repeat(pad_amt, axis).astype(arr.dtype), arr), axis=axis) Chris@87: Chris@87: Chris@87: def _append_med(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Append `pad_amt` median values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate median. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: appended region is the median of the final `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _append_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: end = arr.shape[axis] - 1 Chris@87: if num is not None: Chris@87: med_slice = tuple( Chris@87: slice(None) if i != axis else slice(end, end - num, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: else: Chris@87: med_slice = tuple(slice(None) for x in arr.shape) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate median, reshape to add singleton dimension back Chris@87: med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton) Chris@87: _round_ifneeded(med_chunk, arr.dtype) Chris@87: Chris@87: # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate( Chris@87: (arr, med_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis) Chris@87: Chris@87: Chris@87: def _prepend_min(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Prepend `pad_amt` minimum values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to prepend. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate minimum. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values prepended along `axis`. The Chris@87: prepended region is the minimum of the first `num` values along Chris@87: `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _prepend_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: min_slice = tuple(slice(None) if i != axis else slice(num) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate min, reshape to add singleton dimension back Chris@87: min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton) Chris@87: Chris@87: # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate((min_chunk.repeat(pad_amt, axis=axis), arr), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _append_min(arr, pad_amt, num, axis=-1): Chris@87: """ Chris@87: Append `pad_amt` median values along `axis`. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : int Chris@87: Amount of padding to append. Chris@87: num : int Chris@87: Depth into `arr` along `axis` to calculate minimum. Chris@87: Range: [1, `arr.shape[axis]`] or None (entire axis) Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt` values appended along `axis`. The Chris@87: appended region is the minimum of the final `num` values along `axis`. Chris@87: Chris@87: """ Chris@87: if pad_amt == 0: Chris@87: return arr Chris@87: Chris@87: # Equivalent to edge padding for single value, so do that instead Chris@87: if num == 1: Chris@87: return _append_edge(arr, pad_amt, axis) Chris@87: Chris@87: # Use entire array if `num` is too large Chris@87: if num is not None: Chris@87: if num >= arr.shape[axis]: Chris@87: num = None Chris@87: Chris@87: # Slice a chunk from the edge to calculate stats on Chris@87: end = arr.shape[axis] - 1 Chris@87: if num is not None: Chris@87: min_slice = tuple( Chris@87: slice(None) if i != axis else slice(end, end - num, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: else: Chris@87: min_slice = tuple(slice(None) for x in arr.shape) Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: # Extract slice, calculate min, reshape to add singleton dimension back Chris@87: min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton) Chris@87: Chris@87: # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` Chris@87: return np.concatenate((arr, min_chunk.repeat(pad_amt, axis=axis)), Chris@87: axis=axis) Chris@87: Chris@87: Chris@87: def _pad_ref(arr, pad_amt, method, axis=-1): Chris@87: """ Chris@87: Pad `axis` of `arr` by reflection. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : tuple of ints, length 2 Chris@87: Padding to (prepend, append) along `axis`. Chris@87: method : str Chris@87: Controls method of reflection; options are 'even' or 'odd'. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` Chris@87: values appended along `axis`. Both regions are padded with reflected Chris@87: values from the original array. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: This algorithm does not pad with repetition, i.e. the edges are not Chris@87: repeated in the reflection. For that behavior, use `method='symmetric'`. Chris@87: Chris@87: The modes 'reflect', 'symmetric', and 'wrap' must be padded with a Chris@87: single function, lest the indexing tricks in non-integer multiples of the Chris@87: original shape would violate repetition in the final iteration. Chris@87: Chris@87: """ Chris@87: # Implicit booleanness to test for zero (or None) in any scalar type Chris@87: if pad_amt[0] == 0 and pad_amt[1] == 0: Chris@87: return arr Chris@87: Chris@87: ########################################################################## Chris@87: # Prepended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from near edge to pad `arr` before Chris@87: ref_slice = tuple(slice(None) if i != axis else slice(pad_amt[0], 0, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: Chris@87: ref_chunk1 = arr[ref_slice] Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if pad_amt[0] == 1: Chris@87: ref_chunk1 = ref_chunk1.reshape(pad_singleton) Chris@87: Chris@87: # Memory/computationally more expensive, only do this if `method='odd'` Chris@87: if 'odd' in method and pad_amt[0] > 0: Chris@87: edge_slice1 = tuple(slice(None) if i != axis else 0 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_chunk = arr[edge_slice1].reshape(pad_singleton) Chris@87: ref_chunk1 = 2 * edge_chunk - ref_chunk1 Chris@87: del edge_chunk Chris@87: Chris@87: ########################################################################## Chris@87: # Appended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from far edge to pad `arr` after Chris@87: start = arr.shape[axis] - pad_amt[1] - 1 Chris@87: end = arr.shape[axis] - 1 Chris@87: ref_slice = tuple(slice(None) if i != axis else slice(start, end) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: ref_chunk2 = arr[ref_slice][rev_idx] Chris@87: Chris@87: if pad_amt[1] == 1: Chris@87: ref_chunk2 = ref_chunk2.reshape(pad_singleton) Chris@87: Chris@87: if 'odd' in method: Chris@87: edge_slice2 = tuple(slice(None) if i != axis else -1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_chunk = arr[edge_slice2].reshape(pad_singleton) Chris@87: ref_chunk2 = 2 * edge_chunk - ref_chunk2 Chris@87: del edge_chunk Chris@87: Chris@87: # Concatenate `arr` with both chunks, extending along `axis` Chris@87: return np.concatenate((ref_chunk1, arr, ref_chunk2), axis=axis) Chris@87: Chris@87: Chris@87: def _pad_sym(arr, pad_amt, method, axis=-1): Chris@87: """ Chris@87: Pad `axis` of `arr` by symmetry. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : tuple of ints, length 2 Chris@87: Padding to (prepend, append) along `axis`. Chris@87: method : str Chris@87: Controls method of symmetry; options are 'even' or 'odd'. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` Chris@87: values appended along `axis`. Both regions are padded with symmetric Chris@87: values from the original array. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: This algorithm DOES pad with repetition, i.e. the edges are repeated. Chris@87: For a method that does not repeat edges, use `method='reflect'`. Chris@87: Chris@87: The modes 'reflect', 'symmetric', and 'wrap' must be padded with a Chris@87: single function, lest the indexing tricks in non-integer multiples of the Chris@87: original shape would violate repetition in the final iteration. Chris@87: Chris@87: """ Chris@87: # Implicit booleanness to test for zero (or None) in any scalar type Chris@87: if pad_amt[0] == 0 and pad_amt[1] == 0: Chris@87: return arr Chris@87: Chris@87: ########################################################################## Chris@87: # Prepended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from near edge to pad `arr` before Chris@87: sym_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[0]) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: sym_chunk1 = arr[sym_slice][rev_idx] Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if pad_amt[0] == 1: Chris@87: sym_chunk1 = sym_chunk1.reshape(pad_singleton) Chris@87: Chris@87: # Memory/computationally more expensive, only do this if `method='odd'` Chris@87: if 'odd' in method and pad_amt[0] > 0: Chris@87: edge_slice1 = tuple(slice(None) if i != axis else 0 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_chunk = arr[edge_slice1].reshape(pad_singleton) Chris@87: sym_chunk1 = 2 * edge_chunk - sym_chunk1 Chris@87: del edge_chunk Chris@87: Chris@87: ########################################################################## Chris@87: # Appended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from far edge to pad `arr` after Chris@87: start = arr.shape[axis] - pad_amt[1] Chris@87: end = arr.shape[axis] Chris@87: sym_slice = tuple(slice(None) if i != axis else slice(start, end) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: sym_chunk2 = arr[sym_slice][rev_idx] Chris@87: Chris@87: if pad_amt[1] == 1: Chris@87: sym_chunk2 = sym_chunk2.reshape(pad_singleton) Chris@87: Chris@87: if 'odd' in method: Chris@87: edge_slice2 = tuple(slice(None) if i != axis else -1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: edge_chunk = arr[edge_slice2].reshape(pad_singleton) Chris@87: sym_chunk2 = 2 * edge_chunk - sym_chunk2 Chris@87: del edge_chunk Chris@87: Chris@87: # Concatenate `arr` with both chunks, extending along `axis` Chris@87: return np.concatenate((sym_chunk1, arr, sym_chunk2), axis=axis) Chris@87: Chris@87: Chris@87: def _pad_wrap(arr, pad_amt, axis=-1): Chris@87: """ Chris@87: Pad `axis` of `arr` via wrapping. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: arr : ndarray Chris@87: Input array of arbitrary shape. Chris@87: pad_amt : tuple of ints, length 2 Chris@87: Padding to (prepend, append) along `axis`. Chris@87: axis : int Chris@87: Axis along which to pad `arr`. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: padarr : ndarray Chris@87: Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` Chris@87: values appended along `axis`. Both regions are padded wrapped values Chris@87: from the opposite end of `axis`. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: This method of padding is also known as 'tile' or 'tiling'. Chris@87: Chris@87: The modes 'reflect', 'symmetric', and 'wrap' must be padded with a Chris@87: single function, lest the indexing tricks in non-integer multiples of the Chris@87: original shape would violate repetition in the final iteration. Chris@87: Chris@87: """ Chris@87: # Implicit booleanness to test for zero (or None) in any scalar type Chris@87: if pad_amt[0] == 0 and pad_amt[1] == 0: Chris@87: return arr Chris@87: Chris@87: ########################################################################## Chris@87: # Prepended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from near edge to pad `arr` before Chris@87: start = arr.shape[axis] - pad_amt[0] Chris@87: end = arr.shape[axis] Chris@87: wrap_slice = tuple(slice(None) if i != axis else slice(start, end) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: wrap_chunk1 = arr[wrap_slice] Chris@87: Chris@87: # Shape to restore singleton dimension after slicing Chris@87: pad_singleton = tuple(x if i != axis else 1 Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: if pad_amt[0] == 1: Chris@87: wrap_chunk1 = wrap_chunk1.reshape(pad_singleton) Chris@87: Chris@87: ########################################################################## Chris@87: # Appended region Chris@87: Chris@87: # Slice off a reverse indexed chunk from far edge to pad `arr` after Chris@87: wrap_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[1]) Chris@87: for (i, x) in enumerate(arr.shape)) Chris@87: wrap_chunk2 = arr[wrap_slice] Chris@87: Chris@87: if pad_amt[1] == 1: Chris@87: wrap_chunk2 = wrap_chunk2.reshape(pad_singleton) Chris@87: Chris@87: # Concatenate `arr` with both chunks, extending along `axis` Chris@87: return np.concatenate((wrap_chunk1, arr, wrap_chunk2), axis=axis) Chris@87: Chris@87: Chris@87: def _normalize_shape(narray, shape): Chris@87: """ Chris@87: Private function which does some checks and normalizes the possibly Chris@87: much simpler representations of 'pad_width', 'stat_length', Chris@87: 'constant_values', 'end_values'. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: narray : ndarray Chris@87: Input ndarray Chris@87: shape : {sequence, int}, optional Chris@87: The width of padding (pad_width) or the number of elements on the Chris@87: edge of the narray used for statistics (stat_length). Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique number of Chris@87: elements for each axis where `N` is rank of `narray`. Chris@87: ((before, after),) yields same before and after constants for each Chris@87: axis. Chris@87: (constant,) or int is a shortcut for before = after = constant for Chris@87: all axes. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: _normalize_shape : tuple of tuples Chris@87: int => ((int, int), (int, int), ...) Chris@87: [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...) Chris@87: ((int1, int2), (int3, int4), ...) => no change Chris@87: [[int1, int2], ] => ((int1, int2), (int1, int2), ...) Chris@87: ((int1, int2), ) => ((int1, int2), (int1, int2), ...) Chris@87: [[int , ], ] => ((int, int), (int, int), ...) Chris@87: ((int , ), ) => ((int, int), (int, int), ...) Chris@87: Chris@87: """ Chris@87: normshp = None Chris@87: shapelen = len(np.shape(narray)) Chris@87: if (isinstance(shape, int)) or shape is None: Chris@87: normshp = ((shape, shape), ) * shapelen Chris@87: elif (isinstance(shape, (tuple, list)) Chris@87: and isinstance(shape[0], (tuple, list)) Chris@87: and len(shape) == shapelen): Chris@87: normshp = shape Chris@87: for i in normshp: Chris@87: if len(i) != 2: Chris@87: fmt = "Unable to create correctly shaped tuple from %s" Chris@87: raise ValueError(fmt % (normshp,)) Chris@87: elif (isinstance(shape, (tuple, list)) Chris@87: and isinstance(shape[0], (int, float, long)) Chris@87: and len(shape) == 1): Chris@87: normshp = ((shape[0], shape[0]), ) * shapelen Chris@87: elif (isinstance(shape, (tuple, list)) Chris@87: and isinstance(shape[0], (int, float, long)) Chris@87: and len(shape) == 2): Chris@87: normshp = (shape, ) * shapelen Chris@87: if normshp is None: Chris@87: fmt = "Unable to create correctly shaped tuple from %s" Chris@87: raise ValueError(fmt % (shape,)) Chris@87: return normshp Chris@87: Chris@87: Chris@87: def _validate_lengths(narray, number_elements): Chris@87: """ Chris@87: Private function which does some checks and reformats pad_width and Chris@87: stat_length using _normalize_shape. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: narray : ndarray Chris@87: Input ndarray Chris@87: number_elements : {sequence, int}, optional Chris@87: The width of padding (pad_width) or the number of elements on the edge Chris@87: of the narray used for statistics (stat_length). Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique number of Chris@87: elements for each axis. Chris@87: ((before, after),) yields same before and after constants for each Chris@87: axis. Chris@87: (constant,) or int is a shortcut for before = after = constant for all Chris@87: axes. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: _validate_lengths : tuple of tuples Chris@87: int => ((int, int), (int, int), ...) Chris@87: [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...) Chris@87: ((int1, int2), (int3, int4), ...) => no change Chris@87: [[int1, int2], ] => ((int1, int2), (int1, int2), ...) Chris@87: ((int1, int2), ) => ((int1, int2), (int1, int2), ...) Chris@87: [[int , ], ] => ((int, int), (int, int), ...) Chris@87: ((int , ), ) => ((int, int), (int, int), ...) Chris@87: Chris@87: """ Chris@87: normshp = _normalize_shape(narray, number_elements) Chris@87: for i in normshp: Chris@87: chk = [1 if x is None else x for x in i] Chris@87: chk = [1 if x >= 0 else -1 for x in chk] Chris@87: if (chk[0] < 0) or (chk[1] < 0): Chris@87: fmt = "%s cannot contain negative values." Chris@87: raise ValueError(fmt % (number_elements,)) Chris@87: return normshp Chris@87: Chris@87: Chris@87: ############################################################################### Chris@87: # Public functions Chris@87: Chris@87: Chris@87: def pad(array, pad_width, mode=None, **kwargs): Chris@87: """ Chris@87: Pads an array. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: array : array_like of rank N Chris@87: Input array Chris@87: pad_width : {sequence, int} Chris@87: Number of values padded to the edges of each axis. Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique pad widths Chris@87: for each axis. Chris@87: ((before, after),) yields same before and after pad for each axis. Chris@87: (pad,) or int is a shortcut for before = after = pad width for all Chris@87: axes. Chris@87: mode : {str, function} Chris@87: One of the following string values or a user supplied function. Chris@87: Chris@87: 'constant' Chris@87: Pads with a constant value. Chris@87: 'edge' Chris@87: Pads with the edge values of array. Chris@87: 'linear_ramp' Chris@87: Pads with the linear ramp between end_value and the Chris@87: array edge value. Chris@87: 'maximum' Chris@87: Pads with the maximum value of all or part of the Chris@87: vector along each axis. Chris@87: 'mean' Chris@87: Pads with the mean value of all or part of the Chris@87: vector along each axis. Chris@87: 'median' Chris@87: Pads with the median value of all or part of the Chris@87: vector along each axis. Chris@87: 'minimum' Chris@87: Pads with the minimum value of all or part of the Chris@87: vector along each axis. Chris@87: 'reflect' Chris@87: Pads with the reflection of the vector mirrored on Chris@87: the first and last values of the vector along each Chris@87: axis. Chris@87: 'symmetric' Chris@87: Pads with the reflection of the vector mirrored Chris@87: along the edge of the array. Chris@87: 'wrap' Chris@87: Pads with the wrap of the vector along the axis. Chris@87: The first values are used to pad the end and the Chris@87: end values are used to pad the beginning. Chris@87: Chris@87: Padding function, see Notes. Chris@87: stat_length : {sequence, int}, optional Chris@87: Used in 'maximum', 'mean', 'median', and 'minimum'. Number of Chris@87: values at edge of each axis used to calculate the statistic value. Chris@87: Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique statistic Chris@87: lengths for each axis. Chris@87: Chris@87: ((before, after),) yields same before and after statistic lengths Chris@87: for each axis. Chris@87: Chris@87: (stat_length,) or int is a shortcut for before = after = statistic Chris@87: length for all axes. Chris@87: Chris@87: Default is ``None``, to use the entire axis. Chris@87: constant_values : {sequence, int}, optional Chris@87: Used in 'constant'. The values to set the padded values for each Chris@87: axis. Chris@87: Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique pad constants Chris@87: for each axis. Chris@87: Chris@87: ((before, after),) yields same before and after constants for each Chris@87: axis. Chris@87: Chris@87: (constant,) or int is a shortcut for before = after = constant for Chris@87: all axes. Chris@87: Chris@87: Default is 0. Chris@87: end_values : {sequence, int}, optional Chris@87: Used in 'linear_ramp'. The values used for the ending value of the Chris@87: linear_ramp and that will form the edge of the padded array. Chris@87: Chris@87: ((before_1, after_1), ... (before_N, after_N)) unique end values Chris@87: for each axis. Chris@87: Chris@87: ((before, after),) yields same before and after end values for each Chris@87: axis. Chris@87: Chris@87: (constant,) or int is a shortcut for before = after = end value for Chris@87: all axes. Chris@87: Chris@87: Default is 0. Chris@87: reflect_type : str {'even', 'odd'}, optional Chris@87: Used in 'reflect', and 'symmetric'. The 'even' style is the Chris@87: default with an unaltered reflection around the edge value. For Chris@87: the 'odd' style, the extented part of the array is created by Chris@87: subtracting the reflected values from two times the edge value. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: pad : ndarray Chris@87: Padded array of rank equal to `array` with shape increased Chris@87: according to `pad_width`. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: .. versionadded:: 1.7.0 Chris@87: Chris@87: For an array with rank greater than 1, some of the padding of later Chris@87: axes is calculated from padding of previous axes. This is easiest to Chris@87: think about with a rank 2 array where the corners of the padded array Chris@87: are calculated by using padded values from the first axis. Chris@87: Chris@87: The padding function, if used, should return a rank 1 array equal in Chris@87: length to the vector argument with padded values replaced. It has the Chris@87: following signature:: Chris@87: Chris@87: padding_func(vector, iaxis_pad_width, iaxis, **kwargs) Chris@87: Chris@87: where Chris@87: Chris@87: vector : ndarray Chris@87: A rank 1 array already padded with zeros. Padded values are Chris@87: vector[:pad_tuple[0]] and vector[-pad_tuple[1]:]. Chris@87: iaxis_pad_width : tuple Chris@87: A 2-tuple of ints, iaxis_pad_width[0] represents the number of Chris@87: values padded at the beginning of vector where Chris@87: iaxis_pad_width[1] represents the number of values padded at Chris@87: the end of vector. Chris@87: iaxis : int Chris@87: The axis currently being calculated. Chris@87: kwargs : misc Chris@87: Any keyword arguments the function requires. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> a = [1, 2, 3, 4, 5] Chris@87: >>> np.lib.pad(a, (2,3), 'constant', constant_values=(4,6)) Chris@87: array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'edge') Chris@87: array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'linear_ramp', end_values=(5,-4)) Chris@87: array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,), 'maximum') Chris@87: array([5, 5, 1, 2, 3, 4, 5, 5, 5]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,), 'mean') Chris@87: array([3, 3, 1, 2, 3, 4, 5, 3, 3]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,), 'median') Chris@87: array([3, 3, 1, 2, 3, 4, 5, 3, 3]) Chris@87: Chris@87: >>> a = [[1,2], [3,4]] Chris@87: >>> np.lib.pad(a, ((3, 2), (2, 3)), 'minimum') Chris@87: array([[1, 1, 1, 2, 1, 1, 1], Chris@87: [1, 1, 1, 2, 1, 1, 1], Chris@87: [1, 1, 1, 2, 1, 1, 1], Chris@87: [1, 1, 1, 2, 1, 1, 1], Chris@87: [3, 3, 3, 4, 3, 3, 3], Chris@87: [1, 1, 1, 2, 1, 1, 1], Chris@87: [1, 1, 1, 2, 1, 1, 1]]) Chris@87: Chris@87: >>> a = [1, 2, 3, 4, 5] Chris@87: >>> np.lib.pad(a, (2,3), 'reflect') Chris@87: array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'reflect', reflect_type='odd') Chris@87: array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'symmetric') Chris@87: array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'symmetric', reflect_type='odd') Chris@87: array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]) Chris@87: Chris@87: >>> np.lib.pad(a, (2,3), 'wrap') Chris@87: array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]) Chris@87: Chris@87: >>> def padwithtens(vector, pad_width, iaxis, kwargs): Chris@87: ... vector[:pad_width[0]] = 10 Chris@87: ... vector[-pad_width[1]:] = 10 Chris@87: ... return vector Chris@87: Chris@87: >>> a = np.arange(6) Chris@87: >>> a = a.reshape((2,3)) Chris@87: Chris@87: >>> np.lib.pad(a, 2, padwithtens) Chris@87: array([[10, 10, 10, 10, 10, 10, 10], Chris@87: [10, 10, 10, 10, 10, 10, 10], Chris@87: [10, 10, 0, 1, 2, 10, 10], Chris@87: [10, 10, 3, 4, 5, 10, 10], Chris@87: [10, 10, 10, 10, 10, 10, 10], Chris@87: [10, 10, 10, 10, 10, 10, 10]]) Chris@87: """ Chris@87: Chris@87: narray = np.array(array) Chris@87: pad_width = _validate_lengths(narray, pad_width) Chris@87: Chris@87: allowedkwargs = { Chris@87: 'constant': ['constant_values'], Chris@87: 'edge': [], Chris@87: 'linear_ramp': ['end_values'], Chris@87: 'maximum': ['stat_length'], Chris@87: 'mean': ['stat_length'], Chris@87: 'median': ['stat_length'], Chris@87: 'minimum': ['stat_length'], Chris@87: 'reflect': ['reflect_type'], Chris@87: 'symmetric': ['reflect_type'], Chris@87: 'wrap': [], Chris@87: } Chris@87: Chris@87: kwdefaults = { Chris@87: 'stat_length': None, Chris@87: 'constant_values': 0, Chris@87: 'end_values': 0, Chris@87: 'reflect_type': 'even', Chris@87: } Chris@87: Chris@87: if isinstance(mode, str): Chris@87: # Make sure have allowed kwargs appropriate for mode Chris@87: for key in kwargs: Chris@87: if key not in allowedkwargs[mode]: Chris@87: raise ValueError('%s keyword not in allowed keywords %s' % Chris@87: (key, allowedkwargs[mode])) Chris@87: Chris@87: # Set kwarg defaults Chris@87: for kw in allowedkwargs[mode]: Chris@87: kwargs.setdefault(kw, kwdefaults[kw]) Chris@87: Chris@87: # Need to only normalize particular keywords. Chris@87: for i in kwargs: Chris@87: if i == 'stat_length': Chris@87: kwargs[i] = _validate_lengths(narray, kwargs[i]) Chris@87: if i in ['end_values', 'constant_values']: Chris@87: kwargs[i] = _normalize_shape(narray, kwargs[i]) Chris@87: elif mode is None: Chris@87: raise ValueError('Keyword "mode" must be a function or one of %s.' % Chris@87: (list(allowedkwargs.keys()),)) Chris@87: else: Chris@87: # Drop back to old, slower np.apply_along_axis mode for user-supplied Chris@87: # vector function Chris@87: function = mode Chris@87: Chris@87: # Create a new padded array Chris@87: rank = list(range(len(narray.shape))) Chris@87: total_dim_increase = [np.sum(pad_width[i]) for i in rank] Chris@87: offset_slices = [slice(pad_width[i][0], Chris@87: pad_width[i][0] + narray.shape[i]) Chris@87: for i in rank] Chris@87: new_shape = np.array(narray.shape) + total_dim_increase Chris@87: newmat = np.zeros(new_shape, narray.dtype) Chris@87: Chris@87: # Insert the original array into the padded array Chris@87: newmat[offset_slices] = narray Chris@87: Chris@87: # This is the core of pad ... Chris@87: for iaxis in rank: Chris@87: np.apply_along_axis(function, Chris@87: iaxis, Chris@87: newmat, Chris@87: pad_width[iaxis], Chris@87: iaxis, Chris@87: kwargs) Chris@87: return newmat Chris@87: Chris@87: # If we get here, use new padding method Chris@87: newmat = narray.copy() Chris@87: Chris@87: # API preserved, but completely new algorithm which pads by building the Chris@87: # entire block to pad before/after `arr` with in one step, for each axis. Chris@87: if mode == 'constant': Chris@87: for axis, ((pad_before, pad_after), (before_val, after_val)) \ Chris@87: in enumerate(zip(pad_width, kwargs['constant_values'])): Chris@87: newmat = _prepend_const(newmat, pad_before, before_val, axis) Chris@87: newmat = _append_const(newmat, pad_after, after_val, axis) Chris@87: Chris@87: elif mode == 'edge': Chris@87: for axis, (pad_before, pad_after) in enumerate(pad_width): Chris@87: newmat = _prepend_edge(newmat, pad_before, axis) Chris@87: newmat = _append_edge(newmat, pad_after, axis) Chris@87: Chris@87: elif mode == 'linear_ramp': Chris@87: for axis, ((pad_before, pad_after), (before_val, after_val)) \ Chris@87: in enumerate(zip(pad_width, kwargs['end_values'])): Chris@87: newmat = _prepend_ramp(newmat, pad_before, before_val, axis) Chris@87: newmat = _append_ramp(newmat, pad_after, after_val, axis) Chris@87: Chris@87: elif mode == 'maximum': Chris@87: for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ Chris@87: in enumerate(zip(pad_width, kwargs['stat_length'])): Chris@87: newmat = _prepend_max(newmat, pad_before, chunk_before, axis) Chris@87: newmat = _append_max(newmat, pad_after, chunk_after, axis) Chris@87: Chris@87: elif mode == 'mean': Chris@87: for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ Chris@87: in enumerate(zip(pad_width, kwargs['stat_length'])): Chris@87: newmat = _prepend_mean(newmat, pad_before, chunk_before, axis) Chris@87: newmat = _append_mean(newmat, pad_after, chunk_after, axis) Chris@87: Chris@87: elif mode == 'median': Chris@87: for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ Chris@87: in enumerate(zip(pad_width, kwargs['stat_length'])): Chris@87: newmat = _prepend_med(newmat, pad_before, chunk_before, axis) Chris@87: newmat = _append_med(newmat, pad_after, chunk_after, axis) Chris@87: Chris@87: elif mode == 'minimum': Chris@87: for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ Chris@87: in enumerate(zip(pad_width, kwargs['stat_length'])): Chris@87: newmat = _prepend_min(newmat, pad_before, chunk_before, axis) Chris@87: newmat = _append_min(newmat, pad_after, chunk_after, axis) Chris@87: Chris@87: elif mode == 'reflect': Chris@87: for axis, (pad_before, pad_after) in enumerate(pad_width): Chris@87: # Recursive padding along any axis where `pad_amt` is too large Chris@87: # for indexing tricks. We can only safely pad the original axis Chris@87: # length, to keep the period of the reflections consistent. Chris@87: if ((pad_before > 0) or Chris@87: (pad_after > 0)) and newmat.shape[axis] == 1: Chris@87: # Extending singleton dimension for 'reflect' is legacy Chris@87: # behavior; it really should raise an error. Chris@87: newmat = _prepend_edge(newmat, pad_before, axis) Chris@87: newmat = _append_edge(newmat, pad_after, axis) Chris@87: continue Chris@87: Chris@87: method = kwargs['reflect_type'] Chris@87: safe_pad = newmat.shape[axis] - 1 Chris@87: while ((pad_before > safe_pad) or (pad_after > safe_pad)): Chris@87: offset = 0 Chris@87: pad_iter_b = min(safe_pad, Chris@87: safe_pad * (pad_before // safe_pad)) Chris@87: pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) Chris@87: newmat = _pad_ref(newmat, (pad_iter_b, Chris@87: pad_iter_a), method, axis) Chris@87: pad_before -= pad_iter_b Chris@87: pad_after -= pad_iter_a Chris@87: if pad_iter_b > 0: Chris@87: offset += 1 Chris@87: if pad_iter_a > 0: Chris@87: offset += 1 Chris@87: safe_pad += pad_iter_b + pad_iter_a Chris@87: newmat = _pad_ref(newmat, (pad_before, pad_after), method, axis) Chris@87: Chris@87: elif mode == 'symmetric': Chris@87: for axis, (pad_before, pad_after) in enumerate(pad_width): Chris@87: # Recursive padding along any axis where `pad_amt` is too large Chris@87: # for indexing tricks. We can only safely pad the original axis Chris@87: # length, to keep the period of the reflections consistent. Chris@87: method = kwargs['reflect_type'] Chris@87: safe_pad = newmat.shape[axis] Chris@87: while ((pad_before > safe_pad) or Chris@87: (pad_after > safe_pad)): Chris@87: pad_iter_b = min(safe_pad, Chris@87: safe_pad * (pad_before // safe_pad)) Chris@87: pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) Chris@87: newmat = _pad_sym(newmat, (pad_iter_b, Chris@87: pad_iter_a), method, axis) Chris@87: pad_before -= pad_iter_b Chris@87: pad_after -= pad_iter_a Chris@87: safe_pad += pad_iter_b + pad_iter_a Chris@87: newmat = _pad_sym(newmat, (pad_before, pad_after), method, axis) Chris@87: Chris@87: elif mode == 'wrap': Chris@87: for axis, (pad_before, pad_after) in enumerate(pad_width): Chris@87: # Recursive padding along any axis where `pad_amt` is too large Chris@87: # for indexing tricks. We can only safely pad the original axis Chris@87: # length, to keep the period of the reflections consistent. Chris@87: safe_pad = newmat.shape[axis] Chris@87: while ((pad_before > safe_pad) or Chris@87: (pad_after > safe_pad)): Chris@87: pad_iter_b = min(safe_pad, Chris@87: safe_pad * (pad_before // safe_pad)) Chris@87: pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) Chris@87: newmat = _pad_wrap(newmat, (pad_iter_b, pad_iter_a), axis) Chris@87: Chris@87: pad_before -= pad_iter_b Chris@87: pad_after -= pad_iter_a Chris@87: safe_pad += pad_iter_b + pad_iter_a Chris@87: newmat = _pad_wrap(newmat, (pad_before, pad_after), axis) Chris@87: Chris@87: return newmat