_arraypad_impl.py 32 KB

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