Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2The arraypad module contains a group of functions to pad values onto the edges 

3of an n-dimensional array. 

4 

5""" 

6import numpy as np 

7from numpy.core.overrides import array_function_dispatch 

8from numpy.lib.index_tricks import ndindex 

9 

10 

11__all__ = ['pad'] 

12 

13 

14############################################################################### 

15# Private utility functions. 

16 

17 

18def _round_if_needed(arr, dtype): 

19 """ 

20 Rounds arr inplace if destination dtype is integer. 

21 

22 Parameters 

23 ---------- 

24 arr : ndarray 

25 Input array. 

26 dtype : dtype 

27 The dtype of the destination array. 

28 """ 

29 if np.issubdtype(dtype, np.integer): 

30 arr.round(out=arr) 

31 

32 

33def _slice_at_axis(sl, axis): 

34 """ 

35 Construct tuple of slices to slice an array in the given dimension. 

36 

37 Parameters 

38 ---------- 

39 sl : slice 

40 The slice for the given dimension. 

41 axis : int 

42 The axis to which `sl` is applied. All other dimensions are left 

43 "unsliced". 

44 

45 Returns 

46 ------- 

47 sl : tuple of slices 

48 A tuple with slices matching `shape` in length. 

49 

50 Examples 

51 -------- 

52 >>> _slice_at_axis(slice(None, 3, -1), 1) 

53 (slice(None, None, None), slice(None, 3, -1), (...,)) 

54 """ 

55 return (slice(None),) * axis + (sl,) + (...,) 

56 

57 

58def _view_roi(array, original_area_slice, axis): 

59 """ 

60 Get a view of the current region of interest during iterative padding. 

61 

62 When padding multiple dimensions iteratively corner values are 

63 unnecessarily overwritten multiple times. This function reduces the 

64 working area for the first dimensions so that corners are excluded. 

65 

66 Parameters 

67 ---------- 

68 array : ndarray 

69 The array with the region of interest. 

70 original_area_slice : tuple of slices 

71 Denotes the area with original values of the unpadded array. 

72 axis : int 

73 The currently padded dimension assuming that `axis` is padded before 

74 `axis` + 1. 

75 

76 Returns 

77 ------- 

78 roi : ndarray 

79 The region of interest of the original `array`. 

80 """ 

81 axis += 1 

82 sl = (slice(None),) * axis + original_area_slice[axis:] 

83 return array[sl] 

84 

85 

86def _pad_simple(array, pad_width, fill_value=None): 

87 """ 

88 Pad array on all sides with either a single value or undefined values. 

89 

90 Parameters 

91 ---------- 

92 array : ndarray 

93 Array to grow. 

94 pad_width : sequence of tuple[int, int] 

95 Pad width on both sides for each dimension in `arr`. 

96 fill_value : scalar, optional 

97 If provided the padded area is filled with this value, otherwise 

98 the pad area left undefined. 

99 

100 Returns 

101 ------- 

102 padded : ndarray 

103 The padded array with the same dtype as`array`. Its order will default 

104 to C-style if `array` is not F-contiguous. 

105 original_area_slice : tuple 

106 A tuple of slices pointing to the area of the original array. 

107 """ 

108 # Allocate grown array 

109 new_shape = tuple( 

110 left + size + right 

111 for size, (left, right) in zip(array.shape, pad_width) 

112 ) 

113 order = 'F' if array.flags.fnc else 'C' # Fortran and not also C-order 

114 padded = np.empty(new_shape, dtype=array.dtype, order=order) 

115 

116 if fill_value is not None: 

117 padded.fill(fill_value) 

118 

119 # Copy old array into correct space 

120 original_area_slice = tuple( 

121 slice(left, left + size) 

122 for size, (left, right) in zip(array.shape, pad_width) 

123 ) 

124 padded[original_area_slice] = array 

125 

126 return padded, original_area_slice 

127 

128 

129def _set_pad_area(padded, axis, width_pair, value_pair): 

130 """ 

131 Set empty-padded area in given dimension. 

132 

133 Parameters 

134 ---------- 

135 padded : ndarray 

136 Array with the pad area which is modified inplace. 

137 axis : int 

138 Dimension with the pad area to set. 

139 width_pair : (int, int) 

