Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

return empty MultiIndex for symmetrical difference on equal MultiIndexes #16486

Merged
merged 12 commits into from May 31, 2017
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.20.2.txt
Expand Up @@ -38,6 +38,7 @@ Bug Fixes
~~~~~~~~~

- Bug in using ``pathlib.Path`` or ``py.path.local`` objects with io functions (:issue:`16291`)
- Bug in ``Index`` calling symmetric_difference() on two equal multiindices results in a TypeError (:issue `13490`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Index.symmetric_difference() on two equal MultiIndex's, results in a TypeError

- Bug in ``DataFrame.update()`` with ``overwrite=False`` and ``NaN values`` (:issue:`15593`)


Expand Down
5 changes: 5 additions & 0 deletions pandas/core/indexes/multi.py
Expand Up @@ -419,6 +419,11 @@ def _shallow_copy_with_infer(self, values=None, **kwargs):
@Appender(_index_shared_docs['_shallow_copy'])
def _shallow_copy(self, values=None, **kwargs):
if values is not None:
# On equal MultiIndexes the difference is empty.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block can simply go in _shallow_copy_with_infer, idea being we don't pass None to _shallow_copy unless we actually mean that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it

# Therefore, an empty MultiIndex is returned GH13490
if len(values) == 0:
return MultiIndex(levels=[[] for _ in range(self.nlevels)],
labels=[[] for _ in range(self.nlevels)], **kwargs)
if 'name' in kwargs:
kwargs['names'] = kwargs.pop('name', None)
# discards freq
Expand Down
10 changes: 8 additions & 2 deletions pandas/tests/indexes/test_base.py
Expand Up @@ -188,7 +188,6 @@ def test_constructor_ndarray_like(self):
# it should be possible to convert any object that satisfies the numpy
# ndarray interface directly into an Index
class ArrayLike(object):

def __init__(self, array):
self.array = array

Expand Down Expand Up @@ -246,7 +245,6 @@ def test_index_ctor_infer_nan_nat(self):
[np.timedelta64('nat'), np.nan],
[pd.NaT, np.timedelta64('nat')],
[np.timedelta64('nat'), pd.NaT]]:

tm.assert_index_equal(Index(data), exp)
tm.assert_index_equal(Index(np.array(data, dtype=object)), exp)

Expand Down Expand Up @@ -936,6 +934,14 @@ def test_symmetric_difference(self):
assert tm.equalContents(result, expected)
assert result.name == 'new_name'

def test_symmetric_difference_on_equal_multiindex(self):
# GH13490
idx1 = MultiIndex.from_tuples(self.tuples)
idx2 = MultiIndex.from_tuples(self.tuples)
result = idx1.symmetric_difference(idx2)
expected = MultiIndex(levels=[[], []], labels=[[], []])
assert tm.assert_index_equal(result, expected)

def test_is_numeric(self):
assert not self.dateIndex.is_numeric()
assert not self.strIndex.is_numeric()
Expand Down
11 changes: 11 additions & 0 deletions pandas/tests/indexing/test_multiindex.py
Expand Up @@ -697,6 +697,17 @@ def test_multiindex_slice_first_level(self):
index=range(30, 71))
tm.assert_frame_equal(result, expected)

def test_multiindex_symmetric_difference(self):
# GH 13490
idx = MultiIndex.from_product([['a', 'b'], ['A', 'B']],
names=['a', 'b'])
result = idx ^ idx
assert result.names == idx.names

idx2 = idx.copy().rename(['A', 'B'])
result = idx ^ idx2
assert result.names == [None, None]


class TestMultiIndexSlicers(object):

Expand Down