Skip to content

Commit

Permalink
ENH: Implement Index.__invert__ (pandas-dev#45006)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored Dec 23, 2021
1 parent e50d077 commit 718cec9
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 9 deletions.
1 change: 0 additions & 1 deletion pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1401,7 +1401,6 @@ class Timedelta(_Timedelta):
# Arithmetic Methods
# TODO: Can some of these be defined in the cython class?

__inv__ = _op_unary_method(lambda x: -x, '__inv__')
__neg__ = _op_unary_method(lambda x: -x, '__neg__')
__pos__ = _op_unary_method(lambda x: x, '__pos__')
__abs__ = _op_unary_method(lambda x: abs(x), '__abs__')
Expand Down
7 changes: 3 additions & 4 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6638,10 +6638,9 @@ def __neg__(self):
def __pos__(self):
return self._unary_method(operator.pos)

def __inv__(self):
# TODO: why not operator.inv?
# TODO: __inv__ vs __invert__?
return self._unary_method(lambda x: -x)
def __invert__(self):
# GH#8875
return self._unary_method(operator.inv)

# --------------------------------------------------------------------
# Reductions
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3804,7 +3804,7 @@ def drop_duplicates(self, keep: str | bool = "first") -> MultiIndex:
__neg__ = make_invalid_op("__neg__")
__pos__ = make_invalid_op("__pos__")
__abs__ = make_invalid_op("__abs__")
__inv__ = make_invalid_op("__inv__")
__invert__ = make_invalid_op("__invert__")


def _lexsort_depth(codes: list[np.ndarray], nlevels: int) -> int:
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/frame/test_unary.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class TestDataFrameUnaryOperators:
# __pos__, __neg__, __inv__
# __pos__, __neg__, __invert__

@pytest.mark.parametrize(
"df,expected",
Expand Down
24 changes: 24 additions & 0 deletions pandas/tests/indexes/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,30 @@ def test_append_preserves_dtype(self, simple_index):
alt = index.take(list(range(N)) * 2)
tm.assert_index_equal(result, alt, check_exact=True)

def test_inv(self, simple_index):
idx = simple_index

if idx.dtype.kind in ["i", "u"]:
res = ~idx
expected = Index(~idx.values, name=idx.name)
tm.assert_index_equal(res, expected)

# check that we are matching Series behavior
res2 = ~Series(idx)
# TODO(2.0): once we preserve dtype, check_dtype can be True
tm.assert_series_equal(res2, Series(expected), check_dtype=False)
else:
if idx.dtype.kind == "f":
msg = "ufunc 'invert' not supported for the input types"
else:
msg = "bad operand"
with pytest.raises(TypeError, match=msg):
~idx

# check that we get the same behavior with Series
with pytest.raises(TypeError, match=msg):
~Series(idx)


class NumericBase(Base):
"""
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/multi/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_numeric_compat(idx):
1 // idx


@pytest.mark.parametrize("method", ["all", "any"])
@pytest.mark.parametrize("method", ["all", "any", "__invert__"])
def test_logical_compat(idx, method):
msg = f"cannot perform {method}"

Expand Down
15 changes: 15 additions & 0 deletions pandas/tests/scalar/timedelta/test_timedelta.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@


class TestTimedeltaUnaryOps:
def test_invert(self):
td = Timedelta(10, unit="d")

msg = "bad operand type for unary ~"
with pytest.raises(TypeError, match=msg):
~td

# check this matches pytimedelta and timedelta64
with pytest.raises(TypeError, match=msg):
~(td.to_pytimedelta())

umsg = "ufunc 'invert' not supported for the input types"
with pytest.raises(TypeError, match=umsg):
~(td.to_timedelta64())

def test_unary_ops(self):
td = Timedelta(10, unit="d")

Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/series/test_unary.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class TestSeriesUnaryOps:
# __neg__, __pos__, __inv__
# __neg__, __pos__, __invert__

def test_neg(self):
ser = tm.makeStringSeries()
Expand Down

0 comments on commit 718cec9

Please sign in to comment.