140 Pair of widths that mark the pad area on both sides in the given 

141 dimension. 

142 value_pair : tuple of scalars or ndarrays 

143 Values inserted into the pad area on each side. It must match or be 

144 broadcastable to the shape of `arr`. 

145 """ 

146 left_slice = _slice_at_axis(slice(None, width_pair[0]), axis) 

147 padded[left_slice] = value_pair[0] 

148 

149 right_slice = _slice_at_axis( 

150 slice(padded.shape[axis] - width_pair[1], None), axis) 

151 padded[right_slice] = value_pair[1] 

152 

153 

154def _get_edges(padded, axis, width_pair): 

155 """ 

156 Retrieve edge values from empty-padded array in given dimension. 

157 

158 Parameters 

159 ---------- 

160 padded : ndarray 

161 Empty-padded array. 

162 axis : int 

163 Dimension in which the edges are considered. 

164 width_pair : (int, int) 

165 Pair of widths that mark the pad area on both sides in the given 

166 dimension. 

167 

168 Returns 

169 ------- 

170 left_edge, right_edge : ndarray 

171 Edge values of the valid area in `padded` in the given dimension. Its 

172 shape will always match `padded` except for the dimension given by 

173 `axis` which will have a length of 1. 

174 """ 

175 left_index = width_pair[0] 

176 left_slice = _slice_at_axis(slice(left_index, left_index + 1), axis) 

177 left_edge = padded[left_slice] 

178 

179 right_index = padded.shape[axis] - width_pair[1] 

180 right_slice = _slice_at_axis(slice(right_index - 1, right_index), axis) 

181 right_edge = padded[right_slice] 

182 

183 return left_edge, right_edge 

184 

185 

186def _get_linear_ramps(padded, axis, width_pair, end_value_pair): 

187 """ 

188 Construct linear ramps for empty-padded array in given dimension. 

189 

190 Parameters 

191 ---------- 

192 padded : ndarray 

193 Empty-padded array. 

194 axis : int 

195 Dimension in which the ramps are constructed. 

196 width_pair : (int, int) 

197 Pair of widths that mark the pad area on both sides in the given 

198 dimension. 

199 end_value_pair : (scalar, scalar) 

200 End values for the linear ramps which form the edge of the fully padded 

201 array. These values are included in the linear ramps. 

202 

203 Returns 

204 ------- 

205 left_ramp, right_ramp : ndarray 

206 Linear ramps to set on both sides of `padded`. 

207 """ 

208 edge_pair = _get_edges(padded, axis, width_pair) 

209 

210 left_ramp = np.linspace( 

211 start=end_value_pair[0], 

212 stop=edge_pair[0].squeeze(axis), # Dimensions is replaced by linspace 

213 num=width_pair[0], 

214 endpoint=False, 

215 dtype=padded.dtype, 

216 axis=axis, 

217 ) 

218 

219 right_ramp = np.linspace( 

220 start=end_value_pair[1], 

221 stop=edge_pair[1].squeeze(axis), # Dimension is replaced by linspace 

222 num=width_pair[1], 

223 endpoint=False, 

224 dtype=padded.dtype, 

225 axis=axis, 

226 ) 

227 # Reverse linear space in appropriate dimension 

228 right_ramp = right_ramp[_slice_at_axis(slice(None, None, -1), axis)] 

229 

230 return left_ramp, right_ramp 

231 

232 

233def _get_stats(padded, axis, width_pair, length_pair, stat_func): 

234 """ 

235 Calculate statistic for the empty-padded array in given dimension. 

236 

237 Parameters 

238 ---------- 

239 padded : ndarray 

240 Empty-padded array. 

241 axis : int 

242 Dimension in which the statistic is calculated. 

243 width_pair : (int, int) 

244 Pair of widths that mark the pad area on both sides in the given 

245 dimension. 

246 length_pair : 2-element sequence of None or int 

247 Gives the number of values in valid area from each side that is 

248 taken into account when calculating the statistic. If None the entire 

249 valid area in `padded` is considered. 

250 stat_func : function 

251 Function to compute statistic. The expected signature is 

252 ``stat_func(x: ndarray, axis: int, keepdims: bool) -> ndarray``. 

253 

254 Returns 

255 ------- 

256 left_stat, right_stat : ndarray 

