Skip to content

Commit 580151a

Browse files
committed
BUG/CLN: move .equals to DatetimeOpsMixin
1 parent 59524af commit 580151a

File tree

18 files changed

+177
-84
lines changed

18 files changed

+177
-84
lines changed

doc/source/whatsnew/v0.19.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,7 @@ Bug Fixes
14541454
- Bug in operations on ``NaT`` returning ``float`` instead of ``datetime64[ns]`` (:issue:`12941`)
14551455
- Bug in ``Series`` flexible arithmetic methods (like ``.add()``) raises ``ValueError`` when ``axis=None`` (:issue:`13894`)
14561456
- Bug in ``DataFrame.to_csv()`` with ``MultiIndex`` columns in which a stray empty line was added (:issue:`6618`)
1457+
- Bug in ``DatetimeIndex``, ``TimedeltaIndex`` and ``PeriodIndex.equals()`` may return ``True`` when input isn't ``Index`` but contains the same values (:issue:`13107`)
14571458

14581459

14591460
- Bug in ``Index`` raises ``KeyError`` displaying incorrect column when column is not in the df and columns contains duplicate values (:issue:`13822`)

pandas/indexes/base.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,8 +1618,15 @@ def equals(self, other):
16181618
if not isinstance(other, Index):
16191619
return False
16201620

1621-
return array_equivalent(_values_from_object(self),
1622-
_values_from_object(other))
1621+
if is_object_dtype(self) and not is_object_dtype(other):
1622+
# if other is not object, use other's logic for coercion
1623+
return other.equals(self)
1624+
1625+
try:
1626+
return array_equivalent(_values_from_object(self),
1627+
_values_from_object(other))
1628+
except:
1629+
return False
16231630

