diff --git a/.gitignore b/.gitignore index ff0a6aef47163..4b540f38e8adf 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ .ipynb_checkpoints .tags .cache/ +.vscode # Compiled source # ################### diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt index 3dd8bb2ac2de5..3788d6b17b6b1 100644 --- a/doc/source/whatsnew/v0.21.0.txt +++ b/doc/source/whatsnew/v0.21.0.txt @@ -90,6 +90,7 @@ Bug Fixes Conversion ^^^^^^^^^^ +- Bug in ``IntervalIndex`` where ``is_non_overlapping_monotonic`` returned incorrect value for intervals closed on both sides (:issue:`16560`) Indexing diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index b1523cd6c0d0c..00d904a0d5ebf 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -556,6 +556,11 @@ def is_non_overlapping_monotonic(self): # must be increasing (e.g., [0, 1), [1, 2), [2, 3), ... ) # or decreasing (e.g., [-1, 0), [-2, -1), [-3, -2), ...) # we already require left <= right + if self.closed == 'both': + return ((self.right[:-1] < self.left[1:]).all() or + (self.left[:-1] > self.right[1:]).all()) + + # self.closed == 'left' or 'right' or 'neither' return ((self.right[:-1] <= self.left[1:]).all() or (self.left[:-1] >= self.right[1:]).all()) diff --git a/pandas/tests/indexes/test_interval.py b/pandas/tests/indexes/test_interval.py index 33745017fe3d6..a327702542a42 100644 --- a/pandas/tests/indexes/test_interval.py +++ b/pandas/tests/indexes/test_interval.py @@ -371,10 +371,36 @@ def slice_locs_cases(self, breaks): assert index.slice_locs(1, 1) == (1, 1) assert index.slice_locs(1, 2) == (1, 2) - index = IntervalIndex.from_breaks([0, 1, 2], closed='both') - assert index.slice_locs(1, 1) == (0, 2) + index = IntervalIndex.from_tuples( + [(0, 1), (2, 3), (4, 5)], closed='both') + assert index.slice_locs(1, 1) == (0, 1) assert index.slice_locs(1, 2) == (0, 2) + def test_is_non_overlapping_monotonic(self): + index = IntervalIndex.from_tuples( + [(0, 1), (2, 3), (4, 5)], closed='both') + assert index.is_non_overlapping_monotonic == True + index = IntervalIndex.from_breaks(range(4), closed='both') + assert index.is_non_overlapping_monotonic == False + + index = IntervalIndex.from_breaks([0, 1, 2], closed='neither') + assert index.is_non_overlapping_monotonic == True + index = IntervalIndex.from_tuples( + [(0, 2), (1, 3), (3, 4)], closed='neither') + assert index.is_non_overlapping_monotonic == False + + index = IntervalIndex.from_breaks(range(4), closed='left') + assert index.is_non_overlapping_monotonic == True + index = IntervalIndex.from_tuples( + [(0, 1), (1, 2), (1.5, 3)], closed='left') + assert index.is_non_overlapping_monotonic == False + + index = IntervalIndex.from_breaks(range(4), closed='right') + assert index.is_non_overlapping_monotonic == True + index = IntervalIndex.from_tuples( + [(0, 1), (1, 2), (1.5, 3)], closed='right') + assert index.is_non_overlapping_monotonic == False + def test_slice_locs_int64(self): self.slice_locs_cases([0, 1, 2])