257 Calculated statistic for both sides of `padded`. 

258 """ 

259 # Calculate indices of the edges of the area with original values 

260 left_index = width_pair[0] 

261 right_index = padded.shape[axis] - width_pair[1] 

262 # as well as its length 

263 max_length = right_index - left_index 

264 

265 # Limit stat_lengths to max_length 

266 left_length, right_length = length_pair 

267 if left_length is None or max_length < left_length: 

268 left_length = max_length 

269 if right_length is None or max_length < right_length: 

270 right_length = max_length 

271 

272 if (left_length == 0 or right_length == 0) \ 

273 and stat_func in {np.amax, np.amin}: 

274 # amax and amin can't operate on an empty array, 

275 # raise a more descriptive warning here instead of the default one 

276 raise ValueError("stat_length of 0 yields no value for padding") 

277 

278 # Calculate statistic for the left side 

279 left_slice = _slice_at_axis( 

280 slice(left_index, left_index + left_length), axis) 

281 left_chunk = padded[left_slice] 

282 left_stat = stat_func(left_chunk, axis=axis, keepdims=True) 

283 _round_if_needed(left_stat, padded.dtype) 

284 

285 if left_length == right_length == max_length: 

286 # return early as right_stat must be identical to left_stat 

287 return left_stat, left_stat 

288 

289 # Calculate statistic for the right side 

290 right_slice = _slice_at_axis( 

291 slice(right_index - right_length, right_index), axis) 

292 right_chunk = padded[right_slice] 

293 right_stat = stat_func(right_chunk, axis=axis, keepdims=True) 

294 _round_if_needed(right_stat, padded.dtype) 

295 

296 return left_stat, right_stat 

297 

298 

299def _set_reflect_both(padded, axis, width_pair, method, include_edge=False): 

300 """ 

301 Pad `axis` of `arr` with reflection. 

302 

303 Parameters 

304 ---------- 

305 padded : ndarray 

306 Input array of arbitrary shape. 

307 axis : int 

308 Axis along which to pad `arr`. 

309 width_pair : (int, int) 

310 Pair of widths that mark the pad area on both sides in the given 

311 dimension. 

312 method : str 

313 Controls method of reflection; options are 'even' or 'odd'. 

314 include_edge : bool 

315 If true, edge value is included in reflection, otherwise the edge 

316 value forms the symmetric axis to the reflection. 

317 

318 Returns 

319 ------- 

320 pad_amt : tuple of ints, length 2 

321 New index positions of padding to do along the `axis`. If these are 

322 both 0, padding is done in this dimension. 

323 """ 

324 left_pad, right_pad = width_pair 

325 old_length = padded.shape[axis] - right_pad - left_pad 

326 

327 if include_edge: 

328 # Edge is included, we need to offset the pad amount by 1 

329 edge_offset = 1 

330 else: 

331 edge_offset = 0 # Edge is not included, no need to offset pad amount 

332 old_length -= 1 # but must be omitted from the chunk 

333 

334 if left_pad > 0: 

335 # Pad with reflected values on left side: 

336 # First limit chunk size which can't be larger than pad area 

337 chunk_length = min(old_length, left_pad) 

338 # Slice right to left, stop on or next to edge, start relative to stop 

339 stop = left_pad - edge_offset 

340 start = stop + chunk_length 

341 left_slice = _slice_at_axis(slice(start, stop, -1), axis) 

342 left_chunk = padded[left_slice] 

343 

344 if method == "odd": 

345 # Negate chunk and align with edge 

346 edge_slice = _slice_at_axis(slice(left_pad, left_pad + 1), axis) 

347 left_chunk = 2 * padded[edge_slice] - left_chunk 

348 

349 # Insert chunk into padded area 

350 start = left_pad - chunk_length 

351 stop = left_pad 

352 pad_area = _slice_at_axis(slice(start, stop), axis) 

353 padded[pad_area] = left_chunk 

354 # Adjust pointer to left edge for next iteration 

355 left_pad -= chunk_length 

356 

357 if right_pad > 0: 

358 # Pad with reflected values on right side: 

359 # First limit chunk size which can't be larger than pad area 

360 chunk_length = min(old_length, right_pad) 

361 # Slice right to left, start on or next to edge, stop relative to start 

362 start = -right_pad + edge_offset - 2 

363 stop = start - chunk_length 

364 right_slice = _slice_at_axis(slice(start, stop, -1), axis) 

365 right_chunk = padded[right_slice] 

366 

367 if method == "odd": 

368 # Negate chunk and align with edge 

369 edge_slice = _slice_at_axis( 

370 slice(-right_pad - 1, -right_pad), axis) 

371 right_chunk = 2 * padded[edge_slice] - right_chunk 

372 

373 # Insert chunk into padded area 

374 start = padded.shape[axis] - right_pad 

375 stop = start + chunk_length 

376 pad_area = _slice_at_axis(slice(start, stop), axis) 

377 padded[pad_area] = right_chunk 

378 # Adjust pointer to right edge for next iteration 

379 right_pad -= chunk_length 

380 

381 return left_pad, right_pad 

382 

383 

384def _set_wrap_both(padded, axis, width_pair): 

385 """ 

