Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/numpy/lib/_arraypad_impl.py: 8%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

239 statements  

1""" 

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

3of an n-dimensional array. 

4 

5""" 

6import typing 

7 

8import numpy as np 

9from numpy._core.overrides import array_function_dispatch 

10from numpy.lib._index_tricks_impl import ndindex 

11 

12__all__ = ['pad'] 

13 

14 

15############################################################################### 

16# Private utility functions. 

17 

18 

19def _round_if_needed(arr, dtype): 

20 """ 

21 Rounds arr inplace if destination dtype is integer. 

22 

23 Parameters 

24 ---------- 

25 arr : ndarray 

26 Input array. 

27 dtype : dtype 

28 The dtype of the destination array. 

29 """ 

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

31 arr.round(out=arr) 

32 

33 

34def _slice_at_axis(sl, axis): 

35 """ 

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

37 

38 Parameters 

39 ---------- 

40 sl : slice 

41 The slice for the given dimension. 

42 axis : int 

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

44 "unsliced". 

45 

46 Returns 

47 ------- 

48 sl : tuple of slices 

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

50 

51 Examples 

52 -------- 

53 >>> np._slice_at_axis(slice(None, 3, -1), 1) 

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

55 """ 

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

57 

58 

59def _view_roi(array, original_area_slice, axis): 

60 """ 

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

62 

63 When padding multiple dimensions iteratively corner values are 

64 unnecessarily overwritten multiple times. This function reduces the 

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

66 

67 Parameters 

68 ---------- 

69 array : ndarray 

70 The array with the region of interest. 

71 original_area_slice : tuple of slices 

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

73 axis : int 

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

75 `axis` + 1. 

76 

77 Returns 

78 ------- 

79 roi : ndarray 

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

81 """ 

82 axis += 1 

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

84 return array[sl] 

85 

86 

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

88 """ 

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

90 

91 Parameters 

92 ---------- 

93 array : ndarray 

94 Array to grow. 

95 pad_width : sequence of tuple[int, int] 

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

97 fill_value : scalar, optional 

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

99 the pad area left undefined. 

100 

101 Returns 

102 ------- 

103 padded : ndarray 

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

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

106 original_area_slice : tuple 

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

108 """ 

109 # Allocate grown array 

110 new_shape = tuple( 

111 left + size + right 

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

113 ) 

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

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

116 

117 if fill_value is not None: 

118 padded.fill(fill_value) 

119 

120 # Copy old array into correct space 

121 original_area_slice = tuple( 

122 slice(left, left + size) 

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

124 ) 

125 padded[original_area_slice] = array 

126 

127 return padded, original_area_slice 

128 

129 

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

131 """ 

132 Set empty-padded area in given dimension. 

133 

134 Parameters 

135 ---------- 

136 padded : ndarray 

137 Array with the pad area which is modified inplace. 

138 axis : int 

139 Dimension with the pad area to set. 

140 width_pair : (int, int) 

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

142 dimension. 

143 value_pair : tuple of scalars or ndarrays 

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

145 broadcastable to the shape of `arr`. 

146 """ 

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

148 padded[left_slice] = value_pair[0] 

149 

