arraypad.py 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  1. """
  2. The arraypad module contains a group of functions to pad values onto the edges
  3. of an n-dimensional array.
  4. """
  5. from __future__ import division, absolute_import, print_function
  6. import numpy as np
  7. from numpy.core.overrides import array_function_dispatch
  8. __all__ = ['pad']
  9. ###############################################################################
  10. # Private utility functions.
  11. def _arange_ndarray(arr, shape, axis, reverse=False):
  12. """
  13. Create an ndarray of `shape` with increments along specified `axis`
  14. Parameters
  15. ----------
  16. arr : ndarray
  17. Input array of arbitrary shape.
  18. shape : tuple of ints
  19. Shape of desired array. Should be equivalent to `arr.shape` except
  20. `shape[axis]` which may have any positive value.
  21. axis : int
  22. Axis to increment along.
  23. reverse : bool
  24. If False, increment in a positive fashion from 1 to `shape[axis]`,
  25. inclusive. If True, the bounds are the same but the order reversed.
  26. Returns
  27. -------
  28. padarr : ndarray
  29. Output array sized to pad `arr` along `axis`, with linear range from
  30. 1 to `shape[axis]` along specified `axis`.
  31. Notes
  32. -----
  33. The range is deliberately 1-indexed for this specific use case. Think of
  34. this algorithm as broadcasting `np.arange` to a single `axis` of an
  35. arbitrarily shaped ndarray.
  36. """
  37. initshape = tuple(1 if i != axis else shape[axis]
  38. for (i, x) in enumerate(arr.shape))
  39. if not reverse:
  40. padarr = np.arange(1, shape[axis] + 1)
  41. else:
  42. padarr = np.arange(shape[axis], 0, -1)
  43. padarr = padarr.reshape(initshape)
  44. for i, dim in enumerate(shape):
  45. if padarr.shape[i] != dim:
  46. padarr = padarr.repeat(dim, axis=i)
  47. return padarr
  48. def _round_ifneeded(arr, dtype):
  49. """
  50. Rounds arr inplace if destination dtype is integer.
  51. Parameters
  52. ----------
  53. arr : ndarray
  54. Input array.
  55. dtype : dtype
  56. The dtype of the destination array.
  57. """
  58. if np.issubdtype(dtype, np.integer):
  59. arr.round(out=arr)
  60. def _slice_at_axis(shape, sl, axis):
  61. """
  62. Construct a slice tuple the length of shape, with sl at the specified axis
  63. """
  64. slice_tup = (slice(None),)
  65. return slice_tup * axis + (sl,) + slice_tup * (len(shape) - axis - 1)
  66. def _slice_first(shape, n, axis):
  67. """ Construct a slice tuple to take the first n elements along axis """
  68. return _slice_at_axis(shape, slice(0, n), axis=axis)
  69. def _slice_last(shape, n, axis):
  70. """ Construct a slice tuple to take the last n elements along axis """
  71. dim = shape[axis] # doing this explicitly makes n=0 work
  72. return _slice_at_axis(shape, slice(dim - n, dim), axis=axis)
  73. def _do_prepend(arr, pad_chunk, axis):
  74. return np.concatenate(
  75. (pad_chunk.astype(arr.dtype, copy=False), arr), axis=axis)
  76. def _do_append(arr, pad_chunk, axis):
  77. return np.concatenate(
  78. (arr, pad_chunk.astype(arr.dtype, copy=False)), axis=axis)
  79. def _prepend_const(arr, pad_amt, val, axis=-1):
  80. """
  81. Prepend constant `val` along `axis` of `arr`.
  82. Parameters
  83. ----------
  84. arr : ndarray
  85. Input array of arbitrary shape.
  86. pad_amt : int
  87. Amount of padding to prepend.
  88. val : scalar
  89. Constant value to use. For best results should be of type `arr.dtype`;
  90. if not `arr.dtype` will be cast to `arr.dtype`.
  91. axis : int
  92. Axis along which to pad `arr`.
  93. Returns
  94. -------
  95. padarr : ndarray
  96. Output array, with `pad_amt` constant `val` prepended along `axis`.
  97. """
  98. if pad_amt == 0:
  99. return arr
  100. padshape = tuple(x if i != axis else pad_amt
  101. for (i, x) in enumerate(arr.shape))
  102. return _do_prepend(arr, np.full(padshape, val, dtype=arr.dtype), axis)
  103. def _append_const(arr, pad_amt, val, axis=-1):
  104. """
  105. Append constant `val` along `axis` of `arr`.
  106. Parameters
  107. ----------
  108. arr : ndarray
  109. Input array of arbitrary shape.
  110. pad_amt : int
  111. Amount of padding to append.
  112. val : scalar
  113. Constant value to use. For best results should be of type `arr.dtype`;
  114. if not `arr.dtype` will be cast to `arr.dtype`.
  115. axis : int
  116. Axis along which to pad `arr`.
  117. Returns
  118. -------
  119. padarr : ndarray
  120. Output array, with `pad_amt` constant `val` appended along `axis`.
  121. """
  122. if pad_amt == 0:
  123. return arr
  124. padshape = tuple(x if i != axis else pad_amt
  125. for (i, x) in enumerate(arr.shape))
  126. return _do_append(arr, np.full(padshape, val, dtype=arr.dtype), axis)
  127. def _prepend_edge(arr, pad_amt, axis=-1):
  128. """
  129. Prepend `pad_amt` to `arr` along `axis` by extending edge values.
  130. Parameters
  131. ----------
  132. arr : ndarray
  133. Input array of arbitrary shape.
  134. pad_amt : int
  135. Amount of padding to prepend.
  136. axis : int
  137. Axis along which to pad `arr`.
  138. Returns
  139. -------
  140. padarr : ndarray
  141. Output array, extended by `pad_amt` edge values appended along `axis`.
  142. """
  143. if pad_amt == 0:
  144. return arr
  145. edge_slice = _slice_first(arr.shape, 1, axis=axis)
  146. edge_arr = arr[edge_slice]
  147. return _do_prepend(arr, edge_arr.repeat(pad_amt, axis=axis), axis)
  148. def _append_edge(arr, pad_amt, axis=-1):
  149. """
  150. Append `pad_amt` to `arr` along `axis` by extending edge values.
  151. Parameters
  152. ----------
  153. arr : ndarray
  154. Input array of arbitrary shape.
  155. pad_amt : int
  156. Amount of padding to append.
  157. axis : int
  158. Axis along which to pad `arr`.
  159. Returns
  160. -------
  161. padarr : ndarray
  162. Output array, extended by `pad_amt` edge values prepended along
  163. `axis`.
  164. """
  165. if pad_amt == 0:
  166. return arr
  167. edge_slice = _slice_last(arr.shape, 1, axis=axis)
  168. edge_arr = arr[edge_slice]
  169. return _do_append(arr, edge_arr.repeat(pad_amt, axis=axis), axis)
  170. def _prepend_ramp(arr, pad_amt, end, axis=-1):
  171. """
  172. Prepend linear ramp along `axis`.
  173. Parameters
  174. ----------
  175. arr : ndarray
  176. Input array of arbitrary shape.
  177. pad_amt : int
  178. Amount of padding to prepend.
  179. end : scalar
  180. Constal value to use. For best results should be of type `arr.dtype`;
  181. if not `arr.dtype` will be cast to `arr.dtype`.
  182. axis : int
  183. Axis along which to pad `arr`.
  184. Returns
  185. -------
  186. padarr : ndarray
  187. Output array, with `pad_amt` values prepended along `axis`. The
  188. prepended region ramps linearly from the edge value to `end`.
  189. """
  190. if pad_amt == 0:
  191. return arr
  192. # Slice a chunk from the edge to calculate stats on and extract edge
  193. edge_slice = _slice_first(arr.shape, 1, axis=axis)
  194. edge = arr[edge_slice]
  195. ramp_arr = np.linspace(
  196. start=end,
  197. stop=edge.squeeze(axis),
  198. num=pad_amt,
  199. endpoint=False,
  200. dtype=arr.dtype,
  201. axis=axis
  202. )
  203. return _do_prepend(arr, ramp_arr, axis)
  204. def _append_ramp(arr, pad_amt, end, axis=-1):
  205. """
  206. Append linear ramp along `axis`.
  207. Parameters
  208. ----------
  209. arr : ndarray
  210. Input array of arbitrary shape.
  211. pad_amt : int
  212. Amount of padding to append.
  213. end : scalar
  214. Constal value to use. For best results should be of type `arr.dtype`;
  215. if not `arr.dtype` will be cast to `arr.dtype`.
  216. axis : int
  217. Axis along which to pad `arr`.
  218. Returns
  219. -------
  220. padarr : ndarray
  221. Output array, with `pad_amt` values appended along `axis`. The
  222. appended region ramps linearly from the edge value to `end`.
  223. """
  224. if pad_amt == 0:
  225. return arr
  226. # Slice a chunk from the edge to calculate stats on and extract edge
  227. edge_slice = _slice_last(arr.shape, 1, axis=axis)
  228. edge = arr[edge_slice]
  229. ramp_arr = np.linspace(
  230. start=end,
  231. stop=edge.squeeze(axis),
  232. num=pad_amt,
  233. endpoint=False,
  234. dtype=arr.dtype,
  235. axis=axis
  236. )
  237. # Reverse linear space in appropriate dimension
  238. ramp_arr = ramp_arr[
  239. _slice_at_axis(ramp_arr.shape, slice(None, None, -1), axis)
  240. ]
  241. return _do_append(arr, ramp_arr, axis)
  242. def _prepend_max(arr, pad_amt, num, axis=-1):
  243. """
  244. Prepend `pad_amt` maximum values along `axis`.
  245. Parameters
  246. ----------
  247. arr : ndarray
  248. Input array of arbitrary shape.
  249. pad_amt : int
  250. Amount of padding to prepend.
  251. num : int
  252. Depth into `arr` along `axis` to calculate maximum.
  253. Range: [1, `arr.shape[axis]`] or None (entire axis)
  254. axis : int
  255. Axis along which to pad `arr`.
  256. Returns
  257. -------
  258. padarr : ndarray
  259. Output array, with `pad_amt` values appended along `axis`. The
  260. prepended region is the maximum of the first `num` values along
  261. `axis`.
  262. """
  263. if pad_amt == 0:
  264. return arr
  265. # Equivalent to edge padding for single value, so do that instead
  266. if num == 1:
  267. return _prepend_edge(arr, pad_amt, axis)
  268. # Use entire array if `num` is too large
  269. if num is not None:
  270. if num >= arr.shape[axis]:
  271. num = None
  272. # Slice a chunk from the edge to calculate stats on
  273. max_slice = _slice_first(arr.shape, num, axis=axis)
  274. # Extract slice, calculate max
  275. max_chunk = arr[max_slice].max(axis=axis, keepdims=True)
  276. # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt`
  277. return _do_prepend(arr, max_chunk.repeat(pad_amt, axis=axis), axis)
  278. def _append_max(arr, pad_amt, num, axis=-1):
  279. """
  280. Pad one `axis` of `arr` with the maximum of the last `num` elements.
  281. Parameters
  282. ----------
  283. arr : ndarray
  284. Input array of arbitrary shape.
  285. pad_amt : int
  286. Amount of padding to append.
  287. num : int
  288. Depth into `arr` along `axis` to calculate maximum.
  289. Range: [1, `arr.shape[axis]`] or None (entire axis)
  290. axis : int
  291. Axis along which to pad `arr`.
  292. Returns
  293. -------
  294. padarr : ndarray
  295. Output array, with `pad_amt` values appended along `axis`. The
  296. appended region is the maximum of the final `num` values along `axis`.
  297. """
  298. if pad_amt == 0:
  299. return arr
  300. # Equivalent to edge padding for single value, so do that instead
  301. if num == 1:
  302. return _append_edge(arr, pad_amt, axis)
  303. # Use entire array if `num` is too large
  304. if num is not None:
  305. if num >= arr.shape[axis]:
  306. num = None
  307. # Slice a chunk from the edge to calculate stats on
  308. if num is not None:
  309. max_slice = _slice_last(arr.shape, num, axis=axis)
  310. else:
  311. max_slice = tuple(slice(None) for x in arr.shape)
  312. # Extract slice, calculate max
  313. max_chunk = arr[max_slice].max(axis=axis, keepdims=True)
  314. # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt`
  315. return _do_append(arr, max_chunk.repeat(pad_amt, axis=axis), axis)
  316. def _prepend_mean(arr, pad_amt, num, axis=-1):
  317. """
  318. Prepend `pad_amt` mean values along `axis`.
  319. Parameters
  320. ----------
  321. arr : ndarray
  322. Input array of arbitrary shape.
  323. pad_amt : int
  324. Amount of padding to prepend.
  325. num : int
  326. Depth into `arr` along `axis` to calculate mean.
  327. Range: [1, `arr.shape[axis]`] or None (entire axis)
  328. axis : int
  329. Axis along which to pad `arr`.
  330. Returns
  331. -------
  332. padarr : ndarray
  333. Output array, with `pad_amt` values prepended along `axis`. The
  334. prepended region is the mean of the first `num` values along `axis`.
  335. """
  336. if pad_amt == 0:
  337. return arr
  338. # Equivalent to edge padding for single value, so do that instead
  339. if num == 1:
  340. return _prepend_edge(arr, pad_amt, axis)
  341. # Use entire array if `num` is too large
  342. if num is not None:
  343. if num >= arr.shape[axis]:
  344. num = None
  345. # Slice a chunk from the edge to calculate stats on
  346. mean_slice = _slice_first(arr.shape, num, axis=axis)
  347. # Extract slice, calculate mean
  348. mean_chunk = arr[mean_slice].mean(axis, keepdims=True)
  349. _round_ifneeded(mean_chunk, arr.dtype)
  350. # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt`
  351. return _do_prepend(arr, mean_chunk.repeat(pad_amt, axis), axis=axis)
  352. def _append_mean(arr, pad_amt, num, axis=-1):
  353. """
  354. Append `pad_amt` mean values along `axis`.
  355. Parameters
  356. ----------
  357. arr : ndarray
  358. Input array of arbitrary shape.
  359. pad_amt : int
  360. Amount of padding to append.
  361. num : int
  362. Depth into `arr` along `axis` to calculate mean.
  363. Range: [1, `arr.shape[axis]`] or None (entire axis)
  364. axis : int
  365. Axis along which to pad `arr`.
  366. Returns
  367. -------
  368. padarr : ndarray
  369. Output array, with `pad_amt` values appended along `axis`. The
  370. appended region is the maximum of the final `num` values along `axis`.
  371. """
  372. if pad_amt == 0:
  373. return arr
  374. # Equivalent to edge padding for single value, so do that instead
  375. if num == 1:
  376. return _append_edge(arr, pad_amt, axis)
  377. # Use entire array if `num` is too large
  378. if num is not None:
  379. if num >= arr.shape[axis]:
  380. num = None
  381. # Slice a chunk from the edge to calculate stats on
  382. if num is not None:
  383. mean_slice = _slice_last(arr.shape, num, axis=axis)
  384. else:
  385. mean_slice = tuple(slice(None) for x in arr.shape)
  386. # Extract slice, calculate mean
  387. mean_chunk = arr[mean_slice].mean(axis=axis, keepdims=True)
  388. _round_ifneeded(mean_chunk, arr.dtype)
  389. # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt`
  390. return _do_append(arr, mean_chunk.repeat(pad_amt, axis), axis=axis)
  391. def _prepend_med(arr, pad_amt, num, axis=-1):
  392. """
  393. Prepend `pad_amt` median values along `axis`.
  394. Parameters
  395. ----------
  396. arr : ndarray
  397. Input array of arbitrary shape.
  398. pad_amt : int
  399. Amount of padding to prepend.
  400. num : int
  401. Depth into `arr` along `axis` to calculate median.
  402. Range: [1, `arr.shape[axis]`] or None (entire axis)
  403. axis : int
  404. Axis along which to pad `arr`.
  405. Returns
  406. -------
  407. padarr : ndarray
  408. Output array, with `pad_amt` values prepended along `axis`. The
  409. prepended region is the median of the first `num` values along `axis`.
  410. """
  411. if pad_amt == 0:
  412. return arr
  413. # Equivalent to edge padding for single value, so do that instead
  414. if num == 1:
  415. return _prepend_edge(arr, pad_amt, axis)
  416. # Use entire array if `num` is too large
  417. if num is not None:
  418. if num >= arr.shape[axis]:
  419. num = None
  420. # Slice a chunk from the edge to calculate stats on
  421. med_slice = _slice_first(arr.shape, num, axis=axis)
  422. # Extract slice, calculate median
  423. med_chunk = np.median(arr[med_slice], axis=axis, keepdims=True)
  424. _round_ifneeded(med_chunk, arr.dtype)
  425. # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt`
  426. return _do_prepend(arr, med_chunk.repeat(pad_amt, axis), axis=axis)
  427. def _append_med(arr, pad_amt, num, axis=-1):
  428. """
  429. Append `pad_amt` median values along `axis`.
  430. Parameters
  431. ----------
  432. arr : ndarray
  433. Input array of arbitrary shape.
  434. pad_amt : int
  435. Amount of padding to append.
  436. num : int
  437. Depth into `arr` along `axis` to calculate median.
  438. Range: [1, `arr.shape[axis]`] or None (entire axis)
  439. axis : int
  440. Axis along which to pad `arr`.
  441. Returns
  442. -------
  443. padarr : ndarray
  444. Output array, with `pad_amt` values appended along `axis`. The
  445. appended region is the median of the final `num` values along `axis`.
  446. """
  447. if pad_amt == 0:
  448. return arr
  449. # Equivalent to edge padding for single value, so do that instead
  450. if num == 1:
  451. return _append_edge(arr, pad_amt, axis)
  452. # Use entire array if `num` is too large
  453. if num is not None:
  454. if num >= arr.shape[axis]:
  455. num = None
  456. # Slice a chunk from the edge to calculate stats on
  457. if num is not None:
  458. med_slice = _slice_last(arr.shape, num, axis=axis)
  459. else:
  460. med_slice = tuple(slice(None) for x in arr.shape)
  461. # Extract slice, calculate median
  462. med_chunk = np.median(arr[med_slice], axis=axis, keepdims=True)
  463. _round_ifneeded(med_chunk, arr.dtype)
  464. # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt`
  465. return _do_append(arr, med_chunk.repeat(pad_amt, axis), axis=axis)
  466. def _prepend_min(arr, pad_amt, num, axis=-1):
  467. """
  468. Prepend `pad_amt` minimum values along `axis`.
  469. Parameters
  470. ----------
  471. arr : ndarray
  472. Input array of arbitrary shape.
  473. pad_amt : int
  474. Amount of padding to prepend.
  475. num : int
  476. Depth into `arr` along `axis` to calculate minimum.
  477. Range: [1, `arr.shape[axis]`] or None (entire axis)
  478. axis : int
  479. Axis along which to pad `arr`.
  480. Returns
  481. -------
  482. padarr : ndarray
  483. Output array, with `pad_amt` values prepended along `axis`. The
  484. prepended region is the minimum of the first `num` values along
  485. `axis`.
  486. """
  487. if pad_amt == 0:
  488. return arr
  489. # Equivalent to edge padding for single value, so do that instead
  490. if num == 1:
  491. return _prepend_edge(arr, pad_amt, axis)
  492. # Use entire array if `num` is too large
  493. if num is not None:
  494. if num >= arr.shape[axis]:
  495. num = None
  496. # Slice a chunk from the edge to calculate stats on
  497. min_slice = _slice_first(arr.shape, num, axis=axis)
  498. # Extract slice, calculate min
  499. min_chunk = arr[min_slice].min(axis=axis, keepdims=True)
  500. # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt`
  501. return _do_prepend(arr, min_chunk.repeat(pad_amt, axis), axis=axis)
  502. def _append_min(arr, pad_amt, num, axis=-1):
  503. """
  504. Append `pad_amt` median values along `axis`.
  505. Parameters
  506. ----------
  507. arr : ndarray
  508. Input array of arbitrary shape.
  509. pad_amt : int
  510. Amount of padding to append.
  511. num : int
  512. Depth into `arr` along `axis` to calculate minimum.
  513. Range: [1, `arr.shape[axis]`] or None (entire axis)
  514. axis : int
  515. Axis along which to pad `arr`.
  516. Returns
  517. -------
  518. padarr : ndarray
  519. Output array, with `pad_amt` values appended along `axis`. The
  520. appended region is the minimum of the final `num` values along `axis`.
  521. """
  522. if pad_amt == 0:
  523. return arr
  524. # Equivalent to edge padding for single value, so do that instead
  525. if num == 1:
  526. return _append_edge(arr, pad_amt, axis)
  527. # Use entire array if `num` is too large
  528. if num is not None:
  529. if num >= arr.shape[axis]:
  530. num = None
  531. # Slice a chunk from the edge to calculate stats on
  532. if num is not None:
  533. min_slice = _slice_last(arr.shape, num, axis=axis)
  534. else:
  535. min_slice = tuple(slice(None) for x in arr.shape)
  536. # Extract slice, calculate min
  537. min_chunk = arr[min_slice].min(axis=axis, keepdims=True)
  538. # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt`
  539. return _do_append(arr, min_chunk.repeat(pad_amt, axis), axis=axis)
  540. def _pad_ref(arr, pad_amt, method, axis=-1):
  541. """
  542. Pad `axis` of `arr` by reflection.
  543. Parameters
  544. ----------
  545. arr : ndarray
  546. Input array of arbitrary shape.
  547. pad_amt : tuple of ints, length 2
  548. Padding to (prepend, append) along `axis`.
  549. method : str
  550. Controls method of reflection; options are 'even' or 'odd'.
  551. axis : int
  552. Axis along which to pad `arr`.
  553. Returns
  554. -------
  555. padarr : ndarray
  556. Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
  557. values appended along `axis`. Both regions are padded with reflected
  558. values from the original array.
  559. Notes
  560. -----
  561. This algorithm does not pad with repetition, i.e. the edges are not
  562. repeated in the reflection. For that behavior, use `mode='symmetric'`.
  563. The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
  564. single function, lest the indexing tricks in non-integer multiples of the
  565. original shape would violate repetition in the final iteration.
  566. """
  567. # Implicit booleanness to test for zero (or None) in any scalar type
  568. if pad_amt[0] == 0 and pad_amt[1] == 0:
  569. return arr
  570. ##########################################################################
  571. # Prepended region
  572. # Slice off a reverse indexed chunk from near edge to pad `arr` before
  573. ref_slice = _slice_at_axis(arr.shape, slice(pad_amt[0], 0, -1), axis=axis)
  574. ref_chunk1 = arr[ref_slice]
  575. # Memory/computationally more expensive, only do this if `method='odd'`
  576. if 'odd' in method and pad_amt[0] > 0:
  577. edge_slice1 = _slice_first(arr.shape, 1, axis=axis)
  578. edge_chunk = arr[edge_slice1]
  579. ref_chunk1 = 2 * edge_chunk - ref_chunk1
  580. del edge_chunk
  581. ##########################################################################
  582. # Appended region
  583. # Slice off a reverse indexed chunk from far edge to pad `arr` after
  584. start = arr.shape[axis] - pad_amt[1] - 1
  585. end = arr.shape[axis] - 1
  586. ref_slice = _slice_at_axis(arr.shape, slice(start, end), axis=axis)
  587. rev_idx = _slice_at_axis(arr.shape, slice(None, None, -1), axis=axis)
  588. ref_chunk2 = arr[ref_slice][rev_idx]
  589. if 'odd' in method:
  590. edge_slice2 = _slice_last(arr.shape, 1, axis=axis)
  591. edge_chunk = arr[edge_slice2]
  592. ref_chunk2 = 2 * edge_chunk - ref_chunk2
  593. del edge_chunk
  594. # Concatenate `arr` with both chunks, extending along `axis`
  595. return np.concatenate((ref_chunk1, arr, ref_chunk2), axis=axis)
  596. def _pad_sym(arr, pad_amt, method, axis=-1):
  597. """
  598. Pad `axis` of `arr` by symmetry.
  599. Parameters
  600. ----------
  601. arr : ndarray
  602. Input array of arbitrary shape.
  603. pad_amt : tuple of ints, length 2
  604. Padding to (prepend, append) along `axis`.
  605. method : str
  606. Controls method of symmetry; options are 'even' or 'odd'.
  607. axis : int
  608. Axis along which to pad `arr`.
  609. Returns
  610. -------
  611. padarr : ndarray
  612. Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
  613. values appended along `axis`. Both regions are padded with symmetric
  614. values from the original array.
  615. Notes
  616. -----
  617. This algorithm DOES pad with repetition, i.e. the edges are repeated.
  618. For padding without repeated edges, use `mode='reflect'`.
  619. The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
  620. single function, lest the indexing tricks in non-integer multiples of the
  621. original shape would violate repetition in the final iteration.
  622. """
  623. # Implicit booleanness to test for zero (or None) in any scalar type
  624. if pad_amt[0] == 0 and pad_amt[1] == 0:
  625. return arr
  626. ##########################################################################
  627. # Prepended region
  628. # Slice off a reverse indexed chunk from near edge to pad `arr` before
  629. sym_slice = _slice_first(arr.shape, pad_amt[0], axis=axis)
  630. rev_idx = _slice_at_axis(arr.shape, slice(None, None, -1), axis=axis)
  631. sym_chunk1 = arr[sym_slice][rev_idx]
  632. # Memory/computationally more expensive, only do this if `method='odd'`
  633. if 'odd' in method and pad_amt[0] > 0:
  634. edge_slice1 = _slice_first(arr.shape, 1, axis=axis)
  635. edge_chunk = arr[edge_slice1]
  636. sym_chunk1 = 2 * edge_chunk - sym_chunk1
  637. del edge_chunk
  638. ##########################################################################
  639. # Appended region
  640. # Slice off a reverse indexed chunk from far edge to pad `arr` after
  641. sym_slice = _slice_last(arr.shape, pad_amt[1], axis=axis)
  642. sym_chunk2 = arr[sym_slice][rev_idx]
  643. if 'odd' in method:
  644. edge_slice2 = _slice_last(arr.shape, 1, axis=axis)
  645. edge_chunk = arr[edge_slice2]
  646. sym_chunk2 = 2 * edge_chunk - sym_chunk2
  647. del edge_chunk
  648. # Concatenate `arr` with both chunks, extending along `axis`
  649. return np.concatenate((sym_chunk1, arr, sym_chunk2), axis=axis)
  650. def _pad_wrap(arr, pad_amt, axis=-1):
  651. """
  652. Pad `axis` of `arr` via wrapping.
  653. Parameters
  654. ----------
  655. arr : ndarray
  656. Input array of arbitrary shape.
  657. pad_amt : tuple of ints, length 2
  658. Padding to (prepend, append) along `axis`.
  659. axis : int
  660. Axis along which to pad `arr`.
  661. Returns
  662. -------
  663. padarr : ndarray
  664. Output array, with `pad_amt[0]` values prepended and `pad_amt[1]`
  665. values appended along `axis`. Both regions are padded wrapped values
  666. from the opposite end of `axis`.
  667. Notes
  668. -----
  669. This method of padding is also known as 'tile' or 'tiling'.
  670. The modes 'reflect', 'symmetric', and 'wrap' must be padded with a
  671. single function, lest the indexing tricks in non-integer multiples of the
  672. original shape would violate repetition in the final iteration.
  673. """
  674. # Implicit booleanness to test for zero (or None) in any scalar type
  675. if pad_amt[0] == 0 and pad_amt[1] == 0:
  676. return arr
  677. ##########################################################################
  678. # Prepended region
  679. # Slice off a reverse indexed chunk from near edge to pad `arr` before
  680. wrap_slice = _slice_last(arr.shape, pad_amt[0], axis=axis)
  681. wrap_chunk1 = arr[wrap_slice]
  682. ##########################################################################
  683. # Appended region
  684. # Slice off a reverse indexed chunk from far edge to pad `arr` after
  685. wrap_slice = _slice_first(arr.shape, pad_amt[1], axis=axis)
  686. wrap_chunk2 = arr[wrap_slice]
  687. # Concatenate `arr` with both chunks, extending along `axis`
  688. return np.concatenate((wrap_chunk1, arr, wrap_chunk2), axis=axis)
  689. def _as_pairs(x, ndim, as_index=False):
  690. """
  691. Broadcast `x` to an array with the shape (`ndim`, 2).
  692. A helper function for `pad` that prepares and validates arguments like
  693. `pad_width` for iteration in pairs.
  694. Parameters
  695. ----------
  696. x : {None, scalar, array-like}
  697. The object to broadcast to the shape (`ndim`, 2).
  698. ndim : int
  699. Number of pairs the broadcasted `x` will have.
  700. as_index : bool, optional
  701. If `x` is not None, try to round each element of `x` to an integer
  702. (dtype `np.intp`) and ensure every element is positive.
  703. Returns
  704. -------
  705. pairs : nested iterables, shape (`ndim`, 2)
  706. The broadcasted version of `x`.
  707. Raises
  708. ------
  709. ValueError
  710. If `as_index` is True and `x` contains negative elements.
  711. Or if `x` is not broadcastable to the shape (`ndim`, 2).
  712. """
  713. if x is None:
  714. # Pass through None as a special case, otherwise np.round(x) fails
  715. # with an AttributeError
  716. return ((None, None),) * ndim
  717. x = np.array(x)
  718. if as_index:
  719. x = np.round(x).astype(np.intp, copy=False)
  720. if x.ndim < 3:
  721. # Optimization: Possibly use faster paths for cases where `x` has
  722. # only 1 or 2 elements. `np.broadcast_to` could handle these as well
  723. # but is currently slower
  724. if x.size == 1:
  725. # x was supplied as a single value
  726. x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2
  727. if as_index and x < 0:
  728. raise ValueError("index can't contain negative values")
  729. return ((x[0], x[0]),) * ndim
  730. if x.size == 2 and x.shape != (2, 1):
  731. # x was supplied with a single value for each side
  732. # but except case when each dimension has a single value
  733. # which should be broadcasted to a pair,
  734. # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]]
  735. x = x.ravel() # Ensure x[0], x[1] works
  736. if as_index and (x[0] < 0 or x[1] < 0):
  737. raise ValueError("index can't contain negative values")
  738. return ((x[0], x[1]),) * ndim
  739. if as_index and x.min() < 0:
  740. raise ValueError("index can't contain negative values")
  741. # Converting the array with `tolist` seems to improve performance
  742. # when iterating and indexing the result (see usage in `pad`)
  743. return np.broadcast_to(x, (ndim, 2)).tolist()
  744. ###############################################################################
  745. # Public functions
  746. def _pad_dispatcher(array, pad_width, mode, **kwargs):
  747. return (array,)
  748. @array_function_dispatch(_pad_dispatcher, module='numpy')
  749. def pad(array, pad_width, mode, **kwargs):
  750. """
  751. Pads an array.
  752. Parameters
  753. ----------
  754. array : array_like of rank N
  755. Input array
  756. pad_width : {sequence, array_like, int}
  757. Number of values padded to the edges of each axis.
  758. ((before_1, after_1), ... (before_N, after_N)) unique pad widths
  759. for each axis.
  760. ((before, after),) yields same before and after pad for each axis.
  761. (pad,) or int is a shortcut for before = after = pad width for all
  762. axes.
  763. mode : str or function
  764. One of the following string values or a user supplied function.
  765. 'constant'
  766. Pads with a constant value.
  767. 'edge'
  768. Pads with the edge values of array.
  769. 'linear_ramp'
  770. Pads with the linear ramp between end_value and the
  771. array edge value.
  772. 'maximum'
  773. Pads with the maximum value of all or part of the
  774. vector along each axis.
  775. 'mean'
  776. Pads with the mean value of all or part of the
  777. vector along each axis.
  778. 'median'
  779. Pads with the median value of all or part of the
  780. vector along each axis.
  781. 'minimum'
  782. Pads with the minimum value of all or part of the
  783. vector along each axis.
  784. 'reflect'
  785. Pads with the reflection of the vector mirrored on
  786. the first and last values of the vector along each
  787. axis.
  788. 'symmetric'
  789. Pads with the reflection of the vector mirrored
  790. along the edge of the array.
  791. 'wrap'
  792. Pads with the wrap of the vector along the axis.
  793. The first values are used to pad the end and the
  794. end values are used to pad the beginning.
  795. <function>
  796. Padding function, see Notes.
  797. stat_length : sequence or int, optional
  798. Used in 'maximum', 'mean', 'median', and 'minimum'. Number of
  799. values at edge of each axis used to calculate the statistic value.
  800. ((before_1, after_1), ... (before_N, after_N)) unique statistic
  801. lengths for each axis.
  802. ((before, after),) yields same before and after statistic lengths
  803. for each axis.
  804. (stat_length,) or int is a shortcut for before = after = statistic
  805. length for all axes.
  806. Default is ``None``, to use the entire axis.
  807. constant_values : sequence or int, optional
  808. Used in 'constant'. The values to set the padded values for each
  809. axis.
  810. ((before_1, after_1), ... (before_N, after_N)) unique pad constants
  811. for each axis.
  812. ((before, after),) yields same before and after constants for each
  813. axis.
  814. (constant,) or int is a shortcut for before = after = constant for
  815. all axes.
  816. Default is 0.
  817. end_values : sequence or int, optional
  818. Used in 'linear_ramp'. The values used for the ending value of the
  819. linear_ramp and that will form the edge of the padded array.
  820. ((before_1, after_1), ... (before_N, after_N)) unique end values
  821. for each axis.
  822. ((before, after),) yields same before and after end values for each
  823. axis.
  824. (constant,) or int is a shortcut for before = after = end value for
  825. all axes.
  826. Default is 0.
  827. reflect_type : {'even', 'odd'}, optional
  828. Used in 'reflect', and 'symmetric'. The 'even' style is the
  829. default with an unaltered reflection around the edge value. For
  830. the 'odd' style, the extended part of the array is created by
  831. subtracting the reflected values from two times the edge value.
  832. Returns
  833. -------
  834. pad : ndarray
  835. Padded array of rank equal to `array` with shape increased
  836. according to `pad_width`.
  837. Notes
  838. -----
  839. .. versionadded:: 1.7.0
  840. For an array with rank greater than 1, some of the padding of later
  841. axes is calculated from padding of previous axes. This is easiest to
  842. think about with a rank 2 array where the corners of the padded array
  843. are calculated by using padded values from the first axis.
  844. The padding function, if used, should return a rank 1 array equal in
  845. length to the vector argument with padded values replaced. It has the
  846. following signature::
  847. padding_func(vector, iaxis_pad_width, iaxis, kwargs)
  848. where
  849. vector : ndarray
  850. A rank 1 array already padded with zeros. Padded values are
  851. vector[:pad_tuple[0]] and vector[-pad_tuple[1]:].
  852. iaxis_pad_width : tuple
  853. A 2-tuple of ints, iaxis_pad_width[0] represents the number of
  854. values padded at the beginning of vector where
  855. iaxis_pad_width[1] represents the number of values padded at
  856. the end of vector.
  857. iaxis : int
  858. The axis currently being calculated.
  859. kwargs : dict
  860. Any keyword arguments the function requires.
  861. Examples
  862. --------
  863. >>> a = [1, 2, 3, 4, 5]
  864. >>> np.pad(a, (2,3), 'constant', constant_values=(4, 6))
  865. array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6])
  866. >>> np.pad(a, (2, 3), 'edge')
  867. array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5])
  868. >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4))
  869. array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4])
  870. >>> np.pad(a, (2,), 'maximum')
  871. array([5, 5, 1, 2, 3, 4, 5, 5, 5])
  872. >>> np.pad(a, (2,), 'mean')
  873. array([3, 3, 1, 2, 3, 4, 5, 3, 3])
  874. >>> np.pad(a, (2,), 'median')
  875. array([3, 3, 1, 2, 3, 4, 5, 3, 3])
  876. >>> a = [[1, 2], [3, 4]]
  877. >>> np.pad(a, ((3, 2), (2, 3)), 'minimum')
  878. array([[1, 1, 1, 2, 1, 1, 1],
  879. [1, 1, 1, 2, 1, 1, 1],
  880. [1, 1, 1, 2, 1, 1, 1],
  881. [1, 1, 1, 2, 1, 1, 1],
  882. [3, 3, 3, 4, 3, 3, 3],
  883. [1, 1, 1, 2, 1, 1, 1],
  884. [1, 1, 1, 2, 1, 1, 1]])
  885. >>> a = [1, 2, 3, 4, 5]
  886. >>> np.pad(a, (2, 3), 'reflect')
  887. array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2])
  888. >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd')
  889. array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
  890. >>> np.pad(a, (2, 3), 'symmetric')
  891. array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3])
  892. >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd')
  893. array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7])
  894. >>> np.pad(a, (2, 3), 'wrap')
  895. array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3])
  896. >>> def pad_with(vector, pad_width, iaxis, kwargs):
  897. ... pad_value = kwargs.get('padder', 10)
  898. ... vector[:pad_width[0]] = pad_value
  899. ... vector[-pad_width[1]:] = pad_value
  900. ... return vector
  901. >>> a = np.arange(6)
  902. >>> a = a.reshape((2, 3))
  903. >>> np.pad(a, 2, pad_with)
  904. array([[10, 10, 10, 10, 10, 10, 10],
  905. [10, 10, 10, 10, 10, 10, 10],
  906. [10, 10, 0, 1, 2, 10, 10],
  907. [10, 10, 3, 4, 5, 10, 10],
  908. [10, 10, 10, 10, 10, 10, 10],
  909. [10, 10, 10, 10, 10, 10, 10]])
  910. >>> np.pad(a, 2, pad_with, padder=100)
  911. array([[100, 100, 100, 100, 100, 100, 100],
  912. [100, 100, 100, 100, 100, 100, 100],
  913. [100, 100, 0, 1, 2, 100, 100],
  914. [100, 100, 3, 4, 5, 100, 100],
  915. [100, 100, 100, 100, 100, 100, 100],
  916. [100, 100, 100, 100, 100, 100, 100]])
  917. """
  918. if not np.asarray(pad_width).dtype.kind == 'i':
  919. raise TypeError('`pad_width` must be of integral type.')
  920. narray = np.array(array)
  921. pad_width = _as_pairs(pad_width, narray.ndim, as_index=True)
  922. allowedkwargs = {
  923. 'constant': ['constant_values'],
  924. 'edge': [],
  925. 'linear_ramp': ['end_values'],
  926. 'maximum': ['stat_length'],
  927. 'mean': ['stat_length'],
  928. 'median': ['stat_length'],
  929. 'minimum': ['stat_length'],
  930. 'reflect': ['reflect_type'],
  931. 'symmetric': ['reflect_type'],
  932. 'wrap': [],
  933. }
  934. kwdefaults = {
  935. 'stat_length': None,
  936. 'constant_values': 0,
  937. 'end_values': 0,
  938. 'reflect_type': 'even',
  939. }
  940. if isinstance(mode, np.compat.basestring):
  941. # Make sure have allowed kwargs appropriate for mode
  942. for key in kwargs:
  943. if key not in allowedkwargs[mode]:
  944. raise ValueError('%s keyword not in allowed keywords %s' %
  945. (key, allowedkwargs[mode]))
  946. # Set kwarg defaults
  947. for kw in allowedkwargs[mode]:
  948. kwargs.setdefault(kw, kwdefaults[kw])
  949. # Need to only normalize particular keywords.
  950. for i in kwargs:
  951. if i == 'stat_length':
  952. kwargs[i] = _as_pairs(kwargs[i], narray.ndim, as_index=True)
  953. if i in ['end_values', 'constant_values']:
  954. kwargs[i] = _as_pairs(kwargs[i], narray.ndim)
  955. else:
  956. # Drop back to old, slower np.apply_along_axis mode for user-supplied
  957. # vector function
  958. function = mode
  959. # Create a new padded array
  960. rank = list(range(narray.ndim))
  961. total_dim_increase = [np.sum(pad_width[i]) for i in rank]
  962. offset_slices = tuple(
  963. slice(pad_width[i][0], pad_width[i][0] + narray.shape[i])
  964. for i in rank)
  965. new_shape = np.array(narray.shape) + total_dim_increase
  966. newmat = np.zeros(new_shape, narray.dtype)
  967. # Insert the original array into the padded array
  968. newmat[offset_slices] = narray
  969. # This is the core of pad ...
  970. for iaxis in rank:
  971. np.apply_along_axis(function,
  972. iaxis,
  973. newmat,
  974. pad_width[iaxis],
  975. iaxis,
  976. kwargs)
  977. return newmat
  978. # If we get here, use new padding method
  979. newmat = narray.copy()
  980. # API preserved, but completely new algorithm which pads by building the
  981. # entire block to pad before/after `arr` with in one step, for each axis.
  982. if mode == 'constant':
  983. for axis, ((pad_before, pad_after), (before_val, after_val)) \
  984. in enumerate(zip(pad_width, kwargs['constant_values'])):
  985. newmat = _prepend_const(newmat, pad_before, before_val, axis)
  986. newmat = _append_const(newmat, pad_after, after_val, axis)
  987. elif mode == 'edge':
  988. for axis, (pad_before, pad_after) in enumerate(pad_width):
  989. newmat = _prepend_edge(newmat, pad_before, axis)
  990. newmat = _append_edge(newmat, pad_after, axis)
  991. elif mode == 'linear_ramp':
  992. for axis, ((pad_before, pad_after), (before_val, after_val)) \
  993. in enumerate(zip(pad_width, kwargs['end_values'])):
  994. newmat = _prepend_ramp(newmat, pad_before, before_val, axis)
  995. newmat = _append_ramp(newmat, pad_after, after_val, axis)
  996. elif mode == 'maximum':
  997. for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
  998. in enumerate(zip(pad_width, kwargs['stat_length'])):
  999. newmat = _prepend_max(newmat, pad_before, chunk_before, axis)
  1000. newmat = _append_max(newmat, pad_after, chunk_after, axis)
  1001. elif mode == 'mean':
  1002. for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
  1003. in enumerate(zip(pad_width, kwargs['stat_length'])):
  1004. newmat = _prepend_mean(newmat, pad_before, chunk_before, axis)
  1005. newmat = _append_mean(newmat, pad_after, chunk_after, axis)
  1006. elif mode == 'median':
  1007. for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
  1008. in enumerate(zip(pad_width, kwargs['stat_length'])):
  1009. newmat = _prepend_med(newmat, pad_before, chunk_before, axis)
  1010. newmat = _append_med(newmat, pad_after, chunk_after, axis)
  1011. elif mode == 'minimum':
  1012. for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \
  1013. in enumerate(zip(pad_width, kwargs['stat_length'])):
  1014. newmat = _prepend_min(newmat, pad_before, chunk_before, axis)
  1015. newmat = _append_min(newmat, pad_after, chunk_after, axis)
  1016. elif mode == 'reflect':
  1017. for axis, (pad_before, pad_after) in enumerate(pad_width):
  1018. if narray.shape[axis] == 0:
  1019. # Axes with non-zero padding cannot be empty.
  1020. if pad_before > 0 or pad_after > 0:
  1021. raise ValueError("There aren't any elements to reflect"
  1022. " in axis {} of `array`".format(axis))
  1023. # Skip zero padding on empty axes.
  1024. continue
  1025. # Recursive padding along any axis where `pad_amt` is too large
  1026. # for indexing tricks. We can only safely pad the original axis
  1027. # length, to keep the period of the reflections consistent.
  1028. if ((pad_before > 0) or
  1029. (pad_after > 0)) and newmat.shape[axis] == 1:
  1030. # Extending singleton dimension for 'reflect' is legacy
  1031. # behavior; it really should raise an error.
  1032. newmat = _prepend_edge(newmat, pad_before, axis)
  1033. newmat = _append_edge(newmat, pad_after, axis)
  1034. continue
  1035. method = kwargs['reflect_type']
  1036. safe_pad = newmat.shape[axis] - 1
  1037. while ((pad_before > safe_pad) or (pad_after > safe_pad)):
  1038. pad_iter_b = min(safe_pad,
  1039. safe_pad * (pad_before // safe_pad))
  1040. pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
  1041. newmat = _pad_ref(newmat, (pad_iter_b,
  1042. pad_iter_a), method, axis)
  1043. pad_before -= pad_iter_b
  1044. pad_after -= pad_iter_a
  1045. safe_pad += pad_iter_b + pad_iter_a
  1046. newmat = _pad_ref(newmat, (pad_before, pad_after), method, axis)
  1047. elif mode == 'symmetric':
  1048. for axis, (pad_before, pad_after) in enumerate(pad_width):
  1049. # Recursive padding along any axis where `pad_amt` is too large
  1050. # for indexing tricks. We can only safely pad the original axis
  1051. # length, to keep the period of the reflections consistent.
  1052. method = kwargs['reflect_type']
  1053. safe_pad = newmat.shape[axis]
  1054. while ((pad_before > safe_pad) or
  1055. (pad_after > safe_pad)):
  1056. pad_iter_b = min(safe_pad,
  1057. safe_pad * (pad_before // safe_pad))
  1058. pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
  1059. newmat = _pad_sym(newmat, (pad_iter_b,
  1060. pad_iter_a), method, axis)
  1061. pad_before -= pad_iter_b
  1062. pad_after -= pad_iter_a
  1063. safe_pad += pad_iter_b + pad_iter_a
  1064. newmat = _pad_sym(newmat, (pad_before, pad_after), method, axis)
  1065. elif mode == 'wrap':
  1066. for axis, (pad_before, pad_after) in enumerate(pad_width):
  1067. # Recursive padding along any axis where `pad_amt` is too large
  1068. # for indexing tricks. We can only safely pad the original axis
  1069. # length, to keep the period of the reflections consistent.
  1070. safe_pad = newmat.shape[axis]
  1071. while ((pad_before > safe_pad) or
  1072. (pad_after > safe_pad)):
  1073. pad_iter_b = min(safe_pad,
  1074. safe_pad * (pad_before // safe_pad))
  1075. pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad))
  1076. newmat = _pad_wrap(newmat, (pad_iter_b, pad_iter_a), axis)
  1077. pad_before -= pad_iter_b
  1078. pad_after -= pad_iter_a
  1079. safe_pad += pad_iter_b + pad_iter_a
  1080. newmat = _pad_wrap(newmat, (pad_before, pad_after), axis)
  1081. return newmat