386 Pad `axis` of `arr` with wrapped values. 

387 

388 Parameters 

389 ---------- 

390 padded : ndarray 

391 Input array of arbitrary shape. 

392 axis : int 

393 Axis along which to pad `arr`. 

394 width_pair : (int, int) 

395 Pair of widths that mark the pad area on both sides in the given 

396 dimension. 

397 

398 Returns 

399 ------- 

400 pad_amt : tuple of ints, length 2 

401 New index positions of padding to do along the `axis`. If these are 

402 both 0, padding is done in this dimension. 

403 """ 

404 left_pad, right_pad = width_pair 

405 period = padded.shape[axis] - right_pad - left_pad 

406 

407 # If the current dimension of `arr` doesn't contain enough valid values 

408 # (not part of the undefined pad area) we need to pad multiple times. 

409 # Each time the pad area shrinks on both sides which is communicated with 

410 # these variables. 

411 new_left_pad = 0 

412 new_right_pad = 0 

413 

414 if left_pad > 0: 

415 # Pad with wrapped values on left side 

416 # First slice chunk from right side of the non-pad area. 

417 # Use min(period, left_pad) to ensure that chunk is not larger than 

418 # pad area 

419 right_slice = _slice_at_axis( 

420 slice(-right_pad - min(period, left_pad), 

421 -right_pad if right_pad != 0 else None), 

422 axis 

423 ) 

424 right_chunk = padded[right_slice] 

425 

426 if left_pad > period: 

427 # Chunk is smaller than pad area 

428 pad_area = _slice_at_axis(slice(left_pad - period, left_pad), axis) 

429 new_left_pad = left_pad - period 

430 else: 

431 # Chunk matches pad area 

432 pad_area = _slice_at_axis(slice(None, left_pad), axis) 

433 padded[pad_area] = right_chunk 

434 

435 if right_pad > 0: 

436 # Pad with wrapped values on right side 

437 # First slice chunk from left side of the non-pad area. 

438 # Use min(period, right_pad) to ensure that chunk is not larger than 

439 # pad area 

440 left_slice = _slice_at_axis( 

441 slice(left_pad, left_pad + min(period, right_pad),), axis) 

442 left_chunk = padded[left_slice] 

443 

444 if right_pad > period: 

445 # Chunk is smaller than pad area 

446 pad_area = _slice_at_axis( 

447 slice(-right_pad, -right_pad + period), axis) 

448 new_right_pad = right_pad - period 

449 else: 

450 # Chunk matches pad area 

451 pad_area = _slice_at_axis(slice(-right_pad, None), axis) 

452 padded[pad_area] = left_chunk 

453 

454 return new_left_pad, new_right_pad 

455 

456 

457def _as_pairs(x, ndim, as_index=False): 

458 """ 

459 Broadcast `x` to an array with the shape (`ndim`, 2). 

460 

461 A helper function for `pad` that prepares and validates arguments like 

462 `pad_width` for iteration in pairs. 

463 

464 Parameters 

465 ---------- 

466 x : {None, scalar, array-like} 

467 The object to broadcast to the shape (`ndim`, 2). 

468 ndim : int 

469 Number of pairs the broadcasted `x` will have. 

470 as_index : bool, optional 

471 If `x` is not None, try to round each element of `x` to an integer 

472 (dtype `np.intp`) and ensure every element is positive. 

