Skip to content

Commit 73cdfc4

Browse files
authored
REGR: Fix IntegerArray unary ops regression (#36303)
1 parent 7a7b053 commit 73cdfc4

File tree

7 files changed

+107
-2
lines changed

7 files changed

+107
-2
lines changed

doc/source/whatsnew/v1.1.3.rst

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ including other versions of pandas.
1414

1515
Fixed regressions
1616
~~~~~~~~~~~~~~~~~
17+
- Fixed regression in :class:`IntegerArray` unary plus and minus operations raising a ``TypeError`` (:issue:`36063`)
1718
- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a tuple (:issue:`35534`)
1819
- Fixed regression in :meth:`Series.__getitem__` incorrectly raising when the input was a frozenset (:issue:`35747`)
1920
-

pandas/conftest.py

+13
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,19 @@ def any_nullable_int_dtype(request):
10551055
return request.param
10561056

10571057

1058+
@pytest.fixture(params=tm.SIGNED_EA_INT_DTYPES)
1059+
def any_signed_nullable_int_dtype(request):
1060+
"""
1061+
Parameterized fixture for any signed nullable integer dtype.
1062+
1063+
* 'Int8'
1064+
* 'Int16'
1065+
* 'Int32'
1066+
* 'Int64'
1067+
"""
1068+
return request.param
1069+
1070+
10581071
@pytest.fixture(params=tm.ALL_REAL_DTYPES)
10591072
def any_real_dtype(request):
10601073
"""

pandas/core/arrays/integer.py

+9
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,15 @@ def __init__(self, values: np.ndarray, mask: np.ndarray, copy: bool = False):
364364
)
365365
super().__init__(values, mask, copy=copy)
366366

367+
def __neg__(self):
368+
return type(self)(-self._data, self._mask)
369+
370+
def __pos__(self):
371+
return self
372+
373+
def __abs__(self):
374+
return type(self)(np.abs(self._data), self._mask)
375+
367376
@classmethod
368377
def _from_sequence(cls, scalars, dtype=None, copy: bool = False) -> "IntegerArray":
369378
return integer_array(scalars, dtype=dtype, copy=copy)

pandas/core/generic.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,10 @@ def __pos__(self):
14171417
):
14181418
arr = operator.pos(values)
14191419
else:
1420-
raise TypeError(f"Unary plus expects numeric dtype, not {values.dtype}")
1420+
raise TypeError(
1421+
"Unary plus expects bool, numeric, timedelta, "
1422+
f"or object dtype, not {values.dtype}"
1423+
)
14211424
return self.__array_wrap__(arr)
14221425

14231426
def __invert__(self):

pandas/tests/arrays/integer/test_arithmetic.py

+38
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,41 @@ def test_reduce_to_float(op):
261261
index=pd.Index(["a", "b"], name="A"),
262262
)
263263
tm.assert_frame_equal(result, expected)
264+
265+
266+
@pytest.mark.parametrize(
267+
"source, target",
268+
[
269+
([1, 2, 3], [-1, -2, -3]),
270+
([1, 2, None], [-1, -2, None]),
271+
([-1, 0, 1], [1, 0, -1]),
272+
],
273+
)
274+
def test_unary_minus_nullable_int(any_signed_nullable_int_dtype, source, target):
275+
dtype = any_signed_nullable_int_dtype
276+
arr = pd.array(source, dtype=dtype)
277+
result = -arr
278+
expected = pd.array(target, dtype=dtype)
279+
tm.assert_extension_array_equal(result, expected)
280+
281+
282+
@pytest.mark.parametrize(
283+
"source", [[1, 2, 3], [1, 2, None], [-1, 0, 1]],
284+
)
285+
def test_unary_plus_nullable_int(any_signed_nullable_int_dtype, source):
286+
dtype = any_signed_nullable_int_dtype
287+
expected = pd.array(source, dtype=dtype)
288+
result = +expected
289+
tm.assert_extension_array_equal(result, expected)
290+
291+
292+
@pytest.mark.parametrize(
293+
"source, target",
294+
[([1, 2, 3], [1, 2, 3]), ([1, -2, None], [1, 2, None]), ([-1, 0, 1], [1, 0, 1])],
295+
)
296+
def test_abs_nullable_int(any_signed_nullable_int_dtype, source, target):
297+
dtype = any_signed_nullable_int_dtype
298+
s = pd.array(source, dtype=dtype)
299+
result = abs(s)
300+
expected = pd.array(target, dtype=dtype)
301+
tm.assert_extension_array_equal(result, expected)

pandas/tests/frame/test_operators.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def test_pos_object(self, df):
119119
"df", [pd.DataFrame({"a": pd.to_datetime(["2017-01-22", "1970-01-01"])})]
120120
)
121121
def test_pos_raises(self, df):
122-
msg = re.escape("Unary plus expects numeric dtype, not datetime64[ns]")
122+
msg = "Unary plus expects .* dtype, not datetime64\\[ns\\]"
123123
with pytest.raises(TypeError, match=msg):
124124
(+df)
125125
with pytest.raises(TypeError, match=msg):

pandas/tests/series/test_operators.py

+41
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,44 @@ def test_invert(self):
536536
ser = tm.makeStringSeries()
537537
ser.name = "series"
538538
tm.assert_series_equal(-(ser < 0), ~(ser < 0))
539+
540+
@pytest.mark.parametrize(
541+
"source, target",
542+
[
543+
([1, 2, 3], [-1, -2, -3]),
544+
([1, 2, None], [-1, -2, None]),
545+
([-1, 0, 1], [1, 0, -1]),
546+
],
547+
)
548+
def test_unary_minus_nullable_int(
549+
self, any_signed_nullable_int_dtype, source, target
550+
):
551+
dtype = any_signed_nullable_int_dtype
552+
s = pd.Series(source, dtype=dtype)
553+
result = -s
554+
expected = pd.Series(target, dtype=dtype)
555+
tm.assert_series_equal(result, expected)
556+
557+
@pytest.mark.parametrize(
558+
"source", [[1, 2, 3], [1, 2, None], [-1, 0, 1]],
559+
)
560+
def test_unary_plus_nullable_int(self, any_signed_nullable_int_dtype, source):
561+
dtype = any_signed_nullable_int_dtype
562+
expected = pd.Series(source, dtype=dtype)
563+
result = +expected
564+
tm.assert_series_equal(result, expected)
565+
566+
@pytest.mark.parametrize(
567+
"source, target",
568+
[
569+
([1, 2, 3], [1, 2, 3]),
570+
([1, -2, None], [1, 2, None]),
571+
([-1, 0, 1], [1, 0, 1]),
572+
],
573+
)
574+
def test_abs_nullable_int(self, any_signed_nullable_int_dtype, source, target):
575+
dtype = any_signed_nullable_int_dtype
576+
s = pd.Series(source, dtype=dtype)
577+
result = abs(s)
578+
expected = pd.Series(target, dtype=dtype)
579+
tm.assert_series_equal(result, expected)

0 commit comments

Comments
 (0)