16241631
def identical(self, other):
16251632
"""Similar to equals, but check that other comparable attributes are

pandas/indexes/category.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ def equals(self, other):
196196
if self.is_(other):
197197
return True
198198

199+
if not isinstance(other, Index):
200+
return False
201+
199202
try:
200203
other = self._is_dtype_compat(other)
201204
return array_equivalent(self._data, other)

pandas/indexes/multi.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,7 @@ def reindex(self, target, method=None, level=None, limit=None,
14361436
return_indexers=True,
14371437
keep_order=False)
14381438
else:
1439+
target = _ensure_index(target)
14391440
if self.equals(target):
14401441
indexer = None
14411442
else:
@@ -1984,6 +1985,9 @@ def equals(self, other):
19841985
if self.is_(other):
19851986
return True
19861987

1988+
if not isinstance(other, Index):
1989+
return False
1990+
19871991
if not isinstance(other, MultiIndex):
19881992
return array_equivalent(self._values,
19891993
_values_from_object(_ensure_index(other)))

pandas/indexes/numeric.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pandas.types.common import (is_dtype_equal, pandas_dtype,
88
is_float_dtype, is_object_dtype,
99
is_integer_dtype, is_scalar)
10-
from pandas.types.missing import array_equivalent, isnull
10+
from pandas.types.missing import isnull
1111
from pandas.core.common import _values_from_object
1212

1313
from pandas import compat
@@ -160,16 +160,6 @@ def _convert_scalar_indexer(self, key, kind=None):
160160
return (super(Int64Index, self)
161161
._convert_scalar_indexer(key, kind=kind))
162162

163-
def equals(self, other):
164-
"""
165-
Determines if two Index objects contain the same elements.
166-
"""
167-
if self.is_(other):
168-
return True
169-
170-
return array_equivalent(_values_from_object(self),
171-
_values_from_object(other))
172-
173163
def _wrap_joined_index(self, joined, other):
174164
name = self.name if self.name == other.name else None
175165
return Int64Index(joined, name=name)
@@ -306,6 +296,9 @@ def equals(self, other):
306296
if self is other:
307297
return True
308298

299+
if not isinstance(other, Index):
300+
return False
301+
309302
# need to compare nans locations and make sure that they are the same
310303
# since nans don't compare equal this is a bit tricky
311304
try:

pandas/tests/indexes/common.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,20 @@ def test_delete_base(self):
650650
# either depending on numpy version
651651
result = idx.delete(len(idx))
652652

653+
def test_equals(self):
654+
655+
for name, idx in compat.iteritems(self.indices):
656+
self.assertTrue(idx.equals(idx))
657+
self.assertTrue(idx.equals(idx.copy()))
658+
self.assertTrue(idx.equals(idx.astype(object)))
659+
660+
self.assertFalse(idx.equals(list(idx)))
661+
self.assertFalse(idx.equals(np.array(idx)))
662+
663+
if idx.nlevels == 1:
664+
# do not test MultiIndex
665+
self.assertFalse(idx.equals(pd.Series(idx)))
666+
653667
def test_equals_op(self):
654668
# GH9947, GH10637
655669
index_a = self.create_index()

pandas/tests/indexes/test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ def test_astype(self):
400400
casted = self.intIndex.astype('i8')
401401
self.assertEqual(casted.name, 'foobar')
402402

403-
def test_equals(self):
403+
def test_equals_object(self):
404404
# same
405405
self.assertTrue(Index(['a', 'b', 'c']).equals(Index(['a', 'b', 'c'])))
406406

pandas/tests/indexes/test_category.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ def test_ensure_copied_data(self):
522522
result = CategoricalIndex(index.values, copy=False)
523523
self.assertIs(_base(index.values), _base(result.values))
524524

525-
def test_equals(self):
525+
def test_equals_categorical(self):
526526

527527
ci1 = CategoricalIndex(['a', 'b'], categories=['a', 'b'], ordered=True)
528528
ci2 = CategoricalIndex(['a', 'b'], categories=['a', 'b', 'c'],
@@ -556,19 +556,30 @@ def test_equals(self):
556556

557557
# tests
558558
# make sure that we are testing for category inclusion properly
559-
self.assertTrue(CategoricalIndex(
560-
list('aabca'), categories=['c', 'a', 'b']).equals(list('aabca')))
559+
ci = CategoricalIndex(list('aabca'), categories=['c', 'a', 'b'])
560+
self.assertFalse(ci.equals(list('aabca')))
561+
self.assertFalse(ci.equals(CategoricalIndex(list('aabca'))))
562+
self.assertTrue(ci.equals(ci.copy()))
563+
564+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
565+
ci = CategoricalIndex(list('aabca'),
566+
categories=['c', 'a', 'b', np.nan])
567+
self.assertFalse(ci.equals(list('aabca')))
568+
self.assertFalse(ci.equals(CategoricalIndex(list('aabca'))))
561569
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
562-
self.assertTrue(CategoricalIndex(
563-
list('aabca'), categories=['c', 'a', 'b', np.nan]).equals(list(
564-
'aabca')))
565-
566-
self.assertFalse(CategoricalIndex(
567-
list('aabca') + [np.nan], categories=['c', 'a', 'b']).equals(list(
568-
'aabca')))
569-
self.assertTrue(CategoricalIndex(
570-
list('aabca') + [np.nan], categories=['c', 'a', 'b']).equals(list(
571-
'aabca') + [np.nan]))
570+
self.assertTrue(ci.equals(ci.copy()))
571+
572+
ci = CategoricalIndex(list('aabca') + [np.nan],
573+
categories=['c', 'a', 'b'])
574+
self.assertFalse(ci.equals(list('aabca')))
575+
self.assertFalse(ci.equals(CategoricalIndex(list('aabca'))))
576+
self.assertTrue(ci.equals(ci.copy()))
577+
578+
ci = CategoricalIndex(list('aabca') + [np.nan],
579+
categories=['c', 'a', 'b'])
580+
self.assertFalse(ci.equals(list('aabca') + [np.nan]))
581+
self.assertFalse(ci.equals(CategoricalIndex(list('aabca') + [np.nan])))
582+
self.assertTrue(ci.equals(ci.copy()))
572583

573584
def test_string_categorical_index_repr(self):
574585
# short

pandas/tests/indexes/test_multi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ def test_to_hierarchical(self):
12321232
def test_bounds(self):
12331233
self.index._bounds
12341234

1235-
def test_equals(self):
1235+
def test_equals_multi(self):
12361236
self.assertTrue(self.index.equals(self.index))
12371237
self.assertTrue(self.index.equal_levels(self.index))
12381238

pandas/tests/indexes/test_numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def test_astype(self):
265265
i = Float64Index([0, 1.1, np.NAN])
266266
self.assertRaises(ValueError, lambda: i.astype(dtype))
267267

268-
def test_equals(self):
268+
def test_equals_numeric(self):
269269

270270
i = Float64Index([1.0, 2.0])
271271
self.assertTrue(i.equals(i))

0 commit comments

Comments
 (0)