473 

474 Returns 

475 ------- 

476 pairs : nested iterables, shape (`ndim`, 2) 

477 The broadcasted version of `x`. 

478 

479 Raises 

480 ------ 

481 ValueError 

482 If `as_index` is True and `x` contains negative elements. 

483 Or if `x` is not broadcastable to the shape (`ndim`, 2). 

484 """ 

485 if x is None: 

486 # Pass through None as a special case, otherwise np.round(x) fails 

487 # with an AttributeError 

488 return ((None, None),) * ndim 

489 

490 x = np.array(x) 

491 if as_index: 

492 x = np.round(x).astype(np.intp, copy=False) 

493 

494 if x.ndim < 3: 

495 # Optimization: Possibly use faster paths for cases where `x` has 

496 # only 1 or 2 elements. `np.broadcast_to` could handle these as well 

497 # but is currently slower 

498 

499 if x.size == 1: 

500 # x was supplied as a single value 

501 x = x.ravel() # Ensure x[0] works for x.ndim == 0, 1, 2 

502 if as_index and x < 0: 

503 raise ValueError("index can't contain negative values") 

504 return ((x[0], x[0]),) * ndim 

505 

506 if x.size == 2 and x.shape != (2, 1): 

507 # x was supplied with a single value for each side 

508 # but except case when each dimension has a single value 

509 # which should be broadcasted to a pair, 

510 # e.g. [[1], [2]] -> [[1, 1], [2, 2]] not [[1, 2], [1, 2]] 

511 x = x.ravel() # Ensure x[0], x[1] works 

512 if as_index and (x[0] < 0 or x[1] < 0): 

513 raise ValueError("index can't contain negative values") 

514 return ((x[0], x[1]),) * ndim 

515 

516 if as_index and x.min() < 0: 

517 raise ValueError("index can't contain negative values") 

518 

519 # Converting the array with `tolist` seems to improve performance 

520 # when iterating and indexing the result (see usage in `pad`) 

521 return np.broadcast_to(x, (ndim, 2)).tolist() 

522 

523 

524def _pad_dispatcher(array, pad_width, mode=None, **kwargs): 

525 return (array,) 

526 

527 

528############################################################################### 

529# Public functions 

530 

531 

532@array_function_dispatch(_pad_dispatcher, module='numpy') 

533def pad(array, pad_width, mode='constant', **kwargs): 

534 """ 

535 Pad an array. 

536 

537 Parameters 

538 ---------- 

539 array : array_like of rank N 

540 The array to pad. 

541 pad_width : {sequence, array_like, int} 

542 Number of values padded to the edges of each axis. 

543 ((before_1, after_1), ... (before_N, after_N)) unique pad widths 

544 for each axis. 

545 ((before, after),) yields same before and after pad for each axis. 

546 (pad,) or int is a shortcut for before = after = pad width for all 

547 axes. 

548 mode : str or function, optional 

549 One of the following string values or a user supplied function. 

550 

551 'constant' (default) 

552 Pads with a constant value. 

553 'edge' 

554 Pads with the edge values of array. 

555 'linear_ramp' 

556 Pads with the linear ramp between end_value and the 

557 array edge value. 

558 'maximum' 

559 Pads with the maximum value of all or part of the 

560 vector along each axis. 

561 'mean' 

562 Pads with the mean value of all or part of the 

563 vector along each axis. 

564 'median' 

565 Pads with the median value of all or part of the 

566 vector along each axis. 

567 'minimum' 

568 Pads with the minimum value of all or part of the 

569 vector along each axis. 

570 'reflect' 

571 Pads with the reflection of the vector mirrored on 

572 the first and last values of the vector along each 

573 axis. 

574 'symmetric' 

575 Pads with the reflection of the vector mirrored 

576 along the edge of the array. 

577 'wrap' 

578 Pads with the wrap of the vector along the axis. 

579 The first values are used to pad the end and the 

580 end values are used to pad the beginning. 

581 'empty' 

582 Pads with undefined values. 

583 

584 .. versionadded:: 1.17 

585 

586 <function> 

587 Padding function, see Notes. 

588 stat_length : sequence or int, optional 

589 Used in 'maximum', 'mean', 'median', and 'minimum'. Number of 

