Skip to content

Commit dd6843d

Browse files
rob-silAloqeelymroeschke
authored
BUG: Fix is_unique regression for slices of Indexes (#57958)
* Fix is_unique for slices of Indexes * Update doc/source/whatsnew/v2.2.2.rst Co-authored-by: Abdulaziz Aloqeely <[email protected]> * Handle monotonic on slices * Restore and fix monotonic code * Update docstring and comment for test * Update whatsnew for v3.0.0 * Update pandas/_libs/index.pyx Co-authored-by: Abdulaziz Aloqeely <[email protected]> * Update pandas/_libs/index.pyx Co-authored-by: Matthew Roeschke <[email protected]> * Add test for a null slice --------- Co-authored-by: Abdulaziz Aloqeely <[email protected]> Co-authored-by: Matthew Roeschke <[email protected]>
1 parent 7a5bfc4 commit dd6843d

File tree

3 files changed

+58
-12
lines changed

3 files changed

+58
-12
lines changed

doc/source/whatsnew/v3.0.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ Strings
547547

548548
Interval
549549
^^^^^^^^
550+
- :meth:`Index.is_monotonic_decreasing`, :meth:`Index.is_monotonic_increasing`, and :meth:`Index.is_unique` could incorrectly be ``False`` for an ``Index`` created from a slice of another ``Index``. (:issue:`57911`)
550551
- Bug in :func:`interval_range` where start and end numeric types were always cast to 64 bit (:issue:`57268`)
551552
-
552553

pandas/_libs/index.pyx

+32-12
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,24 @@ cdef class IndexEngine:
252252
return self.sizeof()
253253

254254
cpdef _update_from_sliced(self, IndexEngine other, reverse: bool):
255-
self.unique = other.unique
256-
self.need_unique_check = other.need_unique_check
255+
if other.unique:
256+
self.unique = other.unique
257+
self.need_unique_check = other.need_unique_check
258+
257259
if not other.need_monotonic_check and (
258260
other.is_monotonic_increasing or other.is_monotonic_decreasing):
259-
self.need_monotonic_check = other.need_monotonic_check
260-
# reverse=True means the index has been reversed
261-
self.monotonic_inc = other.monotonic_dec if reverse else other.monotonic_inc
262-
self.monotonic_dec = other.monotonic_inc if reverse else other.monotonic_dec
261+
self.need_monotonic_check = 0
262+
if len(self.values) > 0 and self.values[0] != self.values[-1]:
263+
# reverse=True means the index has been reversed
264+
if reverse:
265+
self.monotonic_inc = other.monotonic_dec
266+
self.monotonic_dec = other.monotonic_inc
267+
else:
268+
self.monotonic_inc = other.monotonic_inc
269+
self.monotonic_dec = other.monotonic_dec
270+
else:
271+
self.monotonic_inc = 1
272+
self.monotonic_dec = 1
263273

264274
@property
265275
def is_unique(self) -> bool:
@@ -882,14 +892,24 @@ cdef class SharedEngine:
882892
pass
883893

884894
cpdef _update_from_sliced(self, ExtensionEngine other, reverse: bool):
885-
self.unique = other.unique
886-
self.need_unique_check = other.need_unique_check
895+
if other.unique:
896+
self.unique = other.unique
897+
self.need_unique_check = other.need_unique_check
898+
887899
if not other.need_monotonic_check and (
888900
other.is_monotonic_increasing or other.is_monotonic_decreasing):
889-
self.need_monotonic_check = other.need_monotonic_check
890-
# reverse=True means the index has been reversed
891-
self.monotonic_inc = other.monotonic_dec if reverse else other.monotonic_inc
892-
self.monotonic_dec = other.monotonic_inc if reverse else other.monotonic_dec
901+
self.need_monotonic_check = 0
902+
if len(self.values) > 0 and self.values[0] != self.values[-1]:
903+
# reverse=True means the index has been reversed
904+
if reverse:
905+
self.monotonic_inc = other.monotonic_dec
906+
self.monotonic_dec = other.monotonic_inc
907+
else:
908+
self.monotonic_inc = other.monotonic_inc
909+
self.monotonic_dec = other.monotonic_dec
910+
else:
911+
self.monotonic_inc = 1
912+
self.monotonic_dec = 1
893913

894914
@property
895915
def is_unique(self) -> bool:

pandas/tests/indexes/test_base.py

+25
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,31 @@ def test_slice_keep_name(self):
961961
index = Index(["a", "b"], name="asdf")
962962
assert index.name == index[1:].name
963963

964+
def test_slice_is_unique(self):
965+
# GH 57911
966+
index = Index([1, 1, 2, 3, 4])
967+
assert not index.is_unique
968+
filtered_index = index[2:].copy()
969+
assert filtered_index.is_unique
970+
971+
def test_slice_is_montonic(self):
972+
"""Test that is_monotonic_decreasing is correct on slices."""
973+
# GH 57911
974+
index = Index([1, 2, 3, 3])
975+
assert not index.is_monotonic_decreasing
976+
977+
filtered_index = index[2:].copy()
978+
assert filtered_index.is_monotonic_decreasing
979+
assert filtered_index.is_monotonic_increasing
980+
981+
filtered_index = index[1:].copy()
982+
assert not filtered_index.is_monotonic_decreasing
983+
assert filtered_index.is_monotonic_increasing
984+
985+
filtered_index = index[:].copy()
986+
assert not filtered_index.is_monotonic_decreasing
987+
assert filtered_index.is_monotonic_increasing
988+
964989
@pytest.mark.parametrize(
965990
"index",
966991
[

0 commit comments

Comments
 (0)