150 right_slice = _slice_at_axis( 

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

152 padded[right_slice] = value_pair[1] 

153 

154 

155def _get_edges(padded, axis, width_pair): 

156 """ 

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

158 

159 Parameters 

160 ---------- 

161 padded : ndarray 

162 Empty-padded array. 

163 axis : int 

164 Dimension in which the edges are considered. 

165 width_pair : (int, int) 

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

167 dimension. 

168 

169 Returns 

170 ------- 

171 left_edge, right_edge : ndarray 

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

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

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

175 """ 

176 left_index = width_pair[0] 

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

178 left_edge = padded[left_slice] 

179 

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

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

182 right_edge = padded[right_slice] 

183 

184 return left_edge, right_edge 

185 

186 

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

188 """ 

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

190 

191 Parameters 

192 ---------- 

193 padded : ndarray 

194 Empty-padded array. 

195 axis : int 

196 Dimension in which the ramps are constructed. 

197 width_pair : (int, int) 

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

199 dimension. 

200 end_value_pair : (scalar, scalar) 

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

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

203 

204 Returns 

205 ------- 

206 left_ramp, right_ramp : ndarray 

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

208 """ 

209 edge_pair = _get_edges(padded, axis, width_pair) 

210 

211 left_ramp, right_ramp = ( 

212 np.linspace( 

213 start=end_value, 

214 stop=edge.squeeze(axis), # Dimension is replaced by linspace 

215 num=width, 

216 endpoint=False, 

217 dtype=padded.dtype, 

218 axis=axis 

219 ) 

220 for end_value, edge, width in zip( 

221 end_value_pair, edge_pair, width_pair 

222 ) 

223 ) 

224 

225 # Reverse linear space in appropriate dimension 

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

227 

228 return left_ramp, right_ramp 

229 

230 

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

232 """ 

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

234 

235 Parameters 

236 ---------- 

237 padded : ndarray 

238 Empty-padded array. 

239 axis : int 

240 Dimension in which the statistic is calculated. 

241 width_pair : (int, int) 

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

243 dimension. 

244 length_pair : 2-element sequence of None or int 

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

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

247 valid area in `padded` is considered. 

248 stat_func : function 

249 Function to compute statistic. The expected signature is 

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

251 

252 Returns 

253 ------- 

254 left_stat, right_stat : ndarray 

255 Calculated statistic for both sides of `padded`. 

256 """ 

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

258 left_index = width_pair[0] 

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

260 # as well as its length 

261 max_length = right_index - left_index 

262 

263 # Limit stat_lengths to max_length 

264 left_length, right_length = length_pair 

265 if left_length is None or max_length < left_length: 

266 left_length = max_length 

267 if right_length is None or max_length < right_length: 

268 right_length = max_length 

269 

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

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

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

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

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

275 

276 # Calculate statistic for the left side 

277 left_slice = _slice_at_axis( 

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

279 left_chunk = padded[left_slice] 

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

281 _round_if_needed(left_stat, padded.dtype) 

282 

283 if left_length == right_length == max_length: 

284 # return early as right_stat must be identical to left_stat 

285 return left_stat, left_stat 

286 

287 # Calculate statistic for the right side 

288 right_slice = _slice_at_axis( 

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

290 right_chunk = padded[right_slice] 

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

292 _round_if_needed(right_stat, padded.dtype) 

293 

294 return left_stat, right_stat 

295 

296 

297def _set_reflect_both(padded, axis, width_pair, method, 

298 original_period, include_edge=False): 

299 """ 

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

301 

302 Parameters 

303 ---------- 

304 padded : ndarray 

305 Input array of arbitrary shape. 

306 axis : int 

307 Axis along which to pad `arr`. 

308 width_pair : (int, int) 

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

310 dimension. 

311 method : str 

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

313 original_period : int 

314 Original length of data on `axis` of `arr`. 

315 include_edge : bool 

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

317 value forms the symmetric axis to the reflection. 

318 

319 Returns 

320 ------- 

321 pad_amt : tuple of ints, length 2 

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

323 both 0, padding is done in this dimension. 

324 """ 

325 left_pad, right_pad = width_pair 

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

327 

328 if include_edge: 

329 # Avoid wrapping with only a subset of the original area 

330 # by ensuring period can only be a multiple of the original 

331 # area's length. 

332 old_length = old_length // original_period * original_period 

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

334 edge_offset = 1 

335 else: 

336 # Avoid wrapping with only a subset of the original area 

337 # by ensuring period can only be a multiple of the original 

338 # area's length. 

339 old_length = ((old_length - 1) // (original_period - 1) 

340 * (original_period - 1) + 1) 

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

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

343 

344 if left_pad > 0: 

345 # Pad with reflected values on left side: 

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

347 chunk_length = min(old_length, left_pad) 

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

349 stop = left_pad - edge_offset 

350 start = stop + chunk_length 

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

352 left_chunk = padded[left_slice] 

353 

354 if method == "odd": 

355 # Negate chunk and align with edge 

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

357 left_chunk = 2 * padded[edge_slice] - left_chunk 

358 

359 # Insert chunk into padded area 

360 start = left_pad - chunk_length 

361 stop = left_pad 

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

363 padded[pad_area] = left_chunk 

364 # Adjust pointer to left edge for next iteration 

365 left_pad -= chunk_length 

366 

367 if right_pad > 0: 

368 # Pad with reflected values on right side: 

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

370 chunk_length = min(old_length, right_pad) 

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

372 start = -right_pad + edge_offset - 2 

373 stop = start - chunk_length 

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

375 right_chunk = padded[right_slice] 

376 

377 if method == "odd": 

378 # Negate chunk and align with edge 

379 edge_slice = _slice_at_axis( 

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

381 right_chunk = 2 * padded[edge_slice] - right_chunk 

382 

383 # Insert chunk into padded area 

384 start = padded.shape[axis] - right_pad 

385 stop = start + chunk_length 

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

387 padded[pad_area] = right_chunk 

388 # Adjust pointer to right edge for next iteration 

389 right_pad -= chunk_length 

390 

391 return left_pad, right_pad 

392 

393 

394def _set_wrap_both(padded, axis, width_pair, original_period): 

395 """ 

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

397 

398 Parameters 

399 ---------- 

400 padded : ndarray 

401 Input array of arbitrary shape. 

402 axis : int 

403 Axis along which to pad `arr`. 

404 width_pair : (int, int) 

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

406 dimension. 

407 original_period : int 

408 Original length of data on `axis` of `arr`. 

409 

410 Returns 

411 ------- 

412 pad_amt : tuple of ints, length 2 

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

414 both 0, padding is done in this dimension. 

415 """ 

416 left_pad, right_pad = width_pair 

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

418 # Avoid wrapping with only a subset of the original area by ensuring period 

419 # can only be a multiple of the original area's length. 

420 period = period // original_period * original_period 

421 

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

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

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

425 # these variables. 

426 new_left_pad = 0 

427 new_right_pad = 0 

428 

429 if left_pad > 0: 

430 # Pad with wrapped values on left side 

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

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

433 # pad area. 

434 slice_end = left_pad + period 

435 slice_start = slice_end - min(period, left_pad) 

436 right_slice = _slice_at_axis(slice(slice_start, slice_end), axis) 

437 right_chunk = padded[right_slice] 

438 

439 if left_pad > period: 

440 # Chunk is smaller than pad area 

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

442 new_left_pad = left_pad - period 

443 else: 

444 # Chunk matches pad area 

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

446 padded[pad_area] = right_chunk 

447 

448 if right_pad > 0: 

449 # Pad with wrapped values on right side 

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

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

452 # pad area. 

453 slice_start = -right_pad - period 

454 slice_end = slice_start + min(period, right_pad) 

455 left_slice = _slice_at_axis(slice(slice_start, slice_end), axis) 

456 left_chunk = padded[left_slice] 

457 

458 if right_pad > period: 

459 # Chunk is smaller than pad area 

460 pad_area = _slice_at_axis( 

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

462 new_right_pad = right_pad - period 

463 else: 

464 # Chunk matches pad area 

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

466 padded[pad_area] = left_chunk 

467 

468 return new_left_pad, new_right_pad 

469 

470 

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

472 """ 

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

474 

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

476 `pad_width` for iteration in pairs. 

477 

478 Parameters 

479 ---------- 

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

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

482 ndim : int 

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

484 as_index : bool, optional 

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

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

487 

488 Returns 

489 ------- 

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

491 The broadcasted version of `x`. 

492 

493 Raises 

494 ------ 

495 ValueError 

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

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

498 """ 

499 if x is None: 

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

501 # with an AttributeError 

502 return ((None, None),) * ndim 

503 

504 x = np.array(x) 

505 if as_index: 

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

507 

508 if x.ndim < 3: 

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

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

511 # but is currently slower 

512 

513 if x.size == 1: 

514 # x was supplied as a single value 

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

516 if as_index and x < 0: 

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

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

519 

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

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

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

523 # which should be broadcasted to a pair, 

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

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

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

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

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

529 

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

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

532 

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

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

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

536 

537 

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

539 return (array,) 

540 

541 

542############################################################################### 

543# Public functions 

544 

545 

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

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

548 """ 

549 Pad an array. 

550 

551 Parameters 

552 ---------- 

553 array : array_like of rank N 

554 The array to pad. 

555 pad_width : {sequence, array_like, int, dict} 

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

557 ``((before_1, after_1), ... (before_N, after_N))`` unique pad widths 

558 for each axis. 

559 ``(before, after)`` or ``((before, after),)`` yields same before 

560 and after pad for each axis. 

561 ``(pad,)`` or ``int`` is a shortcut for before = after = pad width 

562 for all axes. 

563 If a ``dict``, each key is an axis and its corresponding value is an ``int`` or 

564 ``int`` pair describing the padding ``(before, after)`` or ``pad`` width for 

565 that axis. 

566 mode : str or function, optional 

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

568 

569 'constant' (default) 

570 Pads with a constant value. 

571 'edge' 

572 Pads with the edge values of array. 

573 'linear_ramp' 

574 Pads with the linear ramp between end_value and the 

575 array edge value. 

576 'maximum' 

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

578 vector along each axis. 

579 'mean' 

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

581 vector along each axis. 

582 'median' 

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

584 vector along each axis. 

585 'minimum' 

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

587 vector along each axis. 

588 'reflect' 

589 Pads with the reflection of the vector mirrored on 

590 the first and last values of the vector along each 

591 axis. 

592 'symmetric' 

593 Pads with the reflection of the vector mirrored 

594 along the edge of the array. 

595 'wrap' 

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

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

598 end values are used to pad the beginning. 

599 'empty' 

600 Pads with undefined values. 

601 

602 <function> 

603 Padding function, see Notes. 

604 stat_length : sequence or int, optional 

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

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

607 

608 ``((before_1, after_1), ... (before_N, after_N))`` unique statistic 

609 lengths for each axis. 

610 

611 ``(before, after)`` or ``((before, after),)`` yields same before 

612 and after statistic lengths for each axis. 

613 

614 ``(stat_length,)`` or ``int`` is a shortcut for 

615 ``before = after = statistic`` length for all axes. 

616 

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

618 constant_values : sequence or scalar, optional 

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

620 axis. 

621 

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

623 for each axis. 

624 

625 ``(before, after)`` or ``((before, after),)`` yields same before 

626 and after constants for each axis. 

627 

628 ``(constant,)`` or ``constant`` is a shortcut for 

629 ``before = after = constant`` for all axes. 

630 

631 Default is 0. 

632 end_values : sequence or scalar, optional 

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

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

635 

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

637 for each axis. 

638 

639 ``(before, after)`` or ``((before, after),)`` yields same before 

640 and after end values for each axis. 

641 

642 ``(constant,)`` or ``constant`` is a shortcut for 

643 ``before = after = constant`` for all axes. 

644 

645 Default is 0. 

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

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

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

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

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

651 

652 Returns 

653 ------- 

654 pad : ndarray 

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

656 according to `pad_width`. 

657 

658 Notes 

659 ----- 

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

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

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

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

664 

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

666 has the following signature:: 

667 

668 padding_func(vector, iaxis_pad_width, iaxis, kwargs) 

669 

670 where 

671 

672 vector : ndarray 

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

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

675 iaxis_pad_width : tuple 

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

677 values padded at the beginning of vector where 

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

679 the end of vector. 

680 iaxis : int 

681 The axis currently being calculated. 

682 kwargs : dict 

683 Any keyword arguments the function requires. 

684 

685 Examples 

686 -------- 

687 >>> import numpy as np 

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

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

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

691 

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

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

694 

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

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

697 

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

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

700 

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

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

703 

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

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

706 

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

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

709 array([[1, 1, 1, 2, 1, 1, 1], 

710 [1, 1, 1, 2, 1, 1, 1], 

711 [1, 1, 1, 2, 1, 1, 1], 

712 [1, 1, 1, 2, 1, 1, 1], 

713 [3, 3, 3, 4, 3, 3, 3], 

714 [1, 1, 1, 2, 1, 1, 1], 

715 [1, 1, 1, 2, 1, 1, 1]]) 

716 

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

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

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

720 

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

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

723 

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

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

726 

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

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

729 

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

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

732 

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

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

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

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

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

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

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

740 array([[10, 10, 10, 10, 10, 10, 10], 

741 [10, 10, 10, 10, 10, 10, 10], 

742 [10, 10, 0, 1, 2, 10, 10], 

743 [10, 10, 3, 4, 5, 10, 10], 

744 [10, 10, 10, 10, 10, 10, 10], 

745 [10, 10, 10, 10, 10, 10, 10]]) 

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

747 array([[100, 100, 100, 100, 100, 100, 100], 

748 [100, 100, 100, 100, 100, 100, 100], 

749 [100, 100, 0, 1, 2, 100, 100], 

750 [100, 100, 3, 4, 5, 100, 100], 

751 [100, 100, 100, 100, 100, 100, 100], 

752 [100, 100, 100, 100, 100, 100, 100]]) 

753 

754 >>> a = np.arange(1, 7).reshape(2, 3) 

755 >>> np.pad(a, {1: (1, 2)}) 

756 array([[0, 1, 2, 3, 0, 0], 

757 [0, 4, 5, 6, 0, 0]]) 

758 >>> np.pad(a, {-1: 2}) 

759 array([[0, 0, 1, 2, 3, 0, 0], 

760 [0, 0, 4, 5, 6, 0, 0]]) 

761 >>> np.pad(a, {0: (3, 0)}) 

762 array([[0, 0, 0], 

763 [0, 0, 0], 

764 [0, 0, 0], 

765 [1, 2, 3], 

766 [4, 5, 6]]) 

767 >>> np.pad(a, {0: (3, 0), 1: 2}) 

768 array([[0, 0, 0, 0, 0, 0, 0], 

769 [0, 0, 0, 0, 0, 0, 0], 

770 [0, 0, 0, 0, 0, 0, 0], 

771 [0, 0, 1, 2, 3, 0, 0], 

772 [0, 0, 4, 5, 6, 0, 0]]) 

773 """ 

774 array = np.asarray(array) 

775 if isinstance(pad_width, dict): 

776 seq = [(0, 0)] * array.ndim 

777 for axis, width in pad_width.items(): 

778 match width: 

779 case int(both): 

780 seq[axis] = both, both 

781 case tuple((int(before), int(after))): 

782 seq[axis] = before, after 

783 case _ as invalid: 

784 typing.assert_never(invalid) 

785 pad_width = seq 

786 pad_width = np.asarray(pad_width) 

787 

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

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

790 

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

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

793 

794 if callable(mode): 

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

796 function = mode 

797 # Create a new zero padded array 

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

799 # And apply along each axis 

800 

801 for axis in range(padded.ndim): 

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

803 # function operates inplace on the padded array. 

804 

805 # view with the iteration axis at the end 

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

807 

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

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

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

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

812 for ind in inds: 

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

814 

815 return padded 

816 

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

818 allowed_kwargs = { 

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

820 'constant': ['constant_values'], 

821 'linear_ramp': ['end_values'], 

822 'maximum': ['stat_length'], 

823 'mean': ['stat_length'], 

824 'median': ['stat_length'], 

825 'minimum': ['stat_length'], 

826 'reflect': ['reflect_type'], 

827 'symmetric': ['reflect_type'], 

828 } 

829 try: 

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

831 except KeyError: 

832 raise ValueError(f"mode '{mode}' is not supported") from None 

833 if unsupported_kwargs: 

834 raise ValueError("unsupported keyword arguments for mode " 

835 f"'{mode}': {unsupported_kwargs}") 

836 

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

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

839 

840 # Create array with final shape and original values 

841 # (padded area is undefined) 

842 padded, original_area_slice = _pad_simple(array, pad_width) 

843 # And prepare iteration over all dimensions 

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

845 axes = range(padded.ndim) 

846 

847 if mode == "constant": 

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

849 values = _as_pairs(values, padded.ndim) 

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

851 roi = _view_roi(padded, original_area_slice, axis) 

852 _set_pad_area(roi, axis, width_pair, value_pair) 

853 

854 elif mode == "empty": 

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

856 

857 elif array.size == 0: 

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

859 # modes depend on `array` not being empty 

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

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

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

863 raise ValueError( 

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

865 "'constant' or 'empty'" 

866 ) 

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

868 # returned the correct result 

869 

870 elif mode == "edge": 

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

872 roi = _view_roi(padded, original_area_slice, axis) 

873 edge_pair = _get_edges(roi, axis, width_pair) 

874 _set_pad_area(roi, axis, width_pair, edge_pair) 

875 

876 elif mode == "linear_ramp": 

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

878 end_values = _as_pairs(end_values, padded.ndim) 

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

880 roi = _view_roi(padded, original_area_slice, axis) 

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

882 _set_pad_area(roi, axis, width_pair, ramp_pair) 

883 

884 elif mode in stat_functions: 

885 func = stat_functions[mode] 

886 length = kwargs.get("stat_length") 

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

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

889 roi = _view_roi(padded, original_area_slice, axis) 

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

891 _set_pad_area(roi, axis, width_pair, stat_pair) 

892 

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

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

895 include_edge = mode == "symmetric" 

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

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

898 # Extending singleton dimension for 'reflect' is legacy 

899 # behavior; it really should raise an error. 

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

901 _set_pad_area( 

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

903 continue 

904 

905 roi = _view_roi(padded, original_area_slice, axis) 

906 while left_index > 0 or right_index > 0: 

907 # Iteratively pad until dimension is filled with reflected 

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

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

910 left_index, right_index = _set_reflect_both( 

911 roi, axis, (left_index, right_index), 

912 method, array.shape[axis], include_edge 

913 ) 

914 

915 elif mode == "wrap": 

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

917 roi = _view_roi(padded, original_area_slice, axis) 

918 original_period = padded.shape[axis] - right_index - left_index 

919 while left_index > 0 or right_index > 0: 

920 # Iteratively pad until dimension is filled with wrapped 

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

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

923 left_index, right_index = _set_wrap_both( 

924 roi, axis, (left_index, right_index), original_period) 

925 

926 return padded