590 values at edge of each axis used to calculate the statistic value. 

591 

592 ((before_1, after_1), ... (before_N, after_N)) unique statistic 

593 lengths for each axis. 

594 

595 ((before, after),) yields same before and after statistic lengths 

596 for each axis. 

597 

598 (stat_length,) or int is a shortcut for before = after = statistic 

599 length for all axes. 

600 

601 Default is ``None``, to use the entire axis. 

602 constant_values : sequence or scalar, optional 

603 Used in 'constant'. The values to set the padded values for each 

604 axis. 

605 

606 ``((before_1, after_1), ... (before_N, after_N))`` unique pad constants 

607 for each axis. 

608 

609 ``((before, after),)`` yields same before and after constants for each 

610 axis. 

611 

612 ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for 

613 all axes. 

614 

615 Default is 0. 

616 end_values : sequence or scalar, optional 

617 Used in 'linear_ramp'. The values used for the ending value of the 

618 linear_ramp and that will form the edge of the padded array. 

619 

620 ``((before_1, after_1), ... (before_N, after_N))`` unique end values 

621 for each axis. 

622 

623 ``((before, after),)`` yields same before and after end values for each 

624 axis. 

625 

626 ``(constant,)`` or ``constant`` is a shortcut for ``before = after = constant`` for 

627 all axes. 

628 

629 Default is 0. 

630 reflect_type : {'even', 'odd'}, optional 

631 Used in 'reflect', and 'symmetric'. The 'even' style is the 

632 default with an unaltered reflection around the edge value. For 

633 the 'odd' style, the extended part of the array is created by 

634 subtracting the reflected values from two times the edge value. 

635 

636 Returns 

637 ------- 

638 pad : ndarray 

639 Padded array of rank equal to `array` with shape increased 

640 according to `pad_width`. 

641 

642 Notes 

643 ----- 

644 .. versionadded:: 1.7.0 

645 

646 For an array with rank greater than 1, some of the padding of later 

647 axes is calculated from padding of previous axes. This is easiest to 

648 think about with a rank 2 array where the corners of the padded array 

649 are calculated by using padded values from the first axis. 

650 

651 The padding function, if used, should modify a rank 1 array in-place. It 

652 has the following signature:: 

653 

654 padding_func(vector, iaxis_pad_width, iaxis, kwargs) 

655 

656 where 

657 

658 vector : ndarray 

659 A rank 1 array already padded with zeros. Padded values are 

660 vector[:iaxis_pad_width[0]] and vector[-iaxis_pad_width[1]:]. 

661 iaxis_pad_width : tuple 

662 A 2-tuple of ints, iaxis_pad_width[0] represents the number of 

663 values padded at the beginning of vector where 

664 iaxis_pad_width[1] represents the number of values padded at 

665 the end of vector. 

666 iaxis : int 

667 The axis currently being calculated. 

668 kwargs : dict 

669 Any keyword arguments the function requires. 

670 

671 Examples 

672 -------- 

673 >>> a = [1, 2, 3, 4, 5] 

674 >>> np.pad(a, (2, 3), 'constant', constant_values=(4, 6)) 

675 array([4, 4, 1, ..., 6, 6, 6]) 

676 

677 >>> np.pad(a, (2, 3), 'edge') 

678 array([1, 1, 1, ..., 5, 5, 5]) 

679 

680 >>> np.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4)) 

681 array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) 

682 

683 >>> np.pad(a, (2,), 'maximum') 

684 array([5, 5, 1, 2, 3, 4, 5, 5, 5]) 

685 

686 >>> np.pad(a, (2,), 'mean') 

687 array([3, 3, 1, 2, 3, 4, 5, 3, 3]) 

688 

689 >>> np.pad(a, (2,), 'median') 

690 array([3, 3, 1, 2, 3, 4, 5, 3, 3]) 

691 

692 >>> a = [[1, 2], [3, 4]] 

693 >>> np.pad(a, ((3, 2), (2, 3)), 'minimum') 

694 array([[1, 1, 1, 2, 1, 1, 1], 

695 [1, 1, 1, 2, 1, 1, 1], 

696 [1, 1, 1, 2, 1, 1, 1], 

697 [1, 1, 1, 2, 1, 1, 1], 

698 [3, 3, 3, 4, 3, 3, 3], 

699 [1, 1, 1, 2, 1, 1, 1], 

700 [1, 1, 1, 2, 1, 1, 1]]) 

