arraypad.py 31 KB

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