From b08bc6dca2d0048718d5b5f4fc09101ba438f140 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 27 Jan 2020 14:08:31 -0800 Subject: [PATCH 1/8] Use IntervalArray for IntervalIndex._concat_same_dtype --- pandas/core/indexes/extension.py | 8 ++++++++ pandas/core/indexes/interval.py | 16 ++-------------- pandas/tests/indexes/interval/test_interval.py | 5 +---- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index 9ddc5c01030b1..72cbe132fce74 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -202,6 +202,14 @@ def repeat(self, repeats, axis=None): result = self._data.repeat(repeats, axis=axis) return self._shallow_copy(result) + def _concat_same_dtype(self, to_concat, name): + """ + assert that we all have the same .closed + we allow a 0-len index here as well + """ + arr = type(self._data)._concat_same_type(to_concat) + return type(self)._simple_new(arr, name=name) + @Appender(Index.take.__doc__) def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): nv.validate_take(tuple(), kwargs) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index a756900ff9ae5..43e1c0c7d3219 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -401,11 +401,11 @@ def __contains__(self, key: Any) -> bool: return False @cache_readonly - def _multiindex(self): + def _multiindex(self) -> MultiIndex: return MultiIndex.from_arrays([self.left, self.right], names=["left", "right"]) @cache_readonly - def values(self): + def values(self) -> IntervalArray: """ Return the IntervalIndex's data as an IntervalArray. """ @@ -948,18 +948,6 @@ def insert(self, loc, item): new_right = self.right.insert(loc, right_insert) return self._shallow_copy(new_left, new_right) - def _concat_same_dtype(self, to_concat, name): - """ - assert that we all have the same .closed - we allow a 0-len index here as well - """ - if not len({i.closed for i in to_concat if len(i)}) == 1: - raise ValueError( - "can only append two IntervalIndex objects " - "that are closed on the same side" - ) - return super()._concat_same_dtype(to_concat, name) - @Appender(_index_shared_docs["take"] % _index_doc_kwargs) def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): result = self._data.take( diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index 47a0ba7fe0f21..d010060880703 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -673,10 +673,7 @@ def test_append(self, closed): ) tm.assert_index_equal(result, expected) - msg = ( - "can only append two IntervalIndex objects that are closed " - "on the same side" - ) + msg = "Intervals must all be closed on the same side" for other_closed in {"left", "right", "both", "neither"} - {closed}: index_other_closed = IntervalIndex.from_arrays( [0, 1], [1, 2], closed=other_closed From 49ba5868ae38f1d3eca7b63932f93b3c8c730caf Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 27 Jan 2020 17:49:40 -0800 Subject: [PATCH 2/8] get IntervalIndex.__getitem__ from ExtensionIndex --- pandas/core/indexes/interval.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 43e1c0c7d3219..170619c429ee7 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -955,14 +955,6 @@ def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): ) return self._shallow_copy(result) - def __getitem__(self, value): - result = self._data[value] - if isinstance(result, IntervalArray): - return self._shallow_copy(result) - else: - # scalar - return result - # -------------------------------------------------------------------- # Rendering Methods # __repr__ associated methods are based on MultiIndex From 67dc67a4457aac99e6a94fb90b1d56ef5ccc4d75 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 29 Jan 2020 11:43:55 -0800 Subject: [PATCH 3/8] implement ExtensionIndex._concat_same_dtype --- pandas/core/indexes/datetimelike.py | 3 +-- pandas/core/indexes/datetimes.py | 3 +-- pandas/core/indexes/extension.py | 16 ++++++++++++++++ pandas/core/indexes/interval.py | 17 ++--------------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index b87dd0f02252f..91869fa6b5a00 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -82,8 +82,7 @@ def wrapper(left, right): cache=True, ) @inherit_names( - ["__iter__", "mean", "freq", "freqstr", "_ndarray_values", "asi8", "_box_values"], - DatetimeLikeArrayMixin, + ["mean", "freq", "freqstr", "asi8", "_box_values"], DatetimeLikeArrayMixin, ) class DatetimeIndexOpsMixin(ExtensionIndex): """ diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3afd1ff35806d..6c5ae3fcca43c 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -70,9 +70,8 @@ class DatetimeDelegateMixin(DatetimelikeDelegateMixin): "_local_timestamps", "_has_same_tz", "_format_native_types", - "__iter__", ] - _extra_raw_properties = ["_box_func", "tz", "tzinfo", "dtype"] + _extra_raw_properties = ["_box_func", "tz", "tzinfo"] _delegated_properties = DatetimeArray._datetimelike_ops + _extra_raw_properties _delegated_methods = ( DatetimeArray._datetimelike_methods + _extra_methods + _extra_raw_methods diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index 46ac26ac22201..dcf1740b78178 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -205,6 +205,9 @@ class ExtensionIndex(Index): __le__ = _make_wrapped_comparison_op("__le__") __ge__ = _make_wrapped_comparison_op("__ge__") + # --------------------------------------------------------------------- + # NDarray-Like Methods + def __getitem__(self, key): result = self._data[key] if isinstance(result, type(self._data)): @@ -217,6 +220,19 @@ def __getitem__(self, key): def __iter__(self): return self._data.__iter__() + def __len__(self) -> int: + return len(self._data) + + @property + def size(self) -> int: + return self._data.size + + @property + def dtype(self): + return self._data.dtype + + # --------------------------------------------------------------------- + @property def _ndarray_values(self) -> np.ndarray: return self._data._ndarray_values diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index f0c8d33c4b864..10b3141f01d56 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -187,23 +187,10 @@ def func(intvidx_self, other, sort=False): ) @inherit_names(["set_closed", "to_tuples"], IntervalArray, wrap=True) @inherit_names( - [ - "__len__", - "__array__", - "overlaps", - "contains", - "size", - "dtype", - "left", - "right", - "length", - ], - IntervalArray, + ["__array__", "overlaps", "contains", "left", "right", "length",], IntervalArray, ) @inherit_names( - ["is_non_overlapping_monotonic", "mid", "_ndarray_values", "closed"], - IntervalArray, - cache=True, + ["is_non_overlapping_monotonic", "mid", "closed"], IntervalArray, cache=True, ) class IntervalIndex(IntervalMixin, ExtensionIndex): _typ = "intervalindex" From 785b95706d4a47afc3b309a2594cb29103340d84 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 3 Feb 2020 15:00:09 -0800 Subject: [PATCH 4/8] remove defunct docstring --- pandas/core/indexes/extension.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index affb8a49f9424..b0e959aeb15f1 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -256,10 +256,6 @@ def repeat(self, repeats, axis=None): return self._shallow_copy(result) def _concat_same_dtype(self, to_concat, name): - """ - assert that we all have the same .closed - we allow a 0-len index here as well - """ arr = type(self._data)._concat_same_type(to_concat) return type(self)._simple_new(arr, name=name) From ab3200f606277bb9d4c16cf33352f5d3a3fc9fe5 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 3 Feb 2020 15:02:49 -0800 Subject: [PATCH 5/8] flake8 fixup --- pandas/core/indexes/interval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 628f82596ac94..34ac8f05fbabb 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -183,7 +183,7 @@ def func(intvidx_self, other, sort=False): ) @inherit_names(["set_closed", "to_tuples"], IntervalArray, wrap=True) @inherit_names( - ["__array__", "overlaps", "contains", "left", "right", "length",], IntervalArray, + ["__array__", "overlaps", "contains", "left", "right", "length"], IntervalArray, ) @inherit_names( ["is_non_overlapping_monotonic", "mid", "closed"], IntervalArray, cache=True, From 18d78296ae2f4a176fd5e0ec3d5c318230e9e02b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 3 Feb 2020 15:23:11 -0800 Subject: [PATCH 6/8] typing fixup --- pandas/core/indexes/extension.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index b0e959aeb15f1..1d15a6083c694 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -229,7 +229,8 @@ def __len__(self) -> int: @property def size(self) -> int: - return self._data.size + # If EA ever requires size, this can become self._data.size + return len(self) @property def dtype(self): From 50d1cec37e979f4929e553d3daa6bf5ecadc69ab Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 4 Feb 2020 09:00:39 -0800 Subject: [PATCH 7/8] remove extra methods --- pandas/core/indexes/extension.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index 1d15a6083c694..479a5cdb64cde 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -224,18 +224,11 @@ def __getitem__(self, key): def __iter__(self): return self._data.__iter__() - def __len__(self) -> int: - return len(self._data) - @property def size(self) -> int: # If EA ever requires size, this can become self._data.size return len(self) - @property - def dtype(self): - return self._data.dtype - # --------------------------------------------------------------------- @property From 4b42448bf9506e279c967eaea1abc31dea020b9a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 4 Feb 2020 11:25:07 -0800 Subject: [PATCH 8/8] comments --- pandas/core/indexes/extension.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index 479a5cdb64cde..59930f0c34c0c 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -200,6 +200,9 @@ class ExtensionIndex(Index): Index subclass for indexes backed by ExtensionArray. """ + # The base class already passes through to _data: + # size, __len__, dtype + _data: ExtensionArray __eq__ = _make_wrapped_comparison_op("__eq__") @@ -224,11 +227,6 @@ def __getitem__(self, key): def __iter__(self): return self._data.__iter__() - @property - def size(self) -> int: - # If EA ever requires size, this can become self._data.size - return len(self) - # --------------------------------------------------------------------- @property