701 

702 >>> a = [1, 2, 3, 4, 5] 

703 >>> np.pad(a, (2, 3), 'reflect') 

704 array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]) 

705 

706 >>> np.pad(a, (2, 3), 'reflect', reflect_type='odd') 

707 array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) 

708 

709 >>> np.pad(a, (2, 3), 'symmetric') 

710 array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]) 

711 

712 >>> np.pad(a, (2, 3), 'symmetric', reflect_type='odd') 

713 array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]) 

714 

715 >>> np.pad(a, (2, 3), 'wrap') 

716 array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]) 

717 

718 >>> def pad_with(vector, pad_width, iaxis, kwargs): 

719 ... pad_value = kwargs.get('padder', 10) 

720 ... vector[:pad_width[0]] = pad_value 

721 ... vector[-pad_width[1]:] = pad_value 

722 >>> a = np.arange(6) 

723 >>> a = a.reshape((2, 3)) 

724 >>> np.pad(a, 2, pad_with) 

725 array([[10, 10, 10, 10, 10, 10, 10], 

726 [10, 10, 10, 10, 10, 10, 10], 

727 [10, 10, 0, 1, 2, 10, 10], 

728 [10, 10, 3, 4, 5, 10, 10], 

729 [10, 10, 10, 10, 10, 10, 10], 

730 [10, 10, 10, 10, 10, 10, 10]]) 

731 >>> np.pad(a, 2, pad_with, padder=100) 

732 array([[100, 100, 100, 100, 100, 100, 100], 

733 [100, 100, 100, 100, 100, 100, 100], 

734 [100, 100, 0, 1, 2, 100, 100], 

735 [100, 100, 3, 4, 5, 100, 100], 

736 [100, 100, 100, 100, 100, 100, 100], 

737 [100, 100, 100, 100, 100, 100, 100]]) 

