1""" 2The arraypad module contains a group of functions to pad values onto the edges 3of an n-dimensional array. 4 5""" 6from __future__ import division, absolute_import, print_function 7 8import numpy as np 9 10 11__all__ = ['pad'] 12 13 14############################################################################### 15# Private utility functions. 16 17 18def _arange_ndarray(arr, shape, axis, reverse=False): 19 """ 20 Create an ndarray of `shape` with increments along specified `axis` 21 22 Parameters 23 ---------- 24 arr : ndarray 25 Input array of arbitrary shape. 26 shape : tuple of ints 27 Shape of desired array. Should be equivalent to `arr.shape` except 28 `shape[axis]` which may have any positive value. 29 axis : int 30 Axis to increment along. 31 reverse : bool 32 If False, increment in a positive fashion from 1 to `shape[axis]`, 33 inclusive. If True, the bounds are the same but the order reversed. 34 35 Returns 36 ------- 37 padarr : ndarray 38 Output array sized to pad `arr` along `axis`, with linear range from 39 1 to `shape[axis]` along specified `axis`. 40 41 Notes 42 ----- 43 The range is deliberately 1-indexed for this specific use case. Think of 44 this algorithm as broadcasting `np.arange` to a single `axis` of an 45 arbitrarily shaped ndarray. 46 47 """ 48 initshape = tuple(1 if i != axis else shape[axis] 49 for (i, x) in enumerate(arr.shape)) 50 if not reverse: 51 padarr = np.arange(1, shape[axis] + 1) 52 else: 53 padarr = np.arange(shape[axis], 0, -1) 54 padarr = padarr.reshape(initshape) 55 for i, dim in enumerate(shape): 56 if padarr.shape[i] != dim: 57 padarr = padarr.repeat(dim, axis=i) 58 return padarr 59 60 61def _round_ifneeded(arr, dtype): 62 """ 63 Rounds arr inplace if destination dtype is integer. 64 65 Parameters 66 ---------- 67 arr : ndarray 68 Input array. 69 dtype : dtype 70 The dtype of the destination array. 71 72 """ 73 if np.issubdtype(dtype, np.integer): 74 arr.round(out=arr) 75 76 77def _prepend_const(arr, pad_amt, val, axis=-1): 78 """ 79 Prepend constant `val` along `axis` of `arr`. 80 81 Parameters 82 ---------- 83 arr : ndarray 84 Input array of arbitrary shape. 85 pad_amt : int 86 Amount of padding to prepend. 87 val : scalar 88 Constant value to use. For best results should be of type `arr.dtype`; 89 if not `arr.dtype` will be cast to `arr.dtype`. 90 axis : int 91 Axis along which to pad `arr`. 92 93 Returns 94 ------- 95 padarr : ndarray 96 Output array, with `pad_amt` constant `val` prepended along `axis`. 97 98 """ 99 if pad_amt == 0: 100 return arr 101 padshape = tuple(x if i != axis else pad_amt 102 for (i, x) in enumerate(arr.shape)) 103 if val == 0: 104 return np.concatenate((np.zeros(padshape, dtype=arr.dtype), arr), 105 axis=axis) 106 else: 107 return np.concatenate(((np.zeros(padshape) + val).astype(arr.dtype), 108 arr), axis=axis) 109 110 111def _append_const(arr, pad_amt, val, axis=-1): 112 """ 113 Append constant `val` along `axis` of `arr`. 114 115 Parameters 116 ---------- 117 arr : ndarray 118 Input array of arbitrary shape. 119 pad_amt : int 120 Amount of padding to append. 121 val : scalar 122 Constant value to use. For best results should be of type `arr.dtype`; 123 if not `arr.dtype` will be cast to `arr.dtype`. 124 axis : int 125 Axis along which to pad `arr`. 126 127 Returns 128 ------- 129 padarr : ndarray 130 Output array, with `pad_amt` constant `val` appended along `axis`. 131 132 """ 133 if pad_amt == 0: 134 return arr 135 padshape = tuple(x if i != axis else pad_amt 136 for (i, x) in enumerate(arr.shape)) 137 if val == 0: 138 return np.concatenate((arr, np.zeros(padshape, dtype=arr.dtype)), 139 axis=axis) 140 else: 141 return np.concatenate( 142 (arr, (np.zeros(padshape) + val).astype(arr.dtype)), axis=axis) 143 144 145def _prepend_edge(arr, pad_amt, axis=-1): 146 """ 147 Prepend `pad_amt` to `arr` along `axis` by extending edge values. 148 149 Parameters 150 ---------- 151 arr : ndarray 152 Input array of arbitrary shape. 153 pad_amt : int 154 Amount of padding to prepend. 155 axis : int 156 Axis along which to pad `arr`. 157 158 Returns 159 ------- 160 padarr : ndarray 161 Output array, extended by `pad_amt` edge values appended along `axis`. 162 163 """ 164 if pad_amt == 0: 165 return arr 166 167 edge_slice = tuple(slice(None) if i != axis else 0 168 for (i, x) in enumerate(arr.shape)) 169 170 # Shape to restore singleton dimension after slicing 171 pad_singleton = tuple(x if i != axis else 1 172 for (i, x) in enumerate(arr.shape)) 173 edge_arr = arr[edge_slice].reshape(pad_singleton) 174 return np.concatenate((edge_arr.repeat(pad_amt, axis=axis), arr), 175 axis=axis) 176 177 178def _append_edge(arr, pad_amt, axis=-1): 179 """ 180 Append `pad_amt` to `arr` along `axis` by extending edge values. 181 182 Parameters 183 ---------- 184 arr : ndarray 185 Input array of arbitrary shape. 186 pad_amt : int 187 Amount of padding to append. 188 axis : int 189 Axis along which to pad `arr`. 190 191 Returns 192 ------- 193 padarr : ndarray 194 Output array, extended by `pad_amt` edge values prepended along 195 `axis`. 196 197 """ 198 if pad_amt == 0: 199 return arr 200 201 edge_slice = tuple(slice(None) if i != axis else arr.shape[axis] - 1 202 for (i, x) in enumerate(arr.shape)) 203 204 # Shape to restore singleton dimension after slicing 205 pad_singleton = tuple(x if i != axis else 1 206 for (i, x) in enumerate(arr.shape)) 207 edge_arr = arr[edge_slice].reshape(pad_singleton) 208 return np.concatenate((arr, edge_arr.repeat(pad_amt, axis=axis)), 209 axis=axis) 210 211 212def _prepend_ramp(arr, pad_amt, end, axis=-1): 213 """ 214 Prepend linear ramp along `axis`. 215 216 Parameters 217 ---------- 218 arr : ndarray 219 Input array of arbitrary shape. 220 pad_amt : int 221 Amount of padding to prepend. 222 end : scalar 223 Constal value to use. For best results should be of type `arr.dtype`; 224 if not `arr.dtype` will be cast to `arr.dtype`. 225 axis : int 226 Axis along which to pad `arr`. 227 228 Returns 229 ------- 230 padarr : ndarray 231 Output array, with `pad_amt` values prepended along `axis`. The 232 prepended region ramps linearly from the edge value to `end`. 233 234 """ 235 if pad_amt == 0: 236 return arr 237 238 # Generate shape for final concatenated array 239 padshape = tuple(x if i != axis else pad_amt 240 for (i, x) in enumerate(arr.shape)) 241 242 # Generate an n-dimensional array incrementing along `axis` 243 ramp_arr = _arange_ndarray(arr, padshape, axis, 244 reverse=True).astype(np.float64) 245 246 # Appropriate slicing to extract n-dimensional edge along `axis` 247 edge_slice = tuple(slice(None) if i != axis else 0 248 for (i, x) in enumerate(arr.shape)) 249 250 # Shape to restore singleton dimension after slicing 251 pad_singleton = tuple(x if i != axis else 1 252 for (i, x) in enumerate(arr.shape)) 253 254 # Extract edge, reshape to original rank, and extend along `axis` 255 edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis) 256 257 # Linear ramp 258 slope = (end - edge_pad) / float(pad_amt) 259 ramp_arr = ramp_arr * slope 260 ramp_arr += edge_pad 261 _round_ifneeded(ramp_arr, arr.dtype) 262 263 # Ramp values will most likely be float, cast them to the same type as arr 264 return np.concatenate((ramp_arr.astype(arr.dtype), arr), axis=axis) 265 266 267def _append_ramp(arr, pad_amt, end, axis=-1): 268 """ 269 Append linear ramp along `axis`. 270 271 Parameters 272 ---------- 273 arr : ndarray 274 Input array of arbitrary shape. 275 pad_amt : int 276 Amount of padding to append. 277 end : scalar 278 Constal value to use. For best results should be of type `arr.dtype`; 279 if not `arr.dtype` will be cast to `arr.dtype`. 280 axis : int 281 Axis along which to pad `arr`. 282 283 Returns 284 ------- 285 padarr : ndarray 286 Output array, with `pad_amt` values appended along `axis`. The 287 appended region ramps linearly from the edge value to `end`. 288 289 """ 290 if pad_amt == 0: 291 return arr 292 293 # Generate shape for final concatenated array 294 padshape = tuple(x if i != axis else pad_amt 295 for (i, x) in enumerate(arr.shape)) 296 297 # Generate an n-dimensional array incrementing along `axis` 298 ramp_arr = _arange_ndarray(arr, padshape, axis, 299 reverse=False).astype(np.float64) 300 301 # Slice a chunk from the edge to calculate stats on 302 edge_slice = tuple(slice(None) if i != axis else -1 303 for (i, x) in enumerate(arr.shape)) 304 305 # Shape to restore singleton dimension after slicing 306 pad_singleton = tuple(x if i != axis else 1 307 for (i, x) in enumerate(arr.shape)) 308 309 # Extract edge, reshape to original rank, and extend along `axis` 310 edge_pad = arr[edge_slice].reshape(pad_singleton).repeat(pad_amt, axis) 311 312 # Linear ramp 313 slope = (end - edge_pad) / float(pad_amt) 314 ramp_arr = ramp_arr * slope 315 ramp_arr += edge_pad 316 _round_ifneeded(ramp_arr, arr.dtype) 317 318 # Ramp values will most likely be float, cast them to the same type as arr 319 return np.concatenate((arr, ramp_arr.astype(arr.dtype)), axis=axis) 320 321 322def _prepend_max(arr, pad_amt, num, axis=-1): 323 """ 324 Prepend `pad_amt` maximum values along `axis`. 325 326 Parameters 327 ---------- 328 arr : ndarray 329 Input array of arbitrary shape. 330 pad_amt : int 331 Amount of padding to prepend. 332 num : int 333 Depth into `arr` along `axis` to calculate maximum. 334 Range: [1, `arr.shape[axis]`] or None (entire axis) 335 axis : int 336 Axis along which to pad `arr`. 337 338 Returns 339 ------- 340 padarr : ndarray 341 Output array, with `pad_amt` values appended along `axis`. The 342 prepended region is the maximum of the first `num` values along 343 `axis`. 344 345 """ 346 if pad_amt == 0: 347 return arr 348 349 # Equivalent to edge padding for single value, so do that instead 350 if num == 1: 351 return _prepend_edge(arr, pad_amt, axis) 352 353 # Use entire array if `num` is too large 354 if num is not None: 355 if num >= arr.shape[axis]: 356 num = None 357 358 # Slice a chunk from the edge to calculate stats on 359 max_slice = tuple(slice(None) if i != axis else slice(num) 360 for (i, x) in enumerate(arr.shape)) 361 362 # Shape to restore singleton dimension after slicing 363 pad_singleton = tuple(x if i != axis else 1 364 for (i, x) in enumerate(arr.shape)) 365 366 # Extract slice, calculate max, reshape to add singleton dimension back 367 max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton) 368 369 # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` 370 return np.concatenate((max_chunk.repeat(pad_amt, axis=axis), arr), 371 axis=axis) 372 373 374def _append_max(arr, pad_amt, num, axis=-1): 375 """ 376 Pad one `axis` of `arr` with the maximum of the last `num` elements. 377 378 Parameters 379 ---------- 380 arr : ndarray 381 Input array of arbitrary shape. 382 pad_amt : int 383 Amount of padding to append. 384 num : int 385 Depth into `arr` along `axis` to calculate maximum. 386 Range: [1, `arr.shape[axis]`] or None (entire axis) 387 axis : int 388 Axis along which to pad `arr`. 389 390 Returns 391 ------- 392 padarr : ndarray 393 Output array, with `pad_amt` values appended along `axis`. The 394 appended region is the maximum of the final `num` values along `axis`. 395 396 """ 397 if pad_amt == 0: 398 return arr 399 400 # Equivalent to edge padding for single value, so do that instead 401 if num == 1: 402 return _append_edge(arr, pad_amt, axis) 403 404 # Use entire array if `num` is too large 405 if num is not None: 406 if num >= arr.shape[axis]: 407 num = None 408 409 # Slice a chunk from the edge to calculate stats on 410 end = arr.shape[axis] - 1 411 if num is not None: 412 max_slice = tuple( 413 slice(None) if i != axis else slice(end, end - num, -1) 414 for (i, x) in enumerate(arr.shape)) 415 else: 416 max_slice = tuple(slice(None) for x in arr.shape) 417 418 # Shape to restore singleton dimension after slicing 419 pad_singleton = tuple(x if i != axis else 1 420 for (i, x) in enumerate(arr.shape)) 421 422 # Extract slice, calculate max, reshape to add singleton dimension back 423 max_chunk = arr[max_slice].max(axis=axis).reshape(pad_singleton) 424 425 # Concatenate `arr` with `max_chunk`, extended along `axis` by `pad_amt` 426 return np.concatenate((arr, max_chunk.repeat(pad_amt, axis=axis)), 427 axis=axis) 428 429 430def _prepend_mean(arr, pad_amt, num, axis=-1): 431 """ 432 Prepend `pad_amt` mean values along `axis`. 433 434 Parameters 435 ---------- 436 arr : ndarray 437 Input array of arbitrary shape. 438 pad_amt : int 439 Amount of padding to prepend. 440 num : int 441 Depth into `arr` along `axis` to calculate mean. 442 Range: [1, `arr.shape[axis]`] or None (entire axis) 443 axis : int 444 Axis along which to pad `arr`. 445 446 Returns 447 ------- 448 padarr : ndarray 449 Output array, with `pad_amt` values prepended along `axis`. The 450 prepended region is the mean of the first `num` values along `axis`. 451 452 """ 453 if pad_amt == 0: 454 return arr 455 456 # Equivalent to edge padding for single value, so do that instead 457 if num == 1: 458 return _prepend_edge(arr, pad_amt, axis) 459 460 # Use entire array if `num` is too large 461 if num is not None: 462 if num >= arr.shape[axis]: 463 num = None 464 465 # Slice a chunk from the edge to calculate stats on 466 mean_slice = tuple(slice(None) if i != axis else slice(num) 467 for (i, x) in enumerate(arr.shape)) 468 469 # Shape to restore singleton dimension after slicing 470 pad_singleton = tuple(x if i != axis else 1 471 for (i, x) in enumerate(arr.shape)) 472 473 # Extract slice, calculate mean, reshape to add singleton dimension back 474 mean_chunk = arr[mean_slice].mean(axis).reshape(pad_singleton) 475 _round_ifneeded(mean_chunk, arr.dtype) 476 477 # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` 478 return np.concatenate((mean_chunk.repeat(pad_amt, axis).astype(arr.dtype), 479 arr), axis=axis) 480 481 482def _append_mean(arr, pad_amt, num, axis=-1): 483 """ 484 Append `pad_amt` mean values along `axis`. 485 486 Parameters 487 ---------- 488 arr : ndarray 489 Input array of arbitrary shape. 490 pad_amt : int 491 Amount of padding to append. 492 num : int 493 Depth into `arr` along `axis` to calculate mean. 494 Range: [1, `arr.shape[axis]`] or None (entire axis) 495 axis : int 496 Axis along which to pad `arr`. 497 498 Returns 499 ------- 500 padarr : ndarray 501 Output array, with `pad_amt` values appended along `axis`. The 502 appended region is the maximum of the final `num` values along `axis`. 503 504 """ 505 if pad_amt == 0: 506 return arr 507 508 # Equivalent to edge padding for single value, so do that instead 509 if num == 1: 510 return _append_edge(arr, pad_amt, axis) 511 512 # Use entire array if `num` is too large 513 if num is not None: 514 if num >= arr.shape[axis]: 515 num = None 516 517 # Slice a chunk from the edge to calculate stats on 518 end = arr.shape[axis] - 1 519 if num is not None: 520 mean_slice = tuple( 521 slice(None) if i != axis else slice(end, end - num, -1) 522 for (i, x) in enumerate(arr.shape)) 523 else: 524 mean_slice = tuple(slice(None) for x in arr.shape) 525 526 # Shape to restore singleton dimension after slicing 527 pad_singleton = tuple(x if i != axis else 1 528 for (i, x) in enumerate(arr.shape)) 529 530 # Extract slice, calculate mean, reshape to add singleton dimension back 531 mean_chunk = arr[mean_slice].mean(axis=axis).reshape(pad_singleton) 532 _round_ifneeded(mean_chunk, arr.dtype) 533 534 # Concatenate `arr` with `mean_chunk`, extended along `axis` by `pad_amt` 535 return np.concatenate( 536 (arr, mean_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis) 537 538 539def _prepend_med(arr, pad_amt, num, axis=-1): 540 """ 541 Prepend `pad_amt` median values along `axis`. 542 543 Parameters 544 ---------- 545 arr : ndarray 546 Input array of arbitrary shape. 547 pad_amt : int 548 Amount of padding to prepend. 549 num : int 550 Depth into `arr` along `axis` to calculate median. 551 Range: [1, `arr.shape[axis]`] or None (entire axis) 552 axis : int 553 Axis along which to pad `arr`. 554 555 Returns 556 ------- 557 padarr : ndarray 558 Output array, with `pad_amt` values prepended along `axis`. The 559 prepended region is the median of the first `num` values along `axis`. 560 561 """ 562 if pad_amt == 0: 563 return arr 564 565 # Equivalent to edge padding for single value, so do that instead 566 if num == 1: 567 return _prepend_edge(arr, pad_amt, axis) 568 569 # Use entire array if `num` is too large 570 if num is not None: 571 if num >= arr.shape[axis]: 572 num = None 573 574 # Slice a chunk from the edge to calculate stats on 575 med_slice = tuple(slice(None) if i != axis else slice(num) 576 for (i, x) in enumerate(arr.shape)) 577 578 # Shape to restore singleton dimension after slicing 579 pad_singleton = tuple(x if i != axis else 1 580 for (i, x) in enumerate(arr.shape)) 581 582 # Extract slice, calculate median, reshape to add singleton dimension back 583 med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton) 584 _round_ifneeded(med_chunk, arr.dtype) 585 586 # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` 587 return np.concatenate( 588 (med_chunk.repeat(pad_amt, axis).astype(arr.dtype), arr), axis=axis) 589 590 591def _append_med(arr, pad_amt, num, axis=-1): 592 """ 593 Append `pad_amt` median values along `axis`. 594 595 Parameters 596 ---------- 597 arr : ndarray 598 Input array of arbitrary shape. 599 pad_amt : int 600 Amount of padding to append. 601 num : int 602 Depth into `arr` along `axis` to calculate median. 603 Range: [1, `arr.shape[axis]`] or None (entire axis) 604 axis : int 605 Axis along which to pad `arr`. 606 607 Returns 608 ------- 609 padarr : ndarray 610 Output array, with `pad_amt` values appended along `axis`. The 611 appended region is the median of the final `num` values along `axis`. 612 613 """ 614 if pad_amt == 0: 615 return arr 616 617 # Equivalent to edge padding for single value, so do that instead 618 if num == 1: 619 return _append_edge(arr, pad_amt, axis) 620 621 # Use entire array if `num` is too large 622 if num is not None: 623 if num >= arr.shape[axis]: 624 num = None 625 626 # Slice a chunk from the edge to calculate stats on 627 end = arr.shape[axis] - 1 628 if num is not None: 629 med_slice = tuple( 630 slice(None) if i != axis else slice(end, end - num, -1) 631 for (i, x) in enumerate(arr.shape)) 632 else: 633 med_slice = tuple(slice(None) for x in arr.shape) 634 635 # Shape to restore singleton dimension after slicing 636 pad_singleton = tuple(x if i != axis else 1 637 for (i, x) in enumerate(arr.shape)) 638 639 # Extract slice, calculate median, reshape to add singleton dimension back 640 med_chunk = np.median(arr[med_slice], axis=axis).reshape(pad_singleton) 641 _round_ifneeded(med_chunk, arr.dtype) 642 643 # Concatenate `arr` with `med_chunk`, extended along `axis` by `pad_amt` 644 return np.concatenate( 645 (arr, med_chunk.repeat(pad_amt, axis).astype(arr.dtype)), axis=axis) 646 647 648def _prepend_min(arr, pad_amt, num, axis=-1): 649 """ 650 Prepend `pad_amt` minimum values along `axis`. 651 652 Parameters 653 ---------- 654 arr : ndarray 655 Input array of arbitrary shape. 656 pad_amt : int 657 Amount of padding to prepend. 658 num : int 659 Depth into `arr` along `axis` to calculate minimum. 660 Range: [1, `arr.shape[axis]`] or None (entire axis) 661 axis : int 662 Axis along which to pad `arr`. 663 664 Returns 665 ------- 666 padarr : ndarray 667 Output array, with `pad_amt` values prepended along `axis`. The 668 prepended region is the minimum of the first `num` values along 669 `axis`. 670 671 """ 672 if pad_amt == 0: 673 return arr 674 675 # Equivalent to edge padding for single value, so do that instead 676 if num == 1: 677 return _prepend_edge(arr, pad_amt, axis) 678 679 # Use entire array if `num` is too large 680 if num is not None: 681 if num >= arr.shape[axis]: 682 num = None 683 684 # Slice a chunk from the edge to calculate stats on 685 min_slice = tuple(slice(None) if i != axis else slice(num) 686 for (i, x) in enumerate(arr.shape)) 687 688 # Shape to restore singleton dimension after slicing 689 pad_singleton = tuple(x if i != axis else 1 690 for (i, x) in enumerate(arr.shape)) 691 692 # Extract slice, calculate min, reshape to add singleton dimension back 693 min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton) 694 695 # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` 696 return np.concatenate((min_chunk.repeat(pad_amt, axis=axis), arr), 697 axis=axis) 698 699 700def _append_min(arr, pad_amt, num, axis=-1): 701 """ 702 Append `pad_amt` median values along `axis`. 703 704 Parameters 705 ---------- 706 arr : ndarray 707 Input array of arbitrary shape. 708 pad_amt : int 709 Amount of padding to append. 710 num : int 711 Depth into `arr` along `axis` to calculate minimum. 712 Range: [1, `arr.shape[axis]`] or None (entire axis) 713 axis : int 714 Axis along which to pad `arr`. 715 716 Returns 717 ------- 718 padarr : ndarray 719 Output array, with `pad_amt` values appended along `axis`. The 720 appended region is the minimum of the final `num` values along `axis`. 721 722 """ 723 if pad_amt == 0: 724 return arr 725 726 # Equivalent to edge padding for single value, so do that instead 727 if num == 1: 728 return _append_edge(arr, pad_amt, axis) 729 730 # Use entire array if `num` is too large 731 if num is not None: 732 if num >= arr.shape[axis]: 733 num = None 734 735 # Slice a chunk from the edge to calculate stats on 736 end = arr.shape[axis] - 1 737 if num is not None: 738 min_slice = tuple( 739 slice(None) if i != axis else slice(end, end - num, -1) 740 for (i, x) in enumerate(arr.shape)) 741 else: 742 min_slice = tuple(slice(None) for x in arr.shape) 743 744 # Shape to restore singleton dimension after slicing 745 pad_singleton = tuple(x if i != axis else 1 746 for (i, x) in enumerate(arr.shape)) 747 748 # Extract slice, calculate min, reshape to add singleton dimension back 749 min_chunk = arr[min_slice].min(axis=axis).reshape(pad_singleton) 750 751 # Concatenate `arr` with `min_chunk`, extended along `axis` by `pad_amt` 752 return np.concatenate((arr, min_chunk.repeat(pad_amt, axis=axis)), 753 axis=axis) 754 755 756def _pad_ref(arr, pad_amt, method, axis=-1): 757 """ 758 Pad `axis` of `arr` by reflection. 759 760 Parameters 761 ---------- 762 arr : ndarray 763 Input array of arbitrary shape. 764 pad_amt : tuple of ints, length 2 765 Padding to (prepend, append) along `axis`. 766 method : str 767 Controls method of reflection; options are 'even' or 'odd'. 768 axis : int 769 Axis along which to pad `arr`. 770 771 Returns 772 ------- 773 padarr : ndarray 774 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` 775 values appended along `axis`. Both regions are padded with reflected 776 values from the original array. 777 778 Notes 779 ----- 780 This algorithm does not pad with repetition, i.e. the edges are not 781 repeated in the reflection. For that behavior, use `mode='symmetric'`. 782 783 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a 784 single function, lest the indexing tricks in non-integer multiples of the 785 original shape would violate repetition in the final iteration. 786 787 """ 788 # Implicit booleanness to test for zero (or None) in any scalar type 789 if pad_amt[0] == 0 and pad_amt[1] == 0: 790 return arr 791 792 ########################################################################## 793 # Prepended region 794 795 # Slice off a reverse indexed chunk from near edge to pad `arr` before 796 ref_slice = tuple(slice(None) if i != axis else slice(pad_amt[0], 0, -1) 797 for (i, x) in enumerate(arr.shape)) 798 799 ref_chunk1 = arr[ref_slice] 800 801 # Shape to restore singleton dimension after slicing 802 pad_singleton = tuple(x if i != axis else 1 803 for (i, x) in enumerate(arr.shape)) 804 if pad_amt[0] == 1: 805 ref_chunk1 = ref_chunk1.reshape(pad_singleton) 806 807 # Memory/computationally more expensive, only do this if `method='odd'` 808 if 'odd' in method and pad_amt[0] > 0: 809 edge_slice1 = tuple(slice(None) if i != axis else 0 810 for (i, x) in enumerate(arr.shape)) 811 edge_chunk = arr[edge_slice1].reshape(pad_singleton) 812 ref_chunk1 = 2 * edge_chunk - ref_chunk1 813 del edge_chunk 814 815 ########################################################################## 816 # Appended region 817 818 # Slice off a reverse indexed chunk from far edge to pad `arr` after 819 start = arr.shape[axis] - pad_amt[1] - 1 820 end = arr.shape[axis] - 1 821 ref_slice = tuple(slice(None) if i != axis else slice(start, end) 822 for (i, x) in enumerate(arr.shape)) 823 rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1) 824 for (i, x) in enumerate(arr.shape)) 825 ref_chunk2 = arr[ref_slice][rev_idx] 826 827 if pad_amt[1] == 1: 828 ref_chunk2 = ref_chunk2.reshape(pad_singleton) 829 830 if 'odd' in method: 831 edge_slice2 = tuple(slice(None) if i != axis else -1 832 for (i, x) in enumerate(arr.shape)) 833 edge_chunk = arr[edge_slice2].reshape(pad_singleton) 834 ref_chunk2 = 2 * edge_chunk - ref_chunk2 835 del edge_chunk 836 837 # Concatenate `arr` with both chunks, extending along `axis` 838 return np.concatenate((ref_chunk1, arr, ref_chunk2), axis=axis) 839 840 841def _pad_sym(arr, pad_amt, method, axis=-1): 842 """ 843 Pad `axis` of `arr` by symmetry. 844 845 Parameters 846 ---------- 847 arr : ndarray 848 Input array of arbitrary shape. 849 pad_amt : tuple of ints, length 2 850 Padding to (prepend, append) along `axis`. 851 method : str 852 Controls method of symmetry; options are 'even' or 'odd'. 853 axis : int 854 Axis along which to pad `arr`. 855 856 Returns 857 ------- 858 padarr : ndarray 859 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` 860 values appended along `axis`. Both regions are padded with symmetric 861 values from the original array. 862 863 Notes 864 ----- 865 This algorithm DOES pad with repetition, i.e. the edges are repeated. 866 For padding without repeated edges, use `mode='reflect'`. 867 868 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a 869 single function, lest the indexing tricks in non-integer multiples of the 870 original shape would violate repetition in the final iteration. 871 872 """ 873 # Implicit booleanness to test for zero (or None) in any scalar type 874 if pad_amt[0] == 0 and pad_amt[1] == 0: 875 return arr 876 877 ########################################################################## 878 # Prepended region 879 880 # Slice off a reverse indexed chunk from near edge to pad `arr` before 881 sym_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[0]) 882 for (i, x) in enumerate(arr.shape)) 883 rev_idx = tuple(slice(None) if i != axis else slice(None, None, -1) 884 for (i, x) in enumerate(arr.shape)) 885 sym_chunk1 = arr[sym_slice][rev_idx] 886 887 # Shape to restore singleton dimension after slicing 888 pad_singleton = tuple(x if i != axis else 1 889 for (i, x) in enumerate(arr.shape)) 890 if pad_amt[0] == 1: 891 sym_chunk1 = sym_chunk1.reshape(pad_singleton) 892 893 # Memory/computationally more expensive, only do this if `method='odd'` 894 if 'odd' in method and pad_amt[0] > 0: 895 edge_slice1 = tuple(slice(None) if i != axis else 0 896 for (i, x) in enumerate(arr.shape)) 897 edge_chunk = arr[edge_slice1].reshape(pad_singleton) 898 sym_chunk1 = 2 * edge_chunk - sym_chunk1 899 del edge_chunk 900 901 ########################################################################## 902 # Appended region 903 904 # Slice off a reverse indexed chunk from far edge to pad `arr` after 905 start = arr.shape[axis] - pad_amt[1] 906 end = arr.shape[axis] 907 sym_slice = tuple(slice(None) if i != axis else slice(start, end) 908 for (i, x) in enumerate(arr.shape)) 909 sym_chunk2 = arr[sym_slice][rev_idx] 910 911 if pad_amt[1] == 1: 912 sym_chunk2 = sym_chunk2.reshape(pad_singleton) 913 914 if 'odd' in method: 915 edge_slice2 = tuple(slice(None) if i != axis else -1 916 for (i, x) in enumerate(arr.shape)) 917 edge_chunk = arr[edge_slice2].reshape(pad_singleton) 918 sym_chunk2 = 2 * edge_chunk - sym_chunk2 919 del edge_chunk 920 921 # Concatenate `arr` with both chunks, extending along `axis` 922 return np.concatenate((sym_chunk1, arr, sym_chunk2), axis=axis) 923 924 925def _pad_wrap(arr, pad_amt, axis=-1): 926 """ 927 Pad `axis` of `arr` via wrapping. 928 929 Parameters 930 ---------- 931 arr : ndarray 932 Input array of arbitrary shape. 933 pad_amt : tuple of ints, length 2 934 Padding to (prepend, append) along `axis`. 935 axis : int 936 Axis along which to pad `arr`. 937 938 Returns 939 ------- 940 padarr : ndarray 941 Output array, with `pad_amt[0]` values prepended and `pad_amt[1]` 942 values appended along `axis`. Both regions are padded wrapped values 943 from the opposite end of `axis`. 944 945 Notes 946 ----- 947 This method of padding is also known as 'tile' or 'tiling'. 948 949 The modes 'reflect', 'symmetric', and 'wrap' must be padded with a 950 single function, lest the indexing tricks in non-integer multiples of the 951 original shape would violate repetition in the final iteration. 952 953 """ 954 # Implicit booleanness to test for zero (or None) in any scalar type 955 if pad_amt[0] == 0 and pad_amt[1] == 0: 956 return arr 957 958 ########################################################################## 959 # Prepended region 960 961 # Slice off a reverse indexed chunk from near edge to pad `arr` before 962 start = arr.shape[axis] - pad_amt[0] 963 end = arr.shape[axis] 964 wrap_slice = tuple(slice(None) if i != axis else slice(start, end) 965 for (i, x) in enumerate(arr.shape)) 966 wrap_chunk1 = arr[wrap_slice] 967 968 # Shape to restore singleton dimension after slicing 969 pad_singleton = tuple(x if i != axis else 1 970 for (i, x) in enumerate(arr.shape)) 971 if pad_amt[0] == 1: 972 wrap_chunk1 = wrap_chunk1.reshape(pad_singleton) 973 974 ########################################################################## 975 # Appended region 976 977 # Slice off a reverse indexed chunk from far edge to pad `arr` after 978 wrap_slice = tuple(slice(None) if i != axis else slice(0, pad_amt[1]) 979 for (i, x) in enumerate(arr.shape)) 980 wrap_chunk2 = arr[wrap_slice] 981 982 if pad_amt[1] == 1: 983 wrap_chunk2 = wrap_chunk2.reshape(pad_singleton) 984 985 # Concatenate `arr` with both chunks, extending along `axis` 986 return np.concatenate((wrap_chunk1, arr, wrap_chunk2), axis=axis) 987 988 989def _normalize_shape(ndarray, shape, cast_to_int=True): 990 """ 991 Private function which does some checks and normalizes the possibly 992 much simpler representations of 'pad_width', 'stat_length', 993 'constant_values', 'end_values'. 994 995 Parameters 996 ---------- 997 narray : ndarray 998 Input ndarray 999 shape : {sequence, array_like, float, int}, optional 1000 The width of padding (pad_width), the number of elements on the 1001 edge of the narray used for statistics (stat_length), the constant 1002 value(s) to use when filling padded regions (constant_values), or the 1003 endpoint target(s) for linear ramps (end_values). 1004 ((before_1, after_1), ... (before_N, after_N)) unique number of 1005 elements for each axis where `N` is rank of `narray`. 1006 ((before, after),) yields same before and after constants for each 1007 axis. 1008 (constant,) or val is a shortcut for before = after = constant for 1009 all axes. 1010 cast_to_int : bool, optional 1011 Controls if values in ``shape`` will be rounded and cast to int 1012 before being returned. 1013 1014 Returns 1015 ------- 1016 normalized_shape : tuple of tuples 1017 val => ((val, val), (val, val), ...) 1018 [[val1, val2], [val3, val4], ...] => ((val1, val2), (val3, val4), ...) 1019 ((val1, val2), (val3, val4), ...) => no change 1020 [[val1, val2], ] => ((val1, val2), (val1, val2), ...) 1021 ((val1, val2), ) => ((val1, val2), (val1, val2), ...) 1022 [[val , ], ] => ((val, val), (val, val), ...) 1023 ((val , ), ) => ((val, val), (val, val), ...) 1024 1025 """ 1026 ndims = ndarray.ndim 1027 1028 # Shortcut shape=None 1029 if shape is None: 1030 return ((None, None), ) * ndims 1031 1032 # Convert any input `info` to a NumPy array 1033 arr = np.asarray(shape) 1034 1035 # Switch based on what input looks like 1036 if arr.ndim <= 1: 1037 if arr.shape == () or arr.shape == (1,): # Single scalar input 1038 # Create new array of ones, multiply by the scalar 1039 arr = np.ones((ndims, 2), dtype=ndarray.dtype) * arr 1040 elif arr.shape == (2,): # Apply padding (before, after) each axis 1041 # Create new axis 0, repeat along it for every axis 1042 arr = arr[np.newaxis, :].repeat(ndims, axis=0) 1043 else: 1044 fmt = "Unable to create correctly shaped tuple from %s" 1045 raise ValueError(fmt % (shape,)) 1046 1047 elif arr.ndim == 2: 1048 if arr.shape[1] == 1 and arr.shape[0] == ndims: 1049 # Padded before and after by the same amount 1050 arr = arr.repeat(2, axis=1) 1051 elif arr.shape[0] == ndims: 1052 # Input correctly formatted, pass it on as `arr` 1053 arr = shape 1054 else: 1055 fmt = "Unable to create correctly shaped tuple from %s" 1056 raise ValueError(fmt % (shape,)) 1057 1058 else: 1059 fmt = "Unable to create correctly shaped tuple from %s" 1060 raise ValueError(fmt % (shape,)) 1061 1062 # Cast if necessary 1063 if cast_to_int is True: 1064 arr = np.round(arr).astype(int) 1065 1066 # Convert list of lists to tuple of tuples 1067 return tuple(tuple(axis) for axis in arr.tolist()) 1068 1069 1070def _validate_lengths(narray, number_elements): 1071 """ 1072 Private function which does some checks and reformats pad_width and 1073 stat_length using _normalize_shape. 1074 1075 Parameters 1076 ---------- 1077 narray : ndarray 1078 Input ndarray 1079 number_elements : {sequence, int}, optional 1080 The width of padding (pad_width) or the number of elements on the edge 1081 of the narray used for statistics (stat_length). 1082 ((before_1, after_1), ... (before_N, after_N)) unique number of 1083 elements for each axis. 1084 ((before, after),) yields same before and after constants for each 1085 axis. 1086 (constant,) or int is a shortcut for before = after = constant for all 1087 axes. 1088 1089 Returns 1090 ------- 1091 _validate_lengths : tuple of tuples 1092 int => ((int, int), (int, int), ...) 1093 [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...) 1094 ((int1, int2), (int3, int4), ...) => no change 1095 [[int1, int2], ] => ((int1, int2), (int1, int2), ...) 1096 ((int1, int2), ) => ((int1, int2), (int1, int2), ...) 1097 [[int , ], ] => ((int, int), (int, int), ...) 1098 ((int , ), ) => ((int, int), (int, int), ...) 1099 1100 """ 1101 normshp = _normalize_shape(narray, number_elements) 1102 for i in normshp: 1103 chk = [1 if x is None else x for x in i] 1104 chk = [1 if x >= 0 else -1 for x in chk] 1105 if (chk[0] < 0) or (chk[1] < 0): 1106 fmt = "%s cannot contain negative values." 1107 raise ValueError(fmt % (number_elements,)) 1108 return normshp 1109 1110 1111############################################################################### 1112# Public functions 1113 1114 1115def pad(array, pad_width, mode=None, **kwargs): 1116 """ 1117 Pads an array. 1118 1119 Parameters 1120 ---------- 1121 array : array_like of rank N 1122 Input array 1123 pad_width : {sequence, array_like, int} 1124 Number of values padded to the edges of each axis. 1125 ((before_1, after_1), ... (before_N, after_N)) unique pad widths 1126 for each axis. 1127 ((before, after),) yields same before and after pad for each axis. 1128 (pad,) or int is a shortcut for before = after = pad width for all 1129 axes. 1130 mode : str or function 1131 One of the following string values or a user supplied function. 1132 1133 'constant' 1134 Pads with a constant value. 1135 'edge' 1136 Pads with the edge values of array. 1137 'linear_ramp' 1138 Pads with the linear ramp between end_value and the 1139 array edge value. 1140 'maximum' 1141 Pads with the maximum value of all or part of the 1142 vector along each axis. 1143 'mean' 1144 Pads with the mean value of all or part of the 1145 vector along each axis. 1146 'median' 1147 Pads with the median value of all or part of the 1148 vector along each axis. 1149 'minimum' 1150 Pads with the minimum value of all or part of the 1151 vector along each axis. 1152 'reflect' 1153 Pads with the reflection of the vector mirrored on 1154 the first and last values of the vector along each 1155 axis. 1156 'symmetric' 1157 Pads with the reflection of the vector mirrored 1158 along the edge of the array. 1159 'wrap' 1160 Pads with the wrap of the vector along the axis. 1161 The first values are used to pad the end and the 1162 end values are used to pad the beginning. 1163 <function> 1164 Padding function, see Notes. 1165 stat_length : sequence or int, optional 1166 Used in 'maximum', 'mean', 'median', and 'minimum'. Number of 1167 values at edge of each axis used to calculate the statistic value. 1168 1169 ((before_1, after_1), ... (before_N, after_N)) unique statistic 1170 lengths for each axis. 1171 1172 ((before, after),) yields same before and after statistic lengths 1173 for each axis. 1174 1175 (stat_length,) or int is a shortcut for before = after = statistic 1176 length for all axes. 1177 1178 Default is ``None``, to use the entire axis. 1179 constant_values : sequence or int, optional 1180 Used in 'constant'. The values to set the padded values for each 1181 axis. 1182 1183 ((before_1, after_1), ... (before_N, after_N)) unique pad constants 1184 for each axis. 1185 1186 ((before, after),) yields same before and after constants for each 1187 axis. 1188 1189 (constant,) or int is a shortcut for before = after = constant for 1190 all axes. 1191 1192 Default is 0. 1193 end_values : sequence or int, optional 1194 Used in 'linear_ramp'. The values used for the ending value of the 1195 linear_ramp and that will form the edge of the padded array. 1196 1197 ((before_1, after_1), ... (before_N, after_N)) unique end values 1198 for each axis. 1199 1200 ((before, after),) yields same before and after end values for each 1201 axis. 1202 1203 (constant,) or int is a shortcut for before = after = end value for 1204 all axes. 1205 1206 Default is 0. 1207 reflect_type : {'even', 'odd'}, optional 1208 Used in 'reflect', and 'symmetric'. The 'even' style is the 1209 default with an unaltered reflection around the edge value. For 1210 the 'odd' style, the extented part of the array is created by 1211 subtracting the reflected values from two times the edge value. 1212 1213 Returns 1214 ------- 1215 pad : ndarray 1216 Padded array of rank equal to `array` with shape increased 1217 according to `pad_width`. 1218 1219 Notes 1220 ----- 1221 This function exists in NumPy >= 1.7.0, but is included in 1222 ``scikit-fuzzy`` for backwards compatibility with earlier versions. 1223 1224 For an array with rank greater than 1, some of the padding of later 1225 axes is calculated from padding of previous axes. This is easiest to 1226 think about with a rank 2 array where the corners of the padded array 1227 are calculated by using padded values from the first axis. 1228 1229 The padding function, if used, should return a rank 1 array equal in 1230 length to the vector argument with padded values replaced. It has the 1231 following signature:: 1232 1233 padding_func(vector, iaxis_pad_width, iaxis, **kwargs) 1234 1235 where 1236 1237 vector : ndarray 1238 A rank 1 array already padded with zeros. Padded values are 1239 vector[:pad_tuple[0]] and vector[-pad_tuple[1]:]. 1240 iaxis_pad_width : tuple 1241 A 2-tuple of ints, iaxis_pad_width[0] represents the number of 1242 values padded at the beginning of vector where 1243 iaxis_pad_width[1] represents the number of values padded at 1244 the end of vector. 1245 iaxis : int 1246 The axis currently being calculated. 1247 kwargs : misc 1248 Any keyword arguments the function requires. 1249 1250 Examples 1251 -------- 1252 >>> import skfuzzy as fuzz 1253 >>> a = [1, 2, 3, 4, 5] 1254 >>> fuzz.pad(a, (2,3), 'constant', constant_values=(4, 6)) 1255 array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6]) 1256 1257 >>> fuzz.pad(a, (2, 3), 'edge') 1258 array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5]) 1259 1260 >>> fuzz.pad(a, (2, 3), 'linear_ramp', end_values=(5, -4)) 1261 array([ 5, 3, 1, 2, 3, 4, 5, 2, -1, -4]) 1262 1263 >>> fuzz.pad(a, (2,), 'maximum') 1264 array([5, 5, 1, 2, 3, 4, 5, 5, 5]) 1265 1266 >>> fuzz.pad(a, (2,), 'mean') 1267 array([3, 3, 1, 2, 3, 4, 5, 3, 3]) 1268 1269 >>> fuzz.pad(a, (2,), 'median') 1270 array([3, 3, 1, 2, 3, 4, 5, 3, 3]) 1271 1272 >>> a = [[1, 2], [3, 4]] 1273 >>> fuzz.pad(a, ((3, 2), (2, 3)), 'minimum') 1274 array([[1, 1, 1, 2, 1, 1, 1], 1275 [1, 1, 1, 2, 1, 1, 1], 1276 [1, 1, 1, 2, 1, 1, 1], 1277 [1, 1, 1, 2, 1, 1, 1], 1278 [3, 3, 3, 4, 3, 3, 3], 1279 [1, 1, 1, 2, 1, 1, 1], 1280 [1, 1, 1, 2, 1, 1, 1]]) 1281 1282 >>> a = [1, 2, 3, 4, 5] 1283 >>> fuzz.pad(a, (2, 3), 'reflect') 1284 array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]) 1285 1286 >>> fuzz.pad(a, (2, 3), 'reflect', reflect_type='odd') 1287 array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]) 1288 1289 >>> fuzz.pad(a, (2, 3), 'symmetric') 1290 array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]) 1291 1292 >>> fuzz.pad(a, (2, 3), 'symmetric', reflect_type='odd') 1293 array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]) 1294 1295 >>> fuzz.pad(a, (2, 3), 'wrap') 1296 array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]) 1297 1298 >>> def padwithtens(vector, pad_width, iaxis, kwargs): 1299 ... vector[:pad_width[0]] = 10 1300 ... vector[-pad_width[1]:] = 10 1301 ... return vector 1302 1303 >>> a = np.arange(6) 1304 >>> a = a.reshape((2, 3)) 1305 1306 >>> fuzz.pad(a, 2, padwithtens) 1307 array([[10, 10, 10, 10, 10, 10, 10], 1308 [10, 10, 10, 10, 10, 10, 10], 1309 [10, 10, 0, 1, 2, 10, 10], 1310 [10, 10, 3, 4, 5, 10, 10], 1311 [10, 10, 10, 10, 10, 10, 10], 1312 [10, 10, 10, 10, 10, 10, 10]]) 1313 """ 1314 if not np.asarray(pad_width).dtype.kind == 'i': 1315 raise TypeError('`pad_width` must be of integral type.') 1316 1317 narray = np.array(array) 1318 pad_width = _validate_lengths(narray, pad_width) 1319 1320 allowedkwargs = { 1321 'constant': ['constant_values'], 1322 'edge': [], 1323 'linear_ramp': ['end_values'], 1324 'maximum': ['stat_length'], 1325 'mean': ['stat_length'], 1326 'median': ['stat_length'], 1327 'minimum': ['stat_length'], 1328 'reflect': ['reflect_type'], 1329 'symmetric': ['reflect_type'], 1330 'wrap': [], 1331 } 1332 1333 kwdefaults = { 1334 'stat_length': None, 1335 'constant_values': 0, 1336 'end_values': 0, 1337 'reflect_type': 'even', 1338 } 1339 1340 if isinstance(mode, str): 1341 # Make sure have allowed kwargs appropriate for mode 1342 for key in kwargs: 1343 if key not in allowedkwargs[mode]: 1344 raise ValueError('%s keyword not in allowed keywords %s' % 1345 (key, allowedkwargs[mode])) 1346 1347 # Set kwarg defaults 1348 for kw in allowedkwargs[mode]: 1349 kwargs.setdefault(kw, kwdefaults[kw]) 1350 1351 # Need to only normalize particular keywords. 1352 for i in kwargs: 1353 if i == 'stat_length': 1354 kwargs[i] = _validate_lengths(narray, kwargs[i]) 1355 if i in ['end_values', 'constant_values']: 1356 kwargs[i] = _normalize_shape(narray, kwargs[i], 1357 cast_to_int=False) 1358 elif mode is None: 1359 raise ValueError('Keyword "mode" must be a function or one of %s.' % 1360 (list(allowedkwargs.keys()),)) 1361 else: 1362 # Drop back to old, slower np.apply_along_axis mode for user-supplied 1363 # vector function 1364 function = mode 1365 1366 # Create a new padded array 1367 rank = list(range(len(narray.shape))) 1368 total_dim_increase = [np.sum(pad_width[i]) for i in rank] 1369 offset_slices = [slice(pad_width[i][0], 1370 pad_width[i][0] + narray.shape[i]) 1371 for i in rank] 1372 new_shape = np.array(narray.shape) + total_dim_increase 1373 newmat = np.zeros(new_shape, narray.dtype) 1374 1375 # Insert the original array into the padded array 1376 newmat[offset_slices] = narray 1377 1378 # This is the core of pad ... 1379 for iaxis in rank: 1380 np.apply_along_axis(function, 1381 iaxis, 1382 newmat, 1383 pad_width[iaxis], 1384 iaxis, 1385 kwargs) 1386 return newmat 1387 1388 # If we get here, use new padding method 1389 newmat = narray.copy() 1390 1391 # API preserved, but completely new algorithm which pads by building the 1392 # entire block to pad before/after `arr` with in one step, for each axis. 1393 if mode == 'constant': 1394 for axis, ((pad_before, pad_after), (before_val, after_val)) \ 1395 in enumerate(zip(pad_width, kwargs['constant_values'])): 1396 newmat = _prepend_const(newmat, pad_before, before_val, axis) 1397 newmat = _append_const(newmat, pad_after, after_val, axis) 1398 1399 elif mode == 'edge': 1400 for axis, (pad_before, pad_after) in enumerate(pad_width): 1401 newmat = _prepend_edge(newmat, pad_before, axis) 1402 newmat = _append_edge(newmat, pad_after, axis) 1403 1404 elif mode == 'linear_ramp': 1405 for axis, ((pad_before, pad_after), (before_val, after_val)) \ 1406 in enumerate(zip(pad_width, kwargs['end_values'])): 1407 newmat = _prepend_ramp(newmat, pad_before, before_val, axis) 1408 newmat = _append_ramp(newmat, pad_after, after_val, axis) 1409 1410 elif mode == 'maximum': 1411 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ 1412 in enumerate(zip(pad_width, kwargs['stat_length'])): 1413 newmat = _prepend_max(newmat, pad_before, chunk_before, axis) 1414 newmat = _append_max(newmat, pad_after, chunk_after, axis) 1415 1416 elif mode == 'mean': 1417 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ 1418 in enumerate(zip(pad_width, kwargs['stat_length'])): 1419 newmat = _prepend_mean(newmat, pad_before, chunk_before, axis) 1420 newmat = _append_mean(newmat, pad_after, chunk_after, axis) 1421 1422 elif mode == 'median': 1423 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ 1424 in enumerate(zip(pad_width, kwargs['stat_length'])): 1425 newmat = _prepend_med(newmat, pad_before, chunk_before, axis) 1426 newmat = _append_med(newmat, pad_after, chunk_after, axis) 1427 1428 elif mode == 'minimum': 1429 for axis, ((pad_before, pad_after), (chunk_before, chunk_after)) \ 1430 in enumerate(zip(pad_width, kwargs['stat_length'])): 1431 newmat = _prepend_min(newmat, pad_before, chunk_before, axis) 1432 newmat = _append_min(newmat, pad_after, chunk_after, axis) 1433 1434 elif mode == 'reflect': 1435 for axis, (pad_before, pad_after) in enumerate(pad_width): 1436 # Recursive padding along any axis where `pad_amt` is too large 1437 # for indexing tricks. We can only safely pad the original axis 1438 # length, to keep the period of the reflections consistent. 1439 if ((pad_before > 0) or 1440 (pad_after > 0)) and newmat.shape[axis] == 1: 1441 # Extending singleton dimension for 'reflect' is legacy 1442 # behavior; it really should raise an error. 1443 newmat = _prepend_edge(newmat, pad_before, axis) 1444 newmat = _append_edge(newmat, pad_after, axis) 1445 continue 1446 1447 method = kwargs['reflect_type'] 1448 safe_pad = newmat.shape[axis] - 1 1449 while ((pad_before > safe_pad) or (pad_after > safe_pad)): 1450 pad_iter_b = min(safe_pad, 1451 safe_pad * (pad_before // safe_pad)) 1452 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) 1453 newmat = _pad_ref(newmat, (pad_iter_b, 1454 pad_iter_a), method, axis) 1455 pad_before -= pad_iter_b 1456 pad_after -= pad_iter_a 1457 safe_pad += pad_iter_b + pad_iter_a 1458 newmat = _pad_ref(newmat, (pad_before, pad_after), method, axis) 1459 1460 elif mode == 'symmetric': 1461 for axis, (pad_before, pad_after) in enumerate(pad_width): 1462 # Recursive padding along any axis where `pad_amt` is too large 1463 # for indexing tricks. We can only safely pad the original axis 1464 # length, to keep the period of the reflections consistent. 1465 method = kwargs['reflect_type'] 1466 safe_pad = newmat.shape[axis] 1467 while ((pad_before > safe_pad) or 1468 (pad_after > safe_pad)): 1469 pad_iter_b = min(safe_pad, 1470 safe_pad * (pad_before // safe_pad)) 1471 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) 1472 newmat = _pad_sym(newmat, (pad_iter_b, 1473 pad_iter_a), method, axis) 1474 pad_before -= pad_iter_b 1475 pad_after -= pad_iter_a 1476 safe_pad += pad_iter_b + pad_iter_a 1477 newmat = _pad_sym(newmat, (pad_before, pad_after), method, axis) 1478 1479 elif mode == 'wrap': 1480 for axis, (pad_before, pad_after) in enumerate(pad_width): 1481 # Recursive padding along any axis where `pad_amt` is too large 1482 # for indexing tricks. We can only safely pad the original axis 1483 # length, to keep the period of the reflections consistent. 1484 safe_pad = newmat.shape[axis] 1485 while ((pad_before > safe_pad) or 1486 (pad_after > safe_pad)): 1487 pad_iter_b = min(safe_pad, 1488 safe_pad * (pad_before // safe_pad)) 1489 pad_iter_a = min(safe_pad, safe_pad * (pad_after // safe_pad)) 1490 newmat = _pad_wrap(newmat, (pad_iter_b, pad_iter_a), axis) 1491 1492 pad_before -= pad_iter_b 1493 pad_after -= pad_iter_a 1494 safe_pad += pad_iter_b + pad_iter_a 1495 newmat = _pad_wrap(newmat, (pad_before, pad_after), axis) 1496 1497 return newmat 1498