From ddc1b8ffe69faf5694e39fb165cc1d8d1f774af3 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 08:46:07 +0200 Subject: [PATCH 01/10] Don't complain for empty index. --- pandas/core/indexes/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9b3f2d191831d..e852aad433059 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2925,7 +2925,7 @@ def union(self, other, sort=None): if not is_dtype_equal(self.dtype, other.dtype): if isinstance(self, ABCMultiIndex) and not is_object_dtype( unpack_nested_dtype(other) - ): + ) and len(other) > 0: raise NotImplementedError( "Can only union MultiIndex with MultiIndex or Index of tuples, " "try mi.to_flat_index().union(other) instead." From 73b190641647a787772cd86d0c6184b903cbc472 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 08:46:40 +0200 Subject: [PATCH 02/10] Simple test. --- pandas/tests/indexes/multi/test_setops.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index 4a170d9cd161f..49ebb627191e2 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -414,6 +414,14 @@ def test_union_empty_self_different_names(): tm.assert_index_equal(result, expected) +def test_union_multiindex_empty_rangeindex(): + # GH#41234 + mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) + ri = pd.RangeIndex(0) + result = mi.union(mi) + tm.assert_index_equal(mi, result) + + @pytest.mark.parametrize( "method", ["union", "intersection", "difference", "symmetric_difference"] ) From 0ed31981f0d9243da31cb4bee589732dc835ffde Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 09:01:08 +0200 Subject: [PATCH 03/10] Changelog. --- doc/source/whatsnew/v1.3.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 6bbaa934eaa12..dec7c2af35fa8 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -754,6 +754,7 @@ Indexing - Bug in :meth:`DataFrame.loc.__setitem__` when setting-with-expansion incorrectly raising when the index in the expanding axis contains duplicates (:issue:`40096`) - Bug in :meth:`DataFrame.loc` incorrectly matching non-boolean index elements (:issue:`20432`) - Bug in :meth:`Series.__delitem__` with ``ExtensionDtype`` incorrectly casting to ``ndarray`` (:issue:`40386`) +- Bug in :meth:`MultiIndex.union` raising for an empty ``RangeIndex`` (:issue:`41234`) Missing ^^^^^^^ From 923456bdaf3738a1d4d1a2b5140b4807b93acea9 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 10:36:03 +0200 Subject: [PATCH 04/10] black. --- pandas/core/indexes/base.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index e852aad433059..ee2ae291cd288 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2923,9 +2923,11 @@ def union(self, other, sort=None): other, result_name = self._convert_can_do_setop(other) if not is_dtype_equal(self.dtype, other.dtype): - if isinstance(self, ABCMultiIndex) and not is_object_dtype( - unpack_nested_dtype(other) - ) and len(other) > 0: + if ( + isinstance(self, ABCMultiIndex) + and not is_object_dtype(unpack_nested_dtype(other)) + and len(other) > 0 + ): raise NotImplementedError( "Can only union MultiIndex with MultiIndex or Index of tuples, " "try mi.to_flat_index().union(other) instead." From 2b91fcc879d98e1849d96082e745f1ce9563eb7a Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 12:34:56 +0200 Subject: [PATCH 05/10] Don't check names. --- pandas/core/indexes/multi.py | 2 +- pandas/tests/indexes/multi/test_setops.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 794f13bbfb6b1..36c8e818f1a24 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3597,7 +3597,7 @@ def _get_reconciled_name_object(self, other) -> MultiIndex: def _maybe_match_names(self, other): """ Try to find common names to attach to the result of an operation between - a and b. Return a consensus list of names if they match at least partly + a and b. Return a consensus list of names if they match at least partly or list of None if they have completely different names. """ if len(self.names) != len(other.names): diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index 49ebb627191e2..34a34113e3015 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -418,8 +418,8 @@ def test_union_multiindex_empty_rangeindex(): # GH#41234 mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) ri = pd.RangeIndex(0) - result = mi.union(mi) - tm.assert_index_equal(mi, result) + result = mi.union(ri) + tm.assert_index_equal(mi, result, check_names=False) @pytest.mark.parametrize( From d32bd2593a7da4397bbe7382456c5c3208731983 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 13:00:35 +0200 Subject: [PATCH 06/10] Also test reverse. --- pandas/tests/indexes/multi/test_setops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index 34a34113e3015..462ff228badfd 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -418,8 +418,8 @@ def test_union_multiindex_empty_rangeindex(): # GH#41234 mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) ri = pd.RangeIndex(0) - result = mi.union(ri) - tm.assert_index_equal(mi, result, check_names=False) + tm.assert_index_equal(mi, mi.union(ri), check_names=False) + tm.assert_index_equal(mi, ri.union(mi), check_names=False) @pytest.mark.parametrize( From fa6f12a123efadca85e2d8608c9bffcf8e61681a Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 13:27:11 +0200 Subject: [PATCH 07/10] Remove changes. --- doc/source/whatsnew/v1.3.0.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index dec7c2af35fa8..6bbaa934eaa12 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -754,7 +754,6 @@ Indexing - Bug in :meth:`DataFrame.loc.__setitem__` when setting-with-expansion incorrectly raising when the index in the expanding axis contains duplicates (:issue:`40096`) - Bug in :meth:`DataFrame.loc` incorrectly matching non-boolean index elements (:issue:`20432`) - Bug in :meth:`Series.__delitem__` with ``ExtensionDtype`` incorrectly casting to ``ndarray`` (:issue:`40386`) -- Bug in :meth:`MultiIndex.union` raising for an empty ``RangeIndex`` (:issue:`41234`) Missing ^^^^^^^ From 73eb105e9236192cacae76fef1eee92030871b3c Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 14:02:03 +0200 Subject: [PATCH 08/10] Add test for concat. --- pandas/tests/reshape/concat/test_concat.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pandas/tests/reshape/concat/test_concat.py b/pandas/tests/reshape/concat/test_concat.py index 2ed38670e88a6..300fee2bcffc9 100644 --- a/pandas/tests/reshape/concat/test_concat.py +++ b/pandas/tests/reshape/concat/test_concat.py @@ -627,3 +627,14 @@ def test_concat_null_object_with_dti(): index=exp_index, ) tm.assert_frame_equal(result, expected) + + +def test_concat_multiindex_with_empty_rangeindex(self): + # GH#41234 + mi = MultiIndex.from_tuples([("B", 1), ("C", 1)]) + df1 = DataFrame([[1, 2]], columns=mi) + df2 = DataFrame(index=[1], columns=pd.RangeIndex(0)) + + result = concat([df1, df2]) + expected = DataFrame([[1, 2], [np.nan, np.nan]], columns=mi) + tm.assert_frame_equal(result, expected) From 04bf4861e34f5ddad19d8e29c7658fa0af248bd7 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Mon, 3 May 2021 14:52:27 +0200 Subject: [PATCH 09/10] Typo. --- pandas/tests/reshape/concat/test_concat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/reshape/concat/test_concat.py b/pandas/tests/reshape/concat/test_concat.py index 300fee2bcffc9..96b88dc61cfed 100644 --- a/pandas/tests/reshape/concat/test_concat.py +++ b/pandas/tests/reshape/concat/test_concat.py @@ -629,7 +629,7 @@ def test_concat_null_object_with_dti(): tm.assert_frame_equal(result, expected) -def test_concat_multiindex_with_empty_rangeindex(self): +def test_concat_multiindex_with_empty_rangeindex(): # GH#41234 mi = MultiIndex.from_tuples([("B", 1), ("C", 1)]) df1 = DataFrame([[1, 2]], columns=mi) From 0b29fc5121bcc3b184671f3a2432b94acec7ec8e Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Tue, 4 May 2021 08:36:37 +0200 Subject: [PATCH 10/10] Suggestion by @jbrockmendel. --- pandas/tests/indexes/multi/test_setops.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/multi/test_setops.py b/pandas/tests/indexes/multi/test_setops.py index 462ff228badfd..0b59e832ce3a8 100644 --- a/pandas/tests/indexes/multi/test_setops.py +++ b/pandas/tests/indexes/multi/test_setops.py @@ -418,8 +418,12 @@ def test_union_multiindex_empty_rangeindex(): # GH#41234 mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["a", "b"]) ri = pd.RangeIndex(0) - tm.assert_index_equal(mi, mi.union(ri), check_names=False) - tm.assert_index_equal(mi, ri.union(mi), check_names=False) + + result_left = mi.union(ri) + tm.assert_index_equal(mi, result_left, check_names=False) + + result_right = ri.union(mi) + tm.assert_index_equal(mi, result_right, check_names=False) @pytest.mark.parametrize(