arraypad.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  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):
  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. Returns
  328. -------
  329. pad_amt : tuple of ints, length 2
  330. New index positions of padding to do along the `axis`. If these are
  331. both 0, padding is done in this dimension.
  332. """
  333. left_pad, right_pad = width_pair
  334. period = padded.shape[axis] - right_pad - left_pad
  335. # If the current dimension of `arr` doesn't contain enough valid values
  336. # (not part of the undefined pad area) we need to pad multiple times.
  337. # Each time the pad area shrinks on both sides which is communicated with
  338. # these variables.
  339. new_left_pad = 0
  340. new_right_pad = 0
  341. if left_pad > 0:
  342. # Pad with wrapped values on left side
  343. # First slice chunk from right side of the non-pad area.
  344. # Use min(period, left_pad) to ensure that chunk is not larger than
  345. # pad area
  346. right_slice = _slice_at_axis(
  347. slice(-right_pad - min(period, left_pad),
  348. -right_pad if right_pad != 0 else None),
  349. axis
  350. )
  351. right_chunk = padded[right_slice]
  352. if left_pad > period:
  353. # Chunk is smaller than pad area
  354. pad_area = _slice_at_axis(slice(left_pad - period, left_pad), axis)
  355. new_left_pad = left_pad - period
  356. else:
  357. # Chunk matches pad area
  358. pad_area = _slice_at_axis(slice(None, left_pad), axis)
  359. padded[pad_area] = right_chunk
  360. if right_pad > 0:
  361. # Pad with wrapped values on right side
  362. # First slice chunk from left side of the non-pad area.
  363. # Use min(period, right_pad) to ensure that chunk is not larger than
  364. # pad area
  365. left_slice = _slice_at_axis(
  366. slice(left_pad, left_pad + min(period, right_pad),), axis)
  367. left_chunk = padded[left_slice]
  368. if right_pad > period:
  369. # Chunk is smaller than pad area
  370. pad_area = _slice_at_axis(
  371. slice(-right_pad, -right_pad + period), axis)
  372. new_right_pad = right_pad - period
  373. else:
  374. # Chunk matches pad area
  375. pad_area = _slice_at_axis(slice(-right_pad, None), axis)
  376. padded[pad_area] = left_chunk
  377. return new_left_pad, new_right_pad
  378. def _as_pairs(x, ndim, as_index=False):
  379. """
  380. Broadcast `x` to an array with the shape (`ndim`, 2).
  381. A helper function for `pad` that prepares and validates arguments like
  382. `pad_width` for iteration in pairs.
  383. Parameters
  384. ----------
  385. x : {None, scalar, array-like}
  386. The object to broadcast to the shape (`ndim`, 2).
  387. ndim : int
  388. Number of pairs the broadcasted `x` will have.
  389. as_index : bool, optional
  390. If `x` is not None, try to round each element of `x` to an integer
  391. (dtype `np.intp`) and ensure every element is positive.
  392. Returns
  393. -------
  394. pairs : nested iterables, shape (`ndim`, 2)
  395. The broadcasted version of `x`.
  396. Raises
  397. ------
  398. ValueError
  399. If `as_index` is True and `x` contains negative elements.
  400. Or if `x` is not broadcastable to the shape (`ndim`, 2).
  401. """
  402. if x is None:
  403. # Pass through None as a special case, otherwise np.round(x) fails
  404. # with an AttributeError
  405. return ((None, None),) * ndim
  406. x = np.array(x)
  407. if as_index:
  408. x = np.round(x).astype(np.intp, copy=False)
  409. if x.ndim < 3:
  410. # Optimization: Possibly use faster paths for cases where `x` has
  411. # only 1 or 2 elements. `np.broadcast_to` could handle these as well
  412. # but is currently slower
  413. if x.size == 1:
  414. # x was supplied as a single value
  415. x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2
  416. if as_index and x < 0:
  417. raise ValueError("index can't contain negative values")
  418. return ((x[0], x[0]),) * ndim
  419. if x.size == 2 and x.shape != (2, 1):
  420. # x was supplied with a single value for each side
  421. # but except case when each dimension has a single value
  422. # which should be broadcasted to a pair,
  423. # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]]
  424. x = x.ravel() # Ensure x[0], x[1] works
  425. if as_index and (x[0] < 0 or x[1] < 0):
  426. raise ValueError("index can't contain negative values")
  427. return ((x[0], x[1]),) * ndim
  428. if as_index and x.min() < 0:
  429. raise ValueError("index can't contain negative values")
  430. # Converting the array with `tolist` seems to improve performance
  431. # when iterating and indexing the result (see usage in `pad`)
  432. return np.broadcast_to(x, (ndim, 2)).tolist()
  433. def _pad_dispatcher(array, pad_width, mode=None, **kwargs):
  434. return (array,)
  435. ###############################################################################
  436. # Public functions
  437. @array_function_dispatch(_pad_dispatcher, module='numpy')
  438. def pad(array, pad_width, mode='constant', **kwargs):
  439. """
  440. Pad an array.
  441. Parameters
  442. ----------
  443. array : array_like of rank N
  444. The array to pad.
  445. pad_width : {sequence, array_like, int}
  446. Number of values padded to the edges of each axis.
  447. ((before_1, after_1), ... (before_N, after_N)) unique pad widths
  448. for each axis.
  449. ((before, after),) yields same before and after pad for each axis.
  450. (pad,) or int is a shortcut for before = after = pad width for all
  451. axes.
  452. mode : str or function, optional
  453. One of the following string values or a user supplied function.
  454. 'constant' (default)
  455. Pads with a constant value.
  456. 'edge'
  457. Pads with the edge values of array.
  458. 'linear_ramp'
  459. Pads with the linear ramp between end_value and the
  460. array edge value.
  461. 'maximum'
  462. Pads with the maximum value of all or part of the
  463. vector along each axis.
  464. 'mean'
  465. Pads with the mean value of all or part of the
  466. vector along each axis.
  467. 'median'
  468. Pads with the median value of all or part of the
  469. vector along each axis.
  470. 'minimum'
  471. Pads with the minimum value of all or part of the
  472. vector along each axis.
  473. 'reflect'
  474. Pads with the reflection of the vector mirrored on
  475. the first and last values of the vector along each
  476. axis.
  477. 'symmetric'
  478. Pads with the reflection of the vector mirrored
  479. along the edge of the array.
  480. 'wrap'
  481. Pads with the wrap of the vector along the axis.
  482. The first values are used to pad the end and the
  483. end values are used to pad the beginning.
  484. 'empty'
  485. Pads with undefined values.
  486. .. versionadded:: 1.17
  487. <function>
  488. Padding function, see Notes.
  489. stat_length : sequence or int, optional
  490. Used in 'maximum', 'mean', 'median', and 'minimum'. Number of
  491. values at edge of each axis used to calculate the statistic value.
  492. ((before_1, after_1), ... (before_N, after_N)) unique statistic
  493. lengths for each axis.
  494. ((before, after),) yields same before and after statistic lengths
  495. for each axis.
  496. (stat_length,) or int is a shortcut for before = after = statistic
  497. length for all axes.
  498. Default is ``None``, to use the entire axis.
  499. constant_values : sequence or scalar, optional
  500. Used in 'constant'. The values to set the padded values for each
  501. axis.
  502. ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants
  503. for each axis.
  504. ``((before, after),)`` yields same before and after constants for each
  505. axis.
  506. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
  507. all axes.
  508. Default is 0.
  509. end_values : sequence or scalar, optional
  510. Used in 'linear_ramp'. The values used for the ending value of the
  511. linear_ramp and that will form the edge of the padded array.
  512. ``((before_1, after_1), ... (before_N, after_N))`` unique end values
  513. for each axis.
  514. ``((before, after),)`` yields same before and after end values for each
  515. axis.
  516. ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for
  517. all axes.
  518. Default is 0.
  519. reflect_type : {'even', 'odd'}, optional
  520. Used in 'reflect', and 'symmetric'. The 'even' style is the
  521. default with an unaltered reflection around the edge value. For
  522. the 'odd' style, the extended part of the array is created by
  523. subtracting the reflected values from two times the edge value.
  524. Returns
  525. -------
  526. pad : ndarray
  527. Padded array of rank equal to `array` with shape increased
  528. according to `pad_width`.
  529. Notes
  530. -----
  531. .. versionadded:: 1.7.0
  532. For an array with rank greater than 1, some of the padding of later
  533. axes is calculated from padding of previous axes. This is easiest to
  534. think about with a rank 2 array where the corners of the padded array
  535. are calculated by using padded values from the first axis.
  536. The padding function, if used, should modify a rank 1 array in-place. It
  537. has the following signature::
  538. padding_func(vector, iaxis_pad_width, iaxis, kwargs)
  539. where
  540. vector : ndarray
  541. A rank 1 array already padded with zeros. Padded values are
  542. vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:].
  543. iaxis_pad_width : tuple
  544. A 2-tuple of ints, iaxis_pad_width[0] represents the number of
  545. values padded at the beginning of vector where
  546. iaxis_pad_width[1] represents the number of values padded at
  547. the end of vector.
  548. iaxis : int
  549. The axis currently being calculated.
  550. kwargs : dict
  551. Any keyword arguments the function requires.
  552. Examples
  553. --------
  554. >>> a = [1, 2, 3, 4, 5]
  555. >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6))
  556. array([4, 4, 1, ..., 6, 6, 6])
  557. >>> np.pad(a, (2, 3), 'edge')
  558. array([1, 1, 1, ..., 5, 5, 5])
  559. >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
  560. array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4])
  561. >>> np.pad(a, (2,), 'maximum')
  562. array([5, 5, 1, 2, 3, 4, 5, 5, 5])
  563. >>> np.pad(a, (2,), 'mean')
  564. array([3, 3, 1, 2, 3, 4, 5, 3, 3])
  565. >>> np.pad(a, (2,), 'median')
  566. array([3, 3, 1, 2, 3, 4, 5, 3, 3])
  567. >>> a = [[1, 2], [3, 4]]
  568. >>> np.pad(a, ((3, 2), (2, 3)), 'minimum')
  569. array([[1, 1, 1, 2, 1, 1, 1],
  570. [1, 1, 1, 2, 1, 1, 1],
  571. [1, 1, 1, 2, 1, 1, 1],
  572. [1, 1, 1, 2, 1, 1, 1],
  573. [3, 3, 3, 4, 3, 3, 3],
  574. [1, 1, 1, 2, 1, 1, 1],
  575. [1, 1, 1, 2, 1, 1, 1]])
  576. >>> a = [1, 2, 3, 4, 5]
  577. >>> np.pad(a, (2, 3), 'reflect')
  578. array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])
  579. >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd')
  580. array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
  581. >>> np.pad(a, (2, 3), 'symmetric')
  582. array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])
  583. >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd')
  584. array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])
  585. >>> np.pad(a, (2, 3), 'wrap')
  586. array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])
  587. >>> def pad_with(vector, pad_width, iaxis, kwargs):
  588. ... pad_value = kwargs.get('padder', 10)
  589. ... vector[:pad_width[0]] = pad_value
  590. ... vector[-pad_width[1]:] = pad_value
  591. >>> a = np.arange(6)
  592. >>> a = a.reshape((2, 3))
  593. >>> np.pad(a, 2, pad_with)
  594. array([[10, 10, 10, 10, 10, 10, 10],
  595. [10, 10, 10, 10, 10, 10, 10],
  596. [10, 10, 0, 1, 2, 10, 10],
  597. [10, 10, 3, 4, 5, 10, 10],
  598. [10, 10, 10, 10, 10, 10, 10],
  599. [10, 10, 10, 10, 10, 10, 10]])
  600. >>> np.pad(a, 2, pad_with, padder=100)
  601. array([[100, 100, 100, 100, 100, 100, 100],
  602. [100, 100, 100, 100, 100, 100, 100],
  603. [100, 100, 0, 1, 2, 100, 100],
  604. [100, 100, 3, 4, 5, 100, 100],
  605. [100, 100, 100, 100, 100, 100, 100],
  606. [100, 100, 100, 100, 100, 100, 100]])
  607. """
  608. array = np.asarray(array)
  609. pad_width = np.asarray(pad_width)
  610. if not pad_width.dtype.kind == 'i':
  611. raise TypeError('`pad_width` must be of integral type.')
  612. # Broadcast to shape (array.ndim, 2)
  613. pad_width = _as_pairs(pad_width, array.ndim, as_index=True)
  614. if callable(mode):
  615. # Old behavior: Use user-supplied function with np.apply_along_axis
  616. function = mode
  617. # Create a new zero padded array
  618. padded, _ = _pad_simple(array, pad_width, fill_value=0)
  619. # And apply along each axis
  620. for axis in range(padded.ndim):
  621. # Iterate using ndindex as in apply_along_axis, but assuming that
  622. # function operates inplace on the padded array.
  623. # view with the iteration axis at the end
  624. view = np.moveaxis(padded, axis, -1)
  625. # compute indices for the iteration axes, and append a trailing
  626. # ellipsis to prevent 0d arrays decaying to scalars (gh-8642)
  627. inds = ndindex(view.shape[:-1])
  628. inds = (ind + (Ellipsis,) for ind in inds)
  629. for ind in inds:
  630. function(view[ind], pad_width[axis], axis, kwargs)
  631. return padded
  632. # Make sure that no unsupported keywords were passed for the current mode
  633. allowed_kwargs = {
  634. 'empty': [], 'edge': [], 'wrap': [],
  635. 'constant': ['constant_values'],
  636. 'linear_ramp': ['end_values'],
  637. 'maximum': ['stat_length'],
  638. 'mean': ['stat_length'],
  639. 'median': ['stat_length'],
  640. 'minimum': ['stat_length'],
  641. 'reflect': ['reflect_type'],
  642. 'symmetric': ['reflect_type'],
  643. }
  644. try:
  645. unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode])
  646. except KeyError:
  647. raise ValueError("mode '{}' is not supported".format(mode)) from None
  648. if unsupported_kwargs:
  649. raise ValueError("unsupported keyword arguments for mode '{}': {}"
  650. .format(mode, unsupported_kwargs))
  651. stat_functions = {"maximum": np.amax, "minimum": np.amin,
  652. "mean": np.mean, "median": np.median}
  653. # Create array with final shape and original values
  654. # (padded area is undefined)
  655. padded, original_area_slice = _pad_simple(array, pad_width)
  656. # And prepare iteration over all dimensions
  657. # (zipping may be more readable than using enumerate)
  658. axes = range(padded.ndim)
  659. if mode == "constant":
  660. values = kwargs.get("constant_values", 0)
  661. values = _as_pairs(values, padded.ndim)
  662. for axis, width_pair, value_pair in zip(axes, pad_width, values):
  663. roi = _view_roi(padded, original_area_slice, axis)
  664. _set_pad_area(roi, axis, width_pair, value_pair)
  665. elif mode == "empty":
  666. pass # Do nothing as _pad_simple already returned the correct result
  667. elif array.size == 0:
  668. # Only modes "constant" and "empty" can extend empty axes, all other
  669. # modes depend on `array` not being empty
  670. # -> ensure every empty axis is only "padded with 0"
  671. for axis, width_pair in zip(axes, pad_width):
  672. if array.shape[axis] == 0 and any(width_pair):
  673. raise ValueError(
  674. "can't extend empty axis {} using modes other than "
  675. "'constant' or 'empty'".format(axis)
  676. )
  677. # passed, don't need to do anything more as _pad_simple already
  678. # returned the correct result
  679. elif mode == "edge":
  680. for axis, width_pair in zip(axes, pad_width):
  681. roi = _view_roi(padded, original_area_slice, axis)
  682. edge_pair = _get_edges(roi, axis, width_pair)
  683. _set_pad_area(roi, axis, width_pair, edge_pair)
  684. elif mode == "linear_ramp":
  685. end_values = kwargs.get("end_values", 0)
  686. end_values = _as_pairs(end_values, padded.ndim)
  687. for axis, width_pair, value_pair in zip(axes, pad_width, end_values):
  688. roi = _view_roi(padded, original_area_slice, axis)
  689. ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair)
  690. _set_pad_area(roi, axis, width_pair, ramp_pair)
  691. elif mode in stat_functions:
  692. func = stat_functions[mode]
  693. length = kwargs.get("stat_length", None)
  694. length = _as_pairs(length, padded.ndim, as_index=True)
  695. for axis, width_pair, length_pair in zip(axes, pad_width, length):
  696. roi = _view_roi(padded, original_area_slice, axis)
  697. stat_pair = _get_stats(roi, axis, width_pair, length_pair, func)
  698. _set_pad_area(roi, axis, width_pair, stat_pair)
  699. elif mode in {"reflect", "symmetric"}:
  700. method = kwargs.get("reflect_type", "even")
  701. include_edge = True if mode == "symmetric" else False
  702. for axis, (left_index, right_index) in zip(axes, pad_width):
  703. if array.shape[axis] == 1 and (left_index > 0 or right_index > 0):
  704. # Extending singleton dimension for 'reflect' is legacy
  705. # behavior; it really should raise an error.
  706. edge_pair = _get_edges(padded, axis, (left_index, right_index))
  707. _set_pad_area(
  708. padded, axis, (left_index, right_index), edge_pair)
  709. continue
  710. roi = _view_roi(padded, original_area_slice, axis)
  711. while left_index > 0 or right_index > 0:
  712. # Iteratively pad until dimension is filled with reflected
  713. # values. This is necessary if the pad area is larger than
  714. # the length of the original values in the current dimension.
  715. left_index, right_index = _set_reflect_both(
  716. roi, axis, (left_index, right_index),
  717. method, include_edge
  718. )
  719. elif mode == "wrap":
  720. for axis, (left_index, right_index) in zip(axes, pad_width):
  721. roi = _view_roi(padded, original_area_slice, axis)
  722. while left_index > 0 or right_index > 0:
  723. # Iteratively pad until dimension is filled with wrapped
  724. # values. This is necessary if the pad area is larger than
  725. # the length of the original values in the current dimension.
  726. left_index, right_index = _set_wrap_both(
  727. roi, axis, (left_index, right_index))
  728. return padded