738 """ 

739 array = np.asarray(array) 

740 pad_width = np.asarray(pad_width) 

741 

742 if not pad_width.dtype.kind == 'i': 

743 raise TypeError('`pad_width` must be of integral type.') 

744 

745 # Broadcast to shape (array.ndim, 2) 

746 pad_width = _as_pairs(pad_width, array.ndim, as_index=True) 

747 

748 if callable(mode): 

749 # Old behavior: Use user-supplied function with np.apply_along_axis 

750 function = mode 

751 # Create a new zero padded array 

752 padded, _ = _pad_simple(array, pad_width, fill_value=0) 

753 # And apply along each axis 

754 

755 for axis in range(padded.ndim): 

756 # Iterate using ndindex as in apply_along_axis, but assuming that 

757 # function operates inplace on the padded array. 

758 

759 # view with the iteration axis at the end 

760 view = np.moveaxis(padded, axis, -1) 

761 

762 # compute indices for the iteration axes, and append a trailing 

763 # ellipsis to prevent 0d arrays decaying to scalars (gh-8642) 

764 inds = ndindex(view.shape[:-1]) 

765 inds = (ind + (Ellipsis,) for ind in inds) 

766 for ind in inds: 

767 function(view[ind], pad_width[axis], axis, kwargs) 

768 

769 return padded 

770 

771 # Make sure that no unsupported keywords were passed for the current mode 

772 allowed_kwargs = { 

773 'empty': [], 'edge': [], 'wrap': [], 

774 'constant': ['constant_values'], 

775 'linear_ramp': ['end_values'], 

776 'maximum': ['stat_length'], 

777 'mean': ['stat_length'], 

778 'median': ['stat_length'], 

779 'minimum': ['stat_length'], 

780 'reflect': ['reflect_type'], 

781 'symmetric': ['reflect_type'], 

782 } 

783 try: 

784 unsupported_kwargs = set(kwargs) - set(allowed_kwargs[mode]) 

785 except KeyError: 

786 raise ValueError("mode '{}' is not supported".format(mode)) 

787 if unsupported_kwargs: 

788 raise ValueError("unsupported keyword arguments for mode '{}': {}" 

789 .format(mode, unsupported_kwargs)) 

790 

791 stat_functions = {"maximum": np.amax, "minimum": np.amin, 

792 "mean": np.mean, "median": np.median} 

793 

794 # Create array with final shape and original values 

795 # (padded area is undefined) 

796 padded, original_area_slice = _pad_simple(array, pad_width) 

797 # And prepare iteration over all dimensions 

798 # (zipping may be more readable than using enumerate) 

799 axes = range(padded.ndim) 

800 

801 if mode == "constant": 

802 values = kwargs.get("constant_values", 0) 

803 values = _as_pairs(values, padded.ndim) 

804 for axis, width_pair, value_pair in zip(axes, pad_width, values): 

805 roi = _view_roi(padded, original_area_slice, axis) 

806 _set_pad_area(roi, axis, width_pair, value_pair) 

807 

808 elif mode == "empty": 

809 pass # Do nothing as _pad_simple already returned the correct result 

810 

811 elif array.size == 0: 

812 # Only modes "constant" and "empty" can extend empty axes, all other 

813 # modes depend on `array` not being empty 

814 # -> ensure every empty axis is only "padded with 0" 

815 for axis, width_pair in zip(axes, pad_width): 

816 if array.shape[axis] == 0 and any(width_pair): 

817 raise ValueError( 

818 "can't extend empty axis {} using modes other than " 

819 "'constant' or 'empty'".format(axis) 

820 ) 

821 # passed, don't need to do anything more as _pad_simple already 

822 # returned the correct result 

823 

824 elif mode == "edge": 

825 for axis, width_pair in zip(axes, pad_width): 

826 roi = _view_roi(padded, original_area_slice, axis) 

827 edge_pair = _get_edges(roi, axis, width_pair) 

828 _set_pad_area(roi, axis, width_pair, edge_pair) 

829 

830 elif mode == "linear_ramp": 

831 end_values = kwargs.get("end_values", 0) 

832 end_values = _as_pairs(end_values, padded.ndim) 

833 for axis, width_pair, value_pair in zip(axes, pad_width, end_values): 

834 roi = _view_roi(padded, original_area_slice, axis) 

835 ramp_pair = _get_linear_ramps(roi, axis, width_pair, value_pair) 

836 _set_pad_area(roi, axis, width_pair, ramp_pair) 

837 

838 elif mode in stat_functions: 

839 func = stat_functions[mode] 

840 length = kwargs.get("stat_length", None) 

841 length = _as_pairs(length, padded.ndim, as_index=True) 

842 for axis, width_pair, length_pair in zip(axes, pad_width, length): 

843 roi = _view_roi(padded, original_area_slice, axis) 

844 stat_pair = _get_stats(roi, axis, width_pair, length_pair, func) 

845 _set_pad_area(roi, axis, width_pair, stat_pair) 

846 

847 elif mode in {"reflect", "symmetric"}: 

848 method = kwargs.get("reflect_type", "even") 

849 include_edge = True if mode == "symmetric" else False 

850 for axis, (left_index, right_index) in zip(axes, pad_width): 

851 if array.shape[axis] == 1 and (left_index > 0 or right_index > 0): 

852 # Extending singleton dimension for 'reflect' is legacy 

853 # behavior; it really should raise an error. 

854 edge_pair = _get_edges(padded, axis, (left_index, right_index)) 

855 _set_pad_area( 

856 padded, axis, (left_index, right_index), edge_pair) 

857 continue 

858 

859 roi = _view_roi(padded, original_area_slice, axis) 

860 while left_index > 0 or right_index > 0: 

861 # Iteratively pad until dimension is filled with reflected 

862 # values. This is necessary if the pad area is larger than 

863 # the length of the original values in the current dimension. 

864 left_index, right_index = _set_reflect_both( 

865 roi, axis, (left_index, right_index), 

866 method, include_edge 

867 ) 

868 

869 elif mode == "wrap": 

870 for axis, (left_index, right_index) in zip(axes, pad_width): 

871 roi = _view_roi(padded, original_area_slice, axis) 

872 while left_index > 0 or right_index > 0: 

873 # Iteratively pad until dimension is filled with wrapped 

874 # values. This is necessary if the pad area is larger than 

875 # the length of the original values in the current dimension. 

876 left_index, right_index = _set_wrap_both( 

877 roi, axis, (left_index, right_index)) 

878 

879 return padded