Skip to content

Commit cb021eb

Browse files
committed
ENH: Extend numpy.pad to handle pad_width dictionary argument.
1 parent b89320a commit cb021eb

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Extend ``numpy.pad`` to accept a dictionary for the ``pad_width`` argument.

numpy/lib/_arraypad_impl.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
of an n-dimensional array.
44
55
"""
6+
import typing
7+
68
import numpy as np
79
from numpy._core.overrides import array_function_dispatch
810
from numpy.lib._index_tricks_impl import ndindex
@@ -550,14 +552,17 @@ def pad(array, pad_width, mode='constant', **kwargs):
550552
----------
551553
array : array_like of rank N
552554
The array to pad.
553-
pad_width : {sequence, array_like, int}
555+
pad_width : {sequence, array_like, int, dict}
554556
Number of values padded to the edges of each axis.
555557
``((before_1, after_1), ... (before_N, after_N))`` unique pad widths
556558
for each axis.
557559
``(before, after)`` or ``((before, after),)`` yields same before
558560
and after pad for each axis.
559561
``(pad,)`` or ``int`` is a shortcut for before = after = pad width
560562
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.
561566
mode : str or function, optional
562567
One of the following string values or a user supplied function.
563568
@@ -745,8 +750,39 @@ def pad(array, pad_width, mode='constant', **kwargs):
745750
[100, 100, 3, 4, 5, 100, 100],
746751
[100, 100, 100, 100, 100, 100, 100],
747752
[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]])
748773
"""
749774
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
750786
pad_width = np.asarray(pad_width)
751787

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

numpy/lib/_arraypad_impl.pyi

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,17 @@ _ModeKind: TypeAlias = L[
4343
# TODO: In practice each keyword argument is exclusive to one or more
4444
# specific modes. Consider adding more overloads to express this in the future.
4545

46+
_PadWidth: TypeAlias = (
47+
_ArrayLikeInt
48+
| dict[int, int]
49+
| dict[int, tuple[int, int]]
50+
| dict[int, int | tuple[int, int]]
51+
)
4652
# Expand `**kwargs` into explicit keyword-only arguments
4753
@overload
4854
def pad(
4955
array: _ArrayLike[_ScalarT],
50-
pad_width: _ArrayLikeInt,
56+
pad_width: _PadWidth,
5157
mode: _ModeKind = ...,
5258
*,
5359
stat_length: _ArrayLikeInt | None = ...,
@@ -58,7 +64,7 @@ def pad(
5864
@overload
5965
def pad(
6066
array: ArrayLike,
61-
pad_width: _ArrayLikeInt,
67+
pad_width: _PadWidth,
6268
mode: _ModeKind = ...,
6369
*,
6470
stat_length: _ArrayLikeInt | None = ...,
@@ -69,14 +75,14 @@ def pad(
6975
@overload
7076
def pad(
7177
array: _ArrayLike[_ScalarT],
72-
pad_width: _ArrayLikeInt,
78+
pad_width: _PadWidth,
7379
mode: _ModeFunc,
7480
**kwargs: Any,
7581
) -> NDArray[_ScalarT]: ...
7682
@overload
7783
def pad(
7884
array: ArrayLike,
79-
pad_width: _ArrayLikeInt,
85+
pad_width: _PadWidth,
8086
mode: _ModeFunc,
8187
**kwargs: Any,
8288
) -> NDArray[Any]: ...

numpy/lib/tests/test_arraypad.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,3 +1413,15 @@ def test_dtype_persistence(dtype, mode):
14131413
arr = np.zeros((3, 2, 1), dtype=dtype)
14141414
result = np.pad(arr, 1, mode=mode)
14151415
assert result.dtype == dtype
1416+
1417+
1418+
@pytest.mark.parametrize("input_shape, pad_width, expected_shape", [
1419+
((3, 4, 5), {-2: (1, 3)}, (3, 4 + 1 + 3, 5)),
1420+
((3, 4, 5), {0: (5, 2)}, (3 + 5 + 2, 4, 5)),
1421+
((3, 4, 5), {0: (5, 2), -1: (3, 4)}, (3 + 5 + 2, 4, 5 + 3 + 4)),
1422+
((3, 4, 5), {1: 5}, (3, 4 + 2 * 5, 5)),
1423+
])
1424+
def test_pad_dict_pad_width(input_shape, pad_width, expected_shape):
1425+
a = np.zeros(input_shape)
1426+
result = np.pad(a, pad_width)
1427+
assert result.shape == expected_shape

numpy/typing/tests/data/reveal/arraypad.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ assert_type(np.pad(AR_LIKE, (2, 3), "constant"), npt.NDArray[Any])
2020

2121
assert_type(np.pad(AR_f8, (2, 3), mode_func), npt.NDArray[np.float64])
2222
assert_type(np.pad(AR_f8, (2, 3), mode_func, a=1, b=2), npt.NDArray[np.float64])
23+
24+
assert_type(np.pad(AR_i8, {-1: (2, 3)}, "constant"), npt.NDArray[np.int64])
25+
assert_type(np.pad(AR_i8, {-2: 4}, "constant"), npt.NDArray[np.int64])
26+
assert_type(np.pad(AR_f8, {-1: (2, 3), -2: 4}, "constant"), npt.NDArray[np.float64])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy