From 640fe505e4df44b5a92cf350a800f302fb3a9ced Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 14 Oct 2020 11:21:25 -0700 Subject: [PATCH 1/4] CLN: Index comparisons --- pandas/core/indexes/base.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 87dd15d5b142b..e5c66d3e2aba5 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -65,7 +65,6 @@ ) from pandas.core.dtypes.concat import concat_compat from pandas.core.dtypes.generic import ( - ABCCategorical, ABCDatetimeIndex, ABCMultiIndex, ABCPandasArray, @@ -83,6 +82,7 @@ from pandas.core.arrays.datetimes import tz_to_dtype, validate_tz_from_dtype from pandas.core.base import IndexOpsMixin, PandasObject import pandas.core.common as com +from pandas.core.construction import extract_array from pandas.core.indexers import deprecate_ndim_indexing from pandas.core.indexes.frozen import FrozenList from pandas.core.ops import get_op_result_name @@ -5371,11 +5371,13 @@ def _cmp_method(self, other, op): if len(self) != len(other): raise ValueError("Lengths must match to compare") - if is_object_dtype(self.dtype) and isinstance(other, ABCCategorical): - left = type(other)(self._values, dtype=other.dtype) - return op(left, other) - elif is_object_dtype(self.dtype) and isinstance(other, ExtensionArray): - # e.g. PeriodArray + if not isinstance(other, ABCMultiIndex): + other = extract_array(other, extract_numpy=True) + else: + other = np.asarray(other) + + if is_object_dtype(self.dtype) and isinstance(other, ExtensionArray): + # e.g. PeriodArray, Categorical with np.errstate(all="ignore"): result = op(self._values, other) @@ -5390,7 +5392,7 @@ def _cmp_method(self, other, op): else: with np.errstate(all="ignore"): - result = ops.comparison_op(self._values, np.asarray(other), op) + result = ops.comparison_op(self._values, other, op) return result From 0f558aaca2189ccf8dfc3a086267a72dc4203002 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 16 Oct 2020 13:51:57 -0700 Subject: [PATCH 2/4] TST: MultiIndex comparison with tuple #21517 --- .../tests/indexes/multi/test_equivalence.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pandas/tests/indexes/multi/test_equivalence.py b/pandas/tests/indexes/multi/test_equivalence.py index 184cedea7dc5c..5b0af149c0cad 100644 --- a/pandas/tests/indexes/multi/test_equivalence.py +++ b/pandas/tests/indexes/multi/test_equivalence.py @@ -84,6 +84,32 @@ def test_equals_op(idx): tm.assert_series_equal(series_a == item, Series(expected3)) +def test_compare_tuple(): + # GH#21517 + mi = MultiIndex.from_product([[1, 2]] * 2) + + all_false = np.array([False, False, False, False]) + + result = mi == mi[0] + expected = np.array([True, False, False, False]) + tm.assert_numpy_array_equal(result, expected) + + result = mi != mi[0] + tm.assert_numpy_array_equal(result, ~expected) + + result = mi < mi[0] + tm.assert_numpy_array_equal(result, all_false) + + result = mi <= mi[0] + tm.assert_numpy_array_equal(result, expected) + + result = mi > mi[0] + tm.assert_numpy_array_equal(result, ~expected) + + result = mi >= mi[0] + tm.assert_numpy_array_equal(result, ~all_false) + + def test_equals_multi(idx): assert idx.equals(idx) assert not idx.equals(idx.values) From 82a5e0b80cb2644db47cab361f5d4fe02682de92 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 16 Oct 2020 13:52:47 -0700 Subject: [PATCH 3/4] whatsnew --- doc/source/whatsnew/v1.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index c9a1dbd0ae90d..29265ca06e364 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -368,6 +368,7 @@ Numeric - Bug in :meth:`DataFrame.__rmatmul__` error handling reporting transposed shapes (:issue:`21581`) - Bug in :class:`Series` flex arithmetic methods where the result when operating with a ``list``, ``tuple`` or ``np.ndarray`` would have an incorrect name (:issue:`36760`) - Bug in :class:`IntegerArray` multiplication with ``timedelta`` and ``np.timedelta64`` objects (:issue:`36870`) +- Bug in :class:`MultiIndex` comparison with tuple incorrectly treating tuple as array-like (:issue:`21517`) - Bug in :meth:`DataFrame.diff` with ``datetime64`` dtypes including ``NaT`` values failing to fill ``NaT`` results correctly (:issue:`32441`) - Bug in :class:`DataFrame` arithmetic ops incorrectly accepting keyword arguments (:issue:`36843`) - Bug in :class:`IntervalArray` comparisons with :class:`Series` not returning :class:`Series` (:issue:`36908`) From bf2f19badcaf75221031bf1688810948b460cce1 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 17 Oct 2020 19:49:02 -0700 Subject: [PATCH 4/4] test for #34180 --- pandas/tests/indexes/multi/test_equivalence.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pandas/tests/indexes/multi/test_equivalence.py b/pandas/tests/indexes/multi/test_equivalence.py index 5b0af149c0cad..fec1c0e44cd9f 100644 --- a/pandas/tests/indexes/multi/test_equivalence.py +++ b/pandas/tests/indexes/multi/test_equivalence.py @@ -110,6 +110,20 @@ def test_compare_tuple(): tm.assert_numpy_array_equal(result, ~all_false) +def test_compare_tuple_strs(): + # GH#34180 + + mi = MultiIndex.from_tuples([("a", "b"), ("b", "c"), ("c", "a")]) + + result = mi == ("c", "a") + expected = np.array([False, False, True]) + tm.assert_numpy_array_equal(result, expected) + + result = mi == ("c",) + expected = np.array([False, False, False]) + tm.assert_numpy_array_equal(result, expected) + + def test_equals_multi(idx): assert idx.equals(idx) assert not idx